setuid程序的管道访问权限

时间:2015-07-29 17:54:09

标签: c linux pipe setuid mkfifo

我正在扩展一些在GNU / Linux(Ubuntu 14.04)下运行的软件(我不是作者),它由一个manager进程和几个worker进程组成。管理员可以通过我可以在配置文件中指定的命令行来启动工作程序。

启动工人后,经理使用管道与其通信。出于安全原因,我们决定让工作人员在与管理员不同的用户下运行(让我们称他们为manager-userworker-user)。这是通过编写一个小包装脚本来实现的,该脚本用su切换用户并启动一个新工作程序。在此之后,管理员可以通过管道与工作进程进行通信。这种方法已经工作了好几个月了。

作为su的替代方案,我们考虑使用setuid位运行工作程序。所以我们编写了一个C包装器,可以由管理器调用来启动一个worker。如果我们将包装器配置为manager-user所有,则工作正常启动(但当然,具有错误的权限)。如果我们将包装器配置为由worker-user拥有并设置setuid位,则启动工作程序,然后退出,因为它们无法连接到管理器。

所以我的问题是:运行setuid可执行文件如何影响父进程和子进程创建的管道的权限?可以通过setuid-wrapper启动的工作进程没有打开管理器管道(或反过来)的权限吗?如果是这种情况,我们如何更改这些权限?

我没有使用setuid的经验,所以欢迎任何信息/解释。

2 个答案:

答案 0 :(得分:4)

Unix pipe可以命名或未命名。命名管道实现为文件,该文件具有标准用户,组和世界所有权权限位。

未命名的管道也具有权限,但在创建管道时,这些权限受到euidegidumask的约束。

因此,如果您的worker-user setuid为具有不同组权限的其他用户,除非egid与主进程相同,否则将无法使用用户或组perms访问父进程创建的管道。

当然,对于某些umask值,未命名的管道世界权限将允许进程通过管道进行通信,但任何进程都能够读取/写入该管道。未命名比命名管道更安全,但授予任何管道世界权限并不是一个好的安全措施。

此用例(希望两个通信进程在不同用户下运行)的可能解决方案将同时具有manager-userworker-user进程在同一个组中,并且在管道创建时清除了umask group-perm位,这样两个进程都可以读取和写入未命名的管道。

所以,如果

  1. manager-userteam所有,
  2. worker-user也归team所有,
  3. 两个进程都清除了其组umask位(没有值1x .. 7x
  4. 在创建未命名的管道之前
  5. 然后manager-user应该能够在未命名的管道上写入,worker-user应该能够读取它(反之亦然,取决于管道的使用方式),即使它们是作为单独的用户运行。

    有关权限位的详细信息,请参阅man上的chmod页面。

答案 1 :(得分:0)

使用pipe()函数使用匿名管道,如下所示(该示例来自german Wikipedia):

# check the link above for #includes and const definitons

int main(void) {
    int fd[2], n, i;
    pid_t pid;
    char line[MAX_CHARS];

    // Create the pipe
    if (pipe(fd) < 0)
        fprintf(stderr, "Failed to create pipe()");

    // Fork child
    if ((pid = fork()) > 0) {
        // Parent process

        close(fd[0]);
        fprintf(stdout, "Parent : ");
        fgets(line, MAX_CHARS, stdin);
        write(fd[1], line, strlen(line));

        if (waitpid(pid, NULL, 0) < 0)
            fprintf(stderr, "Error: waitpid()");
    }

    else {
        // Child process
        close(fd[1]);
        n = read(fd[0], line, MAX_CHARS);

        for (i = 0; i < n; i++)
            line[i] = toupper(line[i]);
        fprintf(stderr, "Child : ");

        write(STDOUT_FILENO, line, n);
    }
    exit(0);
}

上述程序创建一个单向管道,父进程保存读取结束,子进程保存写入结束。