]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
attach: move uid and gid handling to get_attach_context()
authorChristian Brauner <christian.brauner@ubuntu.com>
Mon, 1 Feb 2021 11:22:42 +0000 (12:22 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Mon, 1 Feb 2021 13:41:08 +0000 (14:41 +0100)
the less we do in do_attach(), the better.

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/attach.c

index 977bb2ee585fbae3c08e15bd701743e7685bc73d..6af3bdf64d8c7e311cdf6bd2be15a0b38b6b1900 100644 (file)
@@ -61,8 +61,10 @@ struct attach_context {
        int init_pid;
        int dfd_init_pid;
        int dfd_self_pid;
-       uid_t init_uid;
-       gid_t init_gid;
+       uid_t setup_uid;
+       gid_t setup_gid;
+       uid_t target_uid;
+       gid_t target_gid;
        char *lsm_label;
        struct lxc_container *container;
        signed long personality;
@@ -151,9 +153,11 @@ static struct attach_context *alloc_attach_context(void)
 
        ctx->dfd_self_pid = -EBADF;
        ctx->dfd_init_pid = -EBADF;
-       ctx->init_gid = -ESRCH;
-       ctx->init_uid = 0;
-       ctx->init_gid = 0;
+       ctx->init_pid = -ESRCH;
+       ctx->setup_uid = LXC_INVALID_UID;
+       ctx->setup_gid = LXC_INVALID_GID;
+       ctx->target_uid = LXC_INVALID_UID;
+       ctx->target_gid = LXC_INVALID_GID;
 
        for (int i = 0; i < LXC_NS_MAX; i++)
                ctx->ns_fd[i] = -EBADF;
@@ -181,45 +185,132 @@ static int get_personality(const char *name, const char *lxcpath,
        return 0;
 }
 
+static int userns_setup_ids(struct attach_context *ctx,
+                           lxc_attach_options_t *options,
+                           uid_t init_hostuid, gid_t init_hostgid)
+{
+       __do_free char *line = NULL;
+       __do_fclose FILE *f_gidmap = NULL, *f_uidmap = NULL;
+       size_t len = 0;
+       uid_t init_ns_uid = LXC_INVALID_UID;
+       gid_t init_ns_gid = LXC_INVALID_GID;
+       uid_t nsuid, hostuid, range_uid;
+       gid_t nsgid, hostgid, range_gid;
+
+       if (!(options->namespaces & CLONE_NEWUSER))
+               return 0;
+
+       f_uidmap = fdopenat(ctx->dfd_init_pid, "uid_map", "re");
+       if (!f_uidmap)
+               return log_error_errno(-errno, errno, "Failed to open uid_map");
+
+       while (getline(&line, &len, f_uidmap) != -1) {
+               if (sscanf(line, "%u %u %u", &nsuid, &hostuid, &range_uid) != 3)
+                       continue;
+
+               if (0 >= nsuid && 0 < nsuid + range_uid) {
+                       ctx->setup_uid = 0;
+                       TRACE("Container has mapping for uid 0");
+                       break;
+               }
+
+               if (init_hostuid >= hostuid && init_hostuid < hostuid + range_uid) {
+                       init_ns_uid = (init_hostuid - hostuid) + nsuid;
+                       TRACE("Container runs with uid %d", init_ns_uid);
+               }
+       }
+
+       f_gidmap = fdopenat(ctx->dfd_init_pid, "gid_map", "re");
+       if (!f_gidmap)
+               return log_error_errno(-errno, errno, "Failed to open gid_map");
+
+       while (getline(&line, &len, f_gidmap) != -1) {
+               if (sscanf(line, "%u %u %u", &nsgid, &hostgid, &range_gid) != 3)
+                       continue;
+
+               if (0 >= nsgid && 0 < nsgid + range_gid) {
+                       ctx->setup_gid = 0;
+                       TRACE("Container has mapping for gid 0");
+                       break;
+               }
+
+               if (init_hostgid >= hostgid && init_hostgid < hostgid + range_gid) {
+                       init_ns_gid = (init_hostgid - hostgid) + nsgid;
+                       TRACE("Container runs with gid %d", init_ns_gid);
+               }
+       }
+
+       if (ctx->setup_uid == LXC_INVALID_UID)
+               ctx->setup_uid = init_ns_uid;
+
+       if (ctx->setup_gid == LXC_INVALID_UID)
+               ctx->setup_gid = init_ns_gid;
+
+       /*
+        * TODO: we should also parse supplementary groups and use
+        * setgroups() to set them.
+        */
+
+       return 0;
+}
+
+static void userns_target_ids(struct attach_context *ctx, lxc_attach_options_t *options)
+{
+       if (options->uid != LXC_INVALID_UID)
+               ctx->target_uid = options->uid;
+       else if (options->namespaces & CLONE_NEWUSER)
+               ctx->target_uid = ctx->setup_uid;
+       else
+               ctx->target_uid = 0;
+
+       if (ctx->target_uid == LXC_INVALID_UID)
+               WARN("Invalid uid specified");
+
+       if (options->gid != LXC_INVALID_GID)
+               ctx->target_gid = options->gid;
+       else if (options->namespaces & CLONE_NEWUSER)
+               ctx->target_gid = ctx->setup_gid;
+       else
+               ctx->target_uid = 0;
+
+       if (ctx->target_gid == LXC_INVALID_GID)
+               WARN("Invalid gid specified");
+}
+
 static int parse_init_status(struct attach_context *ctx, lxc_attach_options_t *options)
 {
        __do_free char *line = NULL;
        __do_fclose FILE *f = NULL;
        size_t len = 0;
        bool caps_found = false;
-       bool uid_found, gid_found;
+       int ret;
+       uid_t init_host_uid = LXC_INVALID_UID;
+       gid_t init_host_gid = LXC_INVALID_GID;
+
 
        f = fdopenat(ctx->dfd_init_pid, "status", "re");
        if (!f)
-               return -errno;
-
-       if (options->namespaces & CLONE_NEWUSER)
-               uid_found = gid_found = false;
-       else
-               uid_found = gid_found = true;
+               return log_error_errno(-errno, errno, "Failed to open status file");
 
        while (getline(&line, &len, f) != -1) {
                signed long value = -1;
-               int ret;
 
-               if (options->namespaces & CLONE_NEWUSER) {
-                       /*
-                        * Format is: real, effective, saved set user, fs we only care
-                        * about real uid.
-                        */
-                       ret = sscanf(line, "Uid: %ld", &value);
-                       if (ret != EOF && ret == 1) {
-                               uid_found = true;
-                               ctx->init_uid = (uid_t)value;
-                               goto next;
-                       }
+               /*
+                * Format is: real, effective, saved set user, fs we only care
+                * about real uid.
+                */
+               ret = sscanf(line, "Uid: %ld", &value);
+               if (ret != EOF && ret == 1) {
+                       init_host_uid = (uid_t)value;
+                       TRACE("Container's init process runs with hostuid %d", init_host_uid);
+                       goto next;
+               }
 
-                       ret = sscanf(line, "Gid: %ld", &value);
-                       if (ret != EOF && ret == 1) {
-                               gid_found = true;
-                               ctx->init_gid = (gid_t)value;
-                               goto next;
-                       }
+               ret = sscanf(line, "Gid: %ld", &value);
+               if (ret != EOF && ret == 1) {
+                       init_host_gid = (gid_t)value;
+                       TRACE("Container's init process runs with hostgid %d", init_host_gid);
+                       goto next;
                }
 
                ret = sscanf(line, "CapBnd: %llx", &ctx->capability_mask);
@@ -229,11 +320,18 @@ static int parse_init_status(struct attach_context *ctx, lxc_attach_options_t *o
                }
 
         next:
-               if (uid_found && gid_found && caps_found)
+               if (init_host_uid != LXC_INVALID_UID &&
+                   init_host_gid != LXC_INVALID_GID &&
+                   caps_found)
                        break;
 
        }
 
+       ret = userns_setup_ids(ctx, options, init_host_uid, init_host_gid);
+       if (ret)
+               return log_error_errno(ret, errno, "Failed to get setup ids");
+       userns_target_ids(ctx, options);
+
        /*
         * TODO: we should also parse supplementary groups and use
         * setgroups() to set them.
@@ -269,7 +367,7 @@ static int get_attach_context(struct attach_context *ctx,
 
        ctx->dfd_self_pid = openat(-EBADF, path, O_CLOEXEC | O_NOCTTY | O_NOFOLLOW | O_PATH | O_DIRECTORY);
        if (ctx->dfd_self_pid < 0)
-               return -errno;
+               return log_error_errno(-errno, errno, "Failed to open /proc/self");
 
        ret = snprintf(path, sizeof(path), "/proc/%d", ctx->init_pid);
        if (ret < 0 || ret >= sizeof(path))
@@ -277,7 +375,7 @@ static int get_attach_context(struct attach_context *ctx,
 
        ctx->dfd_init_pid = openat(-EBADF, path, O_CLOEXEC | O_NOCTTY | O_NOFOLLOW | O_PATH | O_DIRECTORY);
        if (ctx->dfd_init_pid < 0)
-               return -errno;
+               return log_error_errno(-errno, errno, "Failed to open /proc/%d", ctx->init_pid);
 
        if (init_pidfd >= 0) {
                ret = lxc_raw_pidfd_send_signal(init_pidfd, 0, NULL, 0);
@@ -287,9 +385,28 @@ static int get_attach_context(struct attach_context *ctx,
                        TRACE("Container process still running and PID was not recycled");
        }
 
+       /* Determine which namespaces the container was created with. */
+       if (options->namespaces == -1) {
+               options->namespaces = lxc_cmd_get_clone_flags(container->name, container->config_path);
+               if (options->namespaces == -1)
+                       return log_error_errno(-EINVAL, EINVAL, "Failed to automatically determine the namespaces which the container uses");
+
+               for (int i = 0; i < LXC_NS_MAX; i++) {
+                       if (ns_info[i].clone_flag & CLONE_NEWCGROUP)
+                               if (!(options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) ||
+                                   !cgns_supported())
+                                       continue;
+
+                       if (ns_info[i].clone_flag & options->namespaces)
+                               continue;
+
+                       ctx->ns_inherited |= ns_info[i].clone_flag;
+               }
+       }
+
        ret = parse_init_status(ctx, options);
        if (ret)
-               return log_error_errno(-errno, errno, "Failed to open parse status file");
+               return log_error_errno(-errno, errno, "Failed to open parse file");
 
        ctx->lsm_ops = lsm_init_static();
 
@@ -856,10 +973,6 @@ __noreturn static void do_attach(struct attach_payload *ap)
        lxc_attach_exec_t attach_function = move_ptr(ap->exec_function);
        void *attach_function_args = move_ptr(ap->exec_payload);
        int lsm_fd, ret;
-       uid_t new_uid;
-       gid_t new_gid;
-       uid_t ns_root_uid = 0;
-       gid_t ns_root_gid = 0;
        lxc_attach_options_t* options = ap->options;
         struct attach_context *ctx = ap->ctx;
         struct lxc_conf *conf = ctx->container->lxc_conf;
@@ -948,39 +1061,9 @@ __noreturn static void do_attach(struct attach_payload *ap)
        if (!lxc_setgroups(0, NULL) && errno != EPERM)
                goto on_error;
 
-       if (options->namespaces & CLONE_NEWUSER) {
-               /* Check whether nsuid 0 has a mapping. */
-               ns_root_uid = get_ns_uid(0);
-
-               /* Check whether nsgid 0 has a mapping. */
-               ns_root_gid = get_ns_gid(0);
-
-               /* If there's no mapping for nsuid 0 try to retrieve the nsuid
-                * init was started with.
-                */
-               if (ns_root_uid == LXC_INVALID_UID)
-                       ns_root_uid = ctx->init_uid;
-
-               if (ns_root_gid == LXC_INVALID_UID)
-                       ns_root_gid = ctx->init_gid;
-
-               if (ns_root_uid == LXC_INVALID_UID)
-                       goto on_error;
-
-               if (!lxc_switch_uid_gid(ns_root_uid, ns_root_gid))
+       if (options->namespaces & CLONE_NEWUSER)
+               if (!lxc_switch_uid_gid(ctx->setup_uid, ctx->setup_gid))
                        goto on_error;
-       }
-
-       /* Set {u,g}id. */
-       if (options->uid != LXC_INVALID_UID)
-               new_uid = options->uid;
-       else
-               new_uid = ns_root_uid;
-
-       if (options->gid != LXC_INVALID_GID)
-               new_gid = options->gid;
-       else
-               new_gid = ns_root_gid;
 
        if (attach_lsm(options) && ctx->lsm_label) {
                bool on_exec;
@@ -1071,19 +1154,19 @@ __noreturn static void do_attach(struct attach_payload *ap)
 
        put_attach_payload(ap);
 
-       /* Avoid unnecessary syscalls. */
-       if (new_uid == ns_root_uid)
-               new_uid = LXC_INVALID_UID;
-
-       if (new_gid == ns_root_gid)
-               new_gid = LXC_INVALID_GID;
-
        /* Make sure that the processes STDIO is correctly owned by the user that we are switching to */
-       ret = fix_stdio_permissions(new_uid);
+       ret = fix_stdio_permissions(ctx->target_uid);
        if (ret)
                INFO("Failed to adjust stdio permissions");
 
-       if (!lxc_switch_uid_gid(new_uid, new_gid))
+       /* Avoid unnecessary syscalls. */
+       if (ctx->setup_uid == ctx->target_uid)
+               ctx->target_uid = LXC_INVALID_UID;
+
+       if (ctx->setup_gid == ctx->target_gid)
+               ctx->target_gid = LXC_INVALID_GID;
+
+       if (!lxc_switch_uid_gid(ctx->target_uid, ctx->target_gid))
                goto on_error;
 
        /* We're done, so we can now do whatever the user intended us to do. */
@@ -1194,27 +1277,6 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
        if (!no_new_privs(ctx->container, options))
                WARN("Could not determine whether PR_SET_NO_NEW_PRIVS is set");
 
-       /* Determine which namespaces the container was created with. */
-       if (options->namespaces == -1) {
-               options->namespaces = lxc_cmd_get_clone_flags(name, lxcpath);
-               if (options->namespaces == -1) {
-                       put_attach_context(ctx);
-                       return log_error(-1, "Failed to automatically determine the namespaces which the container uses");
-               }
-
-               for (int i = 0; i < LXC_NS_MAX; i++) {
-                       if (ns_info[i].clone_flag & CLONE_NEWCGROUP)
-                               if (!(options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) ||
-                                   !cgns_supported())
-                                       continue;
-
-                       if (ns_info[i].clone_flag & options->namespaces)
-                               continue;
-
-                       ctx->ns_inherited |= ns_info[i].clone_flag;
-               }
-       }
-
        ret = get_attach_context_nsfds(ctx, options);
        if (ret) {
                lxc_container_put(container);