static int do_child(void *vargv) { int ret; char **argv = (char **)vargv; /* Assume we want to become root */ if (!lxc_switch_uid_gid(0, 0)) return -1; if (!lxc_setgroups(0, NULL)) return -1; ret = unshare(CLONE_NEWNS); if (ret < 0) { CMD_SYSERROR("Failed to unshare mount namespace"); return -1; } if (detect_shared_rootfs()) { ret = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL); if (ret < 0) { CMD_SYSINFO("Failed to make \"/\" rslave"); return -1; } } execvp(argv[0], argv); CMD_SYSERROR("Failed to execute \"%s\"", argv[0]); return -1; }
static int open_and_lock(char *path) { int fd, ret; struct flock lk; fd = open(path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR); if (fd < 0) { CMD_SYSERROR("Failed to open \"%s\"\n", path); return -1; } lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; lk.l_start = 0; lk.l_len = 0; ret = fcntl(fd, F_SETLKW, &lk); if (ret < 0) { CMD_SYSERROR("Failed to lock \"%s\"\n", path); close(fd); return -1; } return fd; }
static char *get_username(void) { __do_free char *buf = NULL; struct passwd pwent; struct passwd *pwentp = NULL; size_t bufsize; int ret; bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (!buf) return NULL; ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) usernic_error("%s", "Could not find matched password record\n"); CMD_SYSERROR("Failed to get username: %u\n", getuid()); return NULL; } return strdup(pwent.pw_name); }
static void opentty(const char *tty, int which) { int fd, flags, ret; if (tty[0] == '\0') return; fd = open(tty, O_RDWR | O_NONBLOCK); if (fd < 0) { CMD_SYSERROR("Failed to open tty"); return; } flags = fcntl(fd, F_GETFL); flags &= ~O_NONBLOCK; ret = fcntl(fd, F_SETFL, flags); if (ret < 0) { CMD_SYSINFO("Failed to remove O_NONBLOCK from file descriptor %d", fd); close(fd); return; } close(which); if (fd != which) { (void)dup2(fd, which); close(fd); } }
static int find_default_map(void) { size_t bufsize; char *buf; struct passwd pwent; int ret = -1; struct passwd *pwentp = NULL; bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (!buf) return -1; ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) CMD_SYSERROR("Failed to find matched password record"); CMD_SYSERROR("Failed to get password record for uid %d", getuid()); ret = -1; goto out; } ret = read_default_map(subuidfile, ID_TYPE_UID, pwent.pw_name); if (ret < 0) goto out; ret = read_default_map(subgidfile, ID_TYPE_GID, pwent.pw_name); if (ret < 0) goto out; ret = 0; out: free(buf); return ret; }
static int instantiate_veth(char *veth1, char *veth2) { int ret; ret = lxc_veth_create(veth1, veth2); if (ret < 0) { errno = -ret; CMD_SYSERROR("Failed to create %s-%s\n", veth1, veth2); return -1; } /* Changing the high byte of the mac address to 0xfe, the bridge * interface will always keep the host's mac address and not take the * mac address of a container. */ ret = setup_private_host_hw_addr(veth1); if (ret < 0) { errno = -ret; CMD_SYSERROR("Failed to change mac address of host interface %s\n", veth1); } return netdev_set_flag(veth1, IFF_UP); }
static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int n) { struct alloted_s *cur, *al; if (!head || !name) { /* Sanity check. Parameters should not be null. */ usernic_error("%s\n", "Unexpected NULL argument"); return NULL; } al = malloc(sizeof(struct alloted_s)); if (!al) { CMD_SYSERROR("Failed to allocate memory\n"); return NULL; } al->name = strdup(name); if (!al->name) { free(al); return NULL; } al->allowed = n; al->next = NULL; if (!*head) { *head = al; return al; } cur = *head; while (cur->next) cur = cur->next; cur->next = al; return al; }
/* The configuration file consists of lines of the form: * * user type bridge count * or * @group type bridge count * * Return the count entry for the calling user if there is one. Else * return -1. */ static int get_alloted(char *me, char *intype, char *link, struct alloted_s **alloted) { __do_free char *line = NULL; __do_fclose FILE *fin = NULL; int n, ret; char name[100], type[100], br[100]; char **groups; int count = 0; size_t len = 0; fin = fopen(LXC_USERNIC_CONF, "r"); if (!fin) { CMD_SYSERROR("Failed to open \"%s\"\n", LXC_USERNIC_CONF); return -1; } groups = get_groupnames(); while ((getline(&line, &len, fin)) != -1) { ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name, type, br, &n); if (ret != 4) continue; if (strlen(name) == 0) continue; if (strcmp(name, me)) { if (name[0] != '@') continue; if (!name_is_in_groupnames(name + 1, groups)) continue; } if (strcmp(type, intype)) continue; if (strcmp(link, br)) continue; /* Found the user or group with the appropriate settings, * therefore finish the search. What to do if there are more * than one applicable lines? not specified in the docs. Since * getline is implemented with realloc, we don't need to free * line until exiting func. * * If append_alloted returns NULL, e.g. due to a malloc error, * we set count to 0 and break the loop, allowing cleanup and * then exiting from main(). */ if (!append_alloted(alloted, name, n)) { count = 0; break; } count += n; } free_groupnames(groups); /* Now return the total number of nics that this user can create. */ return count; }
int main(int argc, char *argv[]) { int c, pid, ret, status; char buf[1]; int pipe_fds1[2], /* child tells parent it has unshared */ pipe_fds2[2]; /* parent tells child it is mapped and may proceed */ unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS; char ttyname0[256] = {0}, ttyname1[256] = {0}, ttyname2[256] = {0}; char *default_args[] = {"/bin/sh", NULL}; lxc_log_fd = STDERR_FILENO; if (isatty(STDIN_FILENO)) { ret = readlink("/proc/self/fd/0", ttyname0, sizeof(ttyname0)); if (ret < 0) { CMD_SYSERROR("Failed to open stdin"); _exit(EXIT_FAILURE); } ret = readlink("/proc/self/fd/1", ttyname1, sizeof(ttyname1)); if (ret < 0) { CMD_SYSINFO("Failed to open stdout. Continuing"); ttyname1[0] = '\0'; } ret = readlink("/proc/self/fd/2", ttyname2, sizeof(ttyname2)); if (ret < 0) { CMD_SYSINFO("Failed to open stderr. Continuing"); ttyname2[0] = '\0'; } } lxc_list_init(&active_map); while ((c = getopt(argc, argv, "m:h")) != EOF) { switch (c) { case 'm': ret = parse_map(optarg); if (ret < 0) { usage(argv[0]); _exit(EXIT_FAILURE); } break; case 'h': usage(argv[0]); _exit(EXIT_SUCCESS); default: usage(argv[0]); _exit(EXIT_FAILURE); } }; if (lxc_list_empty(&active_map)) { ret = find_default_map(); if (ret < 0) { fprintf(stderr, "Failed to find subuid or subgid allocation\n"); _exit(EXIT_FAILURE); } } argv = &argv[optind]; argc = argc - optind; if (argc < 1) argv = default_args; ret = pipe2(pipe_fds1, O_CLOEXEC); if (ret < 0) { CMD_SYSERROR("Failed to open new pipe"); _exit(EXIT_FAILURE); } ret = pipe2(pipe_fds2, O_CLOEXEC); if (ret < 0) { CMD_SYSERROR("Failed to open new pipe"); close(pipe_fds1[0]); close(pipe_fds1[1]); _exit(EXIT_FAILURE); } pid = fork(); if (pid < 0) { close(pipe_fds1[0]); close(pipe_fds1[1]); close(pipe_fds2[0]); close(pipe_fds2[1]); _exit(EXIT_FAILURE); } if (pid == 0) { close(pipe_fds1[0]); close(pipe_fds2[1]); opentty(ttyname0, STDIN_FILENO); opentty(ttyname1, STDOUT_FILENO); opentty(ttyname2, STDERR_FILENO); ret = unshare(flags); if (ret < 0) { CMD_SYSERROR("Failed to unshare mount and user namespace"); close(pipe_fds1[1]); close(pipe_fds2[0]); _exit(EXIT_FAILURE); } buf[0] = '1'; ret = lxc_write_nointr(pipe_fds1[1], buf, 1); if (ret != 1) { CMD_SYSERROR("Failed to write to pipe file descriptor %d", pipe_fds1[1]); close(pipe_fds1[1]); close(pipe_fds2[0]); _exit(EXIT_FAILURE); } ret = lxc_read_nointr(pipe_fds2[0], buf, 1); if (ret != 1) { CMD_SYSERROR("Failed to read from pipe file descriptor %d", pipe_fds2[0]); close(pipe_fds1[1]); close(pipe_fds2[0]); _exit(EXIT_FAILURE); } close(pipe_fds1[1]); close(pipe_fds2[0]); if (buf[0] != '1') { fprintf(stderr, "Received unexpected value from parent process\n"); _exit(EXIT_FAILURE); } ret = do_child((void *)argv); if (ret < 0) _exit(EXIT_FAILURE); _exit(EXIT_SUCCESS); } close(pipe_fds1[1]); close(pipe_fds2[0]); ret = lxc_read_nointr(pipe_fds1[0], buf, 1); if (ret <= 0) CMD_SYSERROR("Failed to read from pipe file descriptor %d", pipe_fds1[0]); buf[0] = '1'; ret = lxc_map_ids(&active_map, pid); if (ret < 0) fprintf(stderr, "Failed to write id mapping for child process\n"); ret = lxc_write_nointr(pipe_fds2[1], buf, 1); if (ret < 0) { CMD_SYSERROR("Failed to write to pipe file descriptor %d", pipe_fds2[1]); _exit(EXIT_FAILURE); } ret = waitpid(pid, &status, __WALL); if (ret < 0) { CMD_SYSERROR("Failed to wait on child process"); _exit(EXIT_FAILURE); } _exit(WEXITSTATUS(status)); }