]> git.proxmox.com Git - mirror_lxc.git/blobdiff - src/lxc/cmd/lxc_usernsexec.c
Merge pull request #2740 from CameronNemo/usernsexec
[mirror_lxc.git] / src / lxc / cmd / lxc_usernsexec.c
index 69ba7a41e162c03116a3e3119a520cbbda6c4c0f..ab0dffcffcbdccbd3eb3104c1d4eee9cda7a3c7a 100644 (file)
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
-#include "config.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <libgen.h>
+#include <pwd.h>
 #include <sched.h>
-#include <sys/syscall.h>
 #include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
-#include <errno.h>
-#include <libgen.h>
-#include <fcntl.h>
+#include <sys/mount.h>
 #include <sys/stat.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
-#include <sys/mount.h>
 #include <sys/wait.h>
-#include <sched.h>
-#include <pwd.h>
-#include <grp.h>
+#include <unistd.h>
 
 #include "conf.h"
-#include "namespace.h"
+#include "config.h"
+#include "list.h"
+#include "log.h"
+#include "macro.h"
+#include "file_utils.h"
+#include "string_utils.h"
+#include "syscall_wrappers.h"
 #include "utils.h"
 
-#ifndef MS_REC
-#define MS_REC 16384
-#endif
-
-#ifndef MS_SLAVE
-#define MS_SLAVE (1 << 19)
-#endif
-
 extern int lxc_log_fd;
 
-int unshare(int flags);
-
 static void usage(const char *name)
 {
        printf("usage: %s [-h] [-m <uid-maps>] -- [command [arg ..]]\n", name);
@@ -77,21 +74,22 @@ static void usage(const char *name)
 
 static void opentty(const char *tty, int which)
 {
-       int fd, flags;
+       int fd, flags, ret;
 
        if (tty[0] == '\0')
                return;
 
        fd = open(tty, O_RDWR | O_NONBLOCK);
        if (fd < 0) {
-               printf("WARN: could not reopen tty: %s\n", strerror(errno));
+               CMD_SYSERROR("Failed to open tty");
                return;
        }
 
        flags = fcntl(fd, F_GETFL);
        flags &= ~O_NONBLOCK;
-       if (fcntl(fd, F_SETFL, flags) < 0) {
-               printf("WARN: could not set fd flags: %s\n", strerror(errno));
+       ret = fcntl(fd, F_SETFL, flags);
+       if (ret < 0) {
+               CMD_SYSINFO("Failed to remove O_NONBLOCK from file descriptor %d", fd);
                close(fd);
                return;
        }
@@ -106,51 +104,49 @@ static void opentty(const char *tty, int which)
 
 static int do_child(void *vargv)
 {
+       int ret;
        char **argv = (char **)vargv;
 
        /* Assume we want to become root */
-       if (setgid(0) < 0) {
-               perror("setgid");
-               return -1;
-       }
-       if (setuid(0) < 0) {
-               perror("setuid");
+       if (!lxc_switch_uid_gid(0, 0))
                return -1;
-       }
-       if (setgroups(0, NULL) < 0) {
-               perror("setgroups");
+
+       if (!lxc_setgroups(0, NULL))
                return -1;
-       }
-       if (unshare(CLONE_NEWNS) < 0) {
-               perror("unshare CLONE_NEWNS");
+
+       ret = unshare(CLONE_NEWNS);
+       if (ret < 0) {
+               CMD_SYSERROR("Failed to unshare mount namespace");
                return -1;
        }
+
        if (detect_shared_rootfs()) {
-               if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) {
-                       printf("Failed to make / rslave\n");
+               ret = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL);
+               if (ret < 0) {
+                       CMD_SYSINFO("Failed to make \"/\" rslave");
                        return -1;
                }
        }
+
        execvp(argv[0], argv);
-       perror("execvpe");
+       CMD_SYSERROR("Failed to execute \"%s\"", argv[0]);
        return -1;
 }
 
 static struct lxc_list active_map;
 
 /*
- * given a string like "b:0:100000:10", map both uids and gids
- * 0-10 to 100000 to 100010
+ * Given a string like "b:0:100000:10", map both uids and gids 0-10 to 100000
+ * to 100010
  */
 static int parse_map(char *map)
 {
+       int i, ret;
+       long host_id, ns_id, range;
+       char which;
        struct id_map *newmap;
-       struct lxc_list *tmp = NULL;
-       int ret;
-       int i;
        char types[2] = {'u', 'g'};
-       char which;
-       long host_id, ns_id, range;
+       struct lxc_list *tmp = NULL;
 
        if (!map)
                return -1;
@@ -193,73 +189,89 @@ static int parse_map(char *map)
 }
 
 /*
- * This is called if the user did not pass any uid ranges in
- * through -m flags.  It's called once to get the default uid
- * map, and once for the default gid map.
- * Go through /etc/subuids and /etc/subgids to find this user's
- * allowed map.  We only use the first one for each of uid and
- * gid, because otherwise we're not sure which entries the user
- * wanted.
+ * This is called if the user did not pass any uid ranges in through -m flags.
+ * It's called once to get the default uid map, and once for the default gid
+ * map.
+ * Go through /etc/subuids and /etc/subgids to find this user's allowed map. We
+ * only use the first one for each of uid and gid, because otherwise we're not
+ * sure which entries the user wanted.
  */
-static int read_default_map(char *fnam, int which, char *username)
+static int read_default_map(char *fnam, int which, char *user)
 {
+       size_t len;
+       char *p1, *p2;
+       unsigned long ul1, ul2;
        FILE *fin;
-       char *line = NULL;
+       int ret = -1;
        size_t sz = 0;
-       struct id_map *newmap;
+       char *line = NULL;
        struct lxc_list *tmp = NULL;
-       char *p1, *p2;
+       struct id_map *newmap = NULL;
 
        fin = fopen(fnam, "r");
        if (!fin)
                return -1;
+
+       len = strlen(user);
        while (getline(&line, &sz, fin) != -1) {
-               if (sz <= strlen(username) ||
-                   strncmp(line, username, strlen(username)) != 0 ||
-                   line[strlen(username)] != ':')
+               if (sz <= len || strncmp(line, user, len) != 0 || line[len] != ':')
                        continue;
+
                p1 = strchr(line, ':');
                if (!p1)
                        continue;
-               p2 = strchr(p1+1, ':');
+
+               p2 = strchr(p1 + 1, ':');
                if (!p2)
                        continue;
+
+               line[strlen(line) - 1] = '\0';
+               *p2 = '\0';
+
+               ret = lxc_safe_ulong(p1 + 1, &ul1);
+               if (ret < 0)
+                       break;
+
+               ret = lxc_safe_ulong(p2 + 1, &ul2);
+               if (ret < 0)
+                       break;
+
+               ret = -1;
                newmap = malloc(sizeof(*newmap));
-               if (!newmap)  {
-                       fclose(fin);
-                       free(line);
-                       return -1;
-               }
-               newmap->hostid = atol(p1+1);
-               newmap->range = atol(p2+1);
+               if (!newmap)
+                       break;
+
                newmap->nsid = 0;
                newmap->idtype = which;
+               newmap->hostid = ul1;
+               newmap->range = ul2;
 
                tmp = malloc(sizeof(*tmp));
                if (!tmp) {
-                       fclose(fin);
-                       free(line);
                        free(newmap);
-                       return -1;
+                       break;
                }
 
                tmp->elem = newmap;
                lxc_list_add_tail(&active_map, tmp);
+
+               ret = 0;
                break;
        }
 
-       free(line);
        fclose(fin);
-       return 0;
+       free(line);
+
+       return ret;
 }
 
 static int find_default_map(void)
 {
+       size_t bufsize;
+       char *buf;
        struct passwd pwent;
+       int ret = -1;
        struct passwd *pwentp = NULL;
-       char *buf;
-       size_t bufsize;
-       int ret;
 
        bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
        if (bufsize == -1)
@@ -272,60 +284,58 @@ static int find_default_map(void)
        ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp);
        if (!pwentp) {
                if (ret == 0)
-                       printf("WARN: could not find matched password record\n");
+                       CMD_SYSERROR("Failed to find matched password record");
 
-               printf("Failed to get password record - %u\n", getuid());
-               free(buf);
-               return -1;
+               CMD_SYSERROR("Failed to get password record for uid %d", getuid());
+               ret = -1;
+               goto out;
        }
 
-       if (read_default_map(subuidfile, ID_TYPE_UID, pwent.pw_name) < 0) {
-               free(buf);
-               return -1;
-       }
+       ret = read_default_map(subuidfile, ID_TYPE_UID, pwent.pw_name);
+       if (ret < 0)
+               goto out;
 
-       if (read_default_map(subgidfile, ID_TYPE_GID, pwent.pw_name) < 0) {
-               free(buf);
-               return -1;
-       }
+       ret = read_default_map(subgidfile, ID_TYPE_GID, pwent.pw_name);
+       if (ret < 0)
+               goto out;
+
+       ret = 0;
 
+out:
        free(buf);
-       return 0;
+
+       return ret;
 }
 
 int main(int argc, char *argv[])
 {
-       int c;
+       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], ttyname1[256], ttyname2[256];
-       int status;
-       int ret;
-       int pid;
+       char ttyname0[256] = {0}, ttyname1[256] = {0}, ttyname2[256] = {0};
        char *default_args[] = {"/bin/sh", NULL};
-       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 */
 
        lxc_log_fd = STDERR_FILENO;
 
-       memset(ttyname0, '\0', sizeof(ttyname0));
-       memset(ttyname1, '\0', sizeof(ttyname1));
-       memset(ttyname2, '\0', sizeof(ttyname2));
-       if (isatty(0)) {
+       if (isatty(STDIN_FILENO)) {
                ret = readlink("/proc/self/fd/0", ttyname0, sizeof(ttyname0));
                if (ret < 0) {
-                       perror("unable to open stdin.");
-                       exit(EXIT_FAILURE);
+                       CMD_SYSERROR("Failed to open stdin");
+                       _exit(EXIT_FAILURE);
                }
+
                ret = readlink("/proc/self/fd/1", ttyname1, sizeof(ttyname1));
                if (ret < 0) {
-                       printf("Warning: unable to open stdout, continuing.\n");
-                       memset(ttyname1, '\0', sizeof(ttyname1));
+                       CMD_SYSINFO("Failed to open stdout. Continuing");
+                       ttyname1[0] = '\0';
                }
+
                ret = readlink("/proc/self/fd/2", ttyname2, sizeof(ttyname2));
                if (ret < 0) {
-                       printf("Warning: unable to open stderr, continuing.\n");
-                       memset(ttyname2, '\0', sizeof(ttyname2));
+                       CMD_SYSINFO("Failed to open stderr. Continuing");
+                       ttyname2[0] = '\0';
                }
        }
 
@@ -334,24 +344,26 @@ int main(int argc, char *argv[])
        while ((c = getopt(argc, argv, "m:h")) != EOF) {
                switch (c) {
                case 'm':
-                       if (parse_map(optarg)) {
+                       ret = parse_map(optarg);
+                       if (ret < 0) {
                                usage(argv[0]);
-                               exit(EXIT_FAILURE);
+                               _exit(EXIT_FAILURE);
                        }
                        break;
                case 'h':
                        usage(argv[0]);
-                       exit(EXIT_SUCCESS);
+                       _exit(EXIT_SUCCESS);
                default:
                        usage(argv[0]);
-                       exit(EXIT_FAILURE);
+                       _exit(EXIT_FAILURE);
                }
        };
 
        if (lxc_list_empty(&active_map)) {
-               if (find_default_map()) {
-                       fprintf(stderr, "You have no allocated subuids or subgids\n");
-                       exit(EXIT_FAILURE);
+               ret = find_default_map();
+               if (ret < 0) {
+                       fprintf(stderr, "Failed to find subuid or subgid allocation\n");
+                       _exit(EXIT_FAILURE);
                }
        }
 
@@ -360,63 +372,103 @@ int main(int argc, char *argv[])
        if (argc < 1)
                argv = default_args;
 
-       if (pipe2(pipe_fds1, O_CLOEXEC) < 0 || pipe2(pipe_fds2, O_CLOEXEC) < 0) {
-               perror("pipe");
-               exit(EXIT_FAILURE);
+       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) { /* Child. */
+       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, 0);
-               opentty(ttyname1, 1);
-               opentty(ttyname2, 2);
+
+               opentty(ttyname0, STDIN_FILENO);
+               opentty(ttyname1, STDOUT_FILENO);
+               opentty(ttyname2, STDERR_FILENO);
 
                ret = unshare(flags);
                if (ret < 0) {
-                       perror("unshare");
-                       return 1;
+                       CMD_SYSERROR("Failed to unshare mount and user namespace");
+                       close(pipe_fds1[1]);
+                       close(pipe_fds2[0]);
+                       _exit(EXIT_FAILURE);
                }
+
                buf[0] = '1';
-               if (lxc_write_nointr(pipe_fds1[1], buf, 1) < 1) {
-                       perror("write pipe");
-                       exit(EXIT_FAILURE);
-               }
-               if (lxc_read_nointr(pipe_fds2[0], buf, 1) < 1) {
-                       perror("read pipe");
-                       exit(EXIT_FAILURE);
+               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);
                }
-               if (buf[0] != '1') {
-                       fprintf(stderr, "parent had an error, child exiting\n");
-                       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]);
-               return do_child((void *)argv);
+
+               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]);
-       if (lxc_read_nointr(pipe_fds1[0], buf, 1) < 1) {
-               perror("read pipe");
-               exit(EXIT_FAILURE);
-       }
+
+       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';
 
-       if (lxc_map_ids(&active_map, pid))
-               fprintf(stderr, "error mapping child\n");
+       ret = lxc_map_ids(&active_map, pid);
+       if (ret < 0)
+               fprintf(stderr, "Failed to write id mapping for child process\n");
 
-       if (lxc_write_nointr(pipe_fds2[1], buf, 1) < 0) {
-               perror("write to pipe");
-               exit(EXIT_FAILURE);
+       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) {
-               printf("waitpid() returns %d, errno %d\n", ret, errno);
-               exit(EXIT_FAILURE);
+               CMD_SYSERROR("Failed to wait on child process");
+               _exit(EXIT_FAILURE);
        }
 
-       exit(WEXITSTATUS(status));
+       _exit(WEXITSTATUS(status));
 }