取消共享用户命名空间、fork、映射 uid 然后 execvp 失败

unshare user namespace, fork, map uid then execvp failing

我正在尝试执行以下操作序列:

但是,当 运行ning id 时,我的代码将用户输出为 nobody 或失败而没有错误。

#include <sched.h>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <system_error>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

void unshare_user_namespace() {
  if (0 != unshare(CLONE_NEWUSER)) {
    fprintf(stderr, "%s\n", "USER unshare has failed");
    exit(1);
  }
}

void map_id() {
  int pid = getpid();
  char file[100];
  if (0 > sprintf(file, "/proc/%d/uid_map", pid)) {
    printf("Couldn't sprintf uid_map path.");
    exit(1);
  }

  int fd;
  fd = open(file, 1);
  if (fd < 0) {
    printf("Coudln't open file for writing.\n");
    exit(1);
  }

  int uid = getuid();
  char * buf;
  if (0 > sprintf(buf, "0 %d 1", uid)) {
    printf("Couldn't sprintf uid_map content.");
    exit(1);
  }

  if (write(fd, buf, strlen(buf))) {
    printf("Coudln't write mapping into file.\n");
    exit(1);
  }

  free(buf);
  close(fd);
}

void start(char * command, char ** args) {
  unshare_user_namespace();
  int fork_pid = fork();

  if (-1 == fork_pid) {
    fprintf(stderr, "%s\n", "couldn't fork");
    exit(1);
  }

  if (0 == fork_pid) {
    map_id();

    if (-1 == execvp(command, args)) {
      fprintf(stderr, "%s\n", "couldn't execvp");
      exit(1);
    }
  }
}

int main(int argc, char ** argv) {
  start(argv[1], & argv[1]);
  int status;
  wait( & status);
  return 0;
}

我尝试阅读 namespacesunshare 等的手册页,但无法弄清楚我的代码有什么问题。

要运行代码:

$ g++ <file_containing_code> && ./a.out id

很确定您已经找到了答案,但这是我能想到的最小示例:

// gcc -Wall -std=c11
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sched.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdarg.h>

void write_to_file(const char *which, const char *format, ...) {
  FILE * fu = fopen(which, "w");
  va_list args;
  va_start(args, format);
  if (vfprintf(fu, format, args) < 0) {
    perror("cannot write");
    exit(1);
  }
  fclose(fu);
}

int main(int argc, char ** argv) {
  // array of strings, terminated with NULL entry
  char **cmd_and_args = (char**) calloc(argc, sizeof(char*));
  for (int i = 1 ; i < argc; i++) {
    cmd_and_args[i-1] = argv[i];
  }
  uid_t uid = getuid();
  gid_t gid = getgid();
  // first unshare
  if (0 != unshare(CLONE_NEWUSER)) {
    fprintf(stderr, "%s\n", "USER unshare has failed");
    exit(1);
  }
  // remap uid
  write_to_file("/proc/self/uid_map", "0 %d 1", uid);
  // deny setgroups (see user_namespaces(7))
  write_to_file("/proc/self/setgroups", "deny");
  // remap gid
  write_to_file("/proc/self/gid_map", "0 %d 1", gid);
  // exec the command
  if (execvp(cmd_and_args[0], cmd_and_args) < 0) {
    perror("cannot execvp");
    exit(1);
  }
  // unreachable
  free(cmd_and_args);
  return 0;
}