From: Marcos Paulo de Souza Date: Wed, 29 Nov 2017 01:49:28 +0000 (-0200) Subject: lxc_unshare: Add uid_mapping when creating userns X-Git-Tag: lxc-2.0.10~536 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0dd8fb29b9e171b81a5b83018835c0dc6542ce27;p=thirdparty%2Flxc.git lxc_unshare: Add uid_mapping when creating userns Change conf.c to export function write_id_mapping, which will now be called inside main function of lxc_unshare.c. This is required because setuid syscalls only permits a new userns to set a new uid if the uid of parameter is mapped inside the ns using uid_map file[1]. So, just after the clone invocation, map the uid passed as parameter into the newly created user namespace, and put the current uid as the ID-outside-ns. After the mapping is done, setuid call succeeds. Closes: #494 [1] https://elixir.free-electrons.com/linux/latest/source/kernel/user_namespace.c#L286 Signed-off-by: Marcos Paulo de Souza --- diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 898c728ad..03fae281c 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2342,7 +2342,7 @@ struct lxc_conf *lxc_conf_init(void) return new; } -static int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, +int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, size_t buf_size) { char path[MAXPATHLEN]; diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 85538a239..aa819da4d 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -287,6 +287,9 @@ struct lxc_conf { unsigned int ephemeral; }; +int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, + size_t buf_size); + #ifdef HAVE_TLS extern __thread struct lxc_conf *current_config; #else diff --git a/src/lxc/tools/lxc_unshare.c b/src/lxc/tools/lxc_unshare.c index 843f663ed..59b8461a9 100644 --- a/src/lxc/tools/lxc_unshare.c +++ b/src/lxc/tools/lxc_unshare.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -93,24 +94,37 @@ static bool lookup_user(const char *optarg, uid_t *uid) return true; } - struct start_arg { char ***args; int *flags; uid_t *uid; bool setuid; int want_default_mounts; + int wait_fd; const char *want_hostname; }; static int do_start(void *arg) { + int ret; + uint64_t wait_val; struct start_arg *start_arg = arg; char **args = *start_arg->args; int flags = *start_arg->flags; uid_t uid = *start_arg->uid; int want_default_mounts = start_arg->want_default_mounts; const char *want_hostname = start_arg->want_hostname; + int wait_fd = start_arg->wait_fd; + + if (start_arg->setuid) { + /* waiting until uid maps is set */ + ret = read(wait_fd, &wait_val, sizeof(wait_val)); + if (ret == -1) { + close(wait_fd); + fprintf(stderr, "read eventfd failed\n"); + exit(EXIT_FAILURE); + } + } if ((flags & CLONE_NEWNS) && want_default_mounts) lxc_setup_fs(); @@ -143,6 +157,7 @@ int main(int argc, char *argv[]) int flags = 0, daemonize = 0; uid_t uid = 0; /* valid only if (flags & CLONE_NEWUSER) */ pid_t pid; + uint64_t wait_val = 1; struct my_iflist *tmpif, *my_iflist = NULL; struct start_arg start_arg = { .args = &args, @@ -241,12 +256,49 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } + if (start_arg.setuid) { + start_arg.wait_fd = eventfd(0, EFD_CLOEXEC); + if (start_arg.wait_fd < 0) { + fprintf(stderr, "failed to create eventfd\n"); + exit(EXIT_FAILURE); + } + } + pid = lxc_clone(do_start, &start_arg, flags); if (pid < 0) { fprintf(stderr, "failed to clone\n"); exit(EXIT_FAILURE); } + if (start_arg.setuid) { + /* enough space to accommodate uids */ + char *umap = (char *)alloca(100); + + /* create new uid mapping using current UID and the one + * specified as parameter + */ + ret = snprintf(umap, 100, "%d %d 1\n" , *(start_arg.uid), getuid()); + if (ret < 0 || ret >= 100) { + close(start_arg.wait_fd); + fprintf(stderr, "snprintf failed"); + exit(EXIT_FAILURE); + } + + ret = write_id_mapping(ID_TYPE_UID, pid, umap, strlen(umap)); + if (ret < 0) { + close(start_arg.wait_fd); + fprintf(stderr, "uid mapping failed\n"); + exit(EXIT_FAILURE); + } + + ret = write(start_arg.wait_fd, &wait_val, sizeof(wait_val)); + if (ret < 0) { + close(start_arg.wait_fd); + fprintf(stderr, "write to eventfd failed\n"); + exit(EXIT_FAILURE); + } + } + if (my_iflist) { for (tmpif = my_iflist; tmpif; tmpif = tmpif->mi_next) { if (lxc_netdev_move_by_name(tmpif->mi_ifname, pid, NULL) < 0)