[lxc-devel] [lxc/master] lxc-usernsexec: lxc-usernsexec: introduce and use userns_exec() and implement -b, -u, and -g arguments
brauner on Github
lxc-bot at linuxcontainers.org
Thu May 21 10:03:33 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 364 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200521/2d3e0a31/attachment-0001.bin>
-------------- next part --------------
From 1609452e3a0554fef5e688146f6095a158d30c5c Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 21 May 2020 10:25:34 +0200
Subject: [PATCH 1/5] utils_no_static: add filer for helpers that cannot be
statically linked
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
src/lxc/Makefile.am | 3 +++
src/lxc/conf.c | 57 +--------------------------------------------
2 files changed, 4 insertions(+), 56 deletions(-)
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index d1e23647e0..f99fb2520c 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -50,6 +50,7 @@ noinst_HEADERS = api_extensions.h \
tools/arguments.h \
storage/storage_utils.h \
utils.h \
+ utils_no_static.h \
uuid.h
if IS_BIONIC
@@ -150,6 +151,7 @@ liblxc_la_SOURCES = af_unix.c af_unix.h \
syscall_wrappers.h \
terminal.c \
utils.c utils.h \
+ utils_no_static.c utils_no_static.h \
uuid.c uuid.h \
version.h \
$(LSM_SOURCES)
@@ -418,6 +420,7 @@ lxc_usernsexec_SOURCES = cmd/lxc_usernsexec.c \
memory_utils.h \
string_utils.c string_utils.h \
syscall_wrappers.h \
+ utils_no_static.c utils_no_static.h
utils.c utils.h
endif
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 5cbca60006..e4408201d4 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -59,6 +59,7 @@
#include "syscall_wrappers.h"
#include "terminal.h"
#include "utils.h"
+#include "utils_no_static.h"
#include "uuid.h"
#ifdef MAJOR_IN_MKDEV
@@ -4687,62 +4688,6 @@ int userns_exec_mapped_root(const char *path, int path_fd,
return wait_for_pid(pid);
}
-/* not thread-safe, do not use from api without first forking */
-static char *getuname(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(geteuid(), &pwent, buf, bufsize, &pwentp);
- if (!pwentp) {
- if (ret == 0)
- WARN("Could not find matched password record.");
-
- return log_error(NULL, "Failed to get password record - %u", geteuid());
- }
-
- return strdup(pwent.pw_name);
-}
-
-/* not thread-safe, do not use from api without first forking */
-static char *getgname(void)
-{
- __do_free char *buf = NULL;
- struct group grent;
- struct group *grentp = NULL;
- size_t bufsize;
- int ret;
-
- bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
- if (bufsize == -1)
- bufsize = 1024;
-
- buf = malloc(bufsize);
- if (!buf)
- return NULL;
-
- ret = getgrgid_r(getegid(), &grent, buf, bufsize, &grentp);
- if (!grentp) {
- if (ret == 0)
- WARN("Could not find matched group record");
-
- return log_error(NULL, "Failed to get group record - %u", getegid());
- }
-
- return strdup(grent.gr_name);
-}
-
/* not thread-safe, do not use from api without first forking */
void suggest_default_idmap(void)
{
From d713543f2a27a6d406b98081d4683453c4d19a5c Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 21 May 2020 10:32:15 +0200
Subject: [PATCH 2/5] lxc-usernsexec: always mount MS_SLAVE | MS_REC
There's no point in not doing it and checking before.
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
src/lxc/cmd/lxc_usernsexec.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/src/lxc/cmd/lxc_usernsexec.c b/src/lxc/cmd/lxc_usernsexec.c
index 3c22482f4f..63a59a8d4c 100644
--- a/src/lxc/cmd/lxc_usernsexec.c
+++ b/src/lxc/cmd/lxc_usernsexec.c
@@ -100,12 +100,10 @@ static int do_child(void *vargv)
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;
- }
+ ret = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL);
+ if (ret) {
+ CMD_SYSINFO("Failed to make \"/\" rslave");
+ return -1;
}
execvp(argv[0], argv);
From 776cf48433d9852015e9529f136bd4dedff6264e Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 21 May 2020 10:34:01 +0200
Subject: [PATCH 3/5] lxc-usernsexec: setgroups() can legitimately fail when
setgroups == "deny"
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
src/lxc/cmd/lxc_usernsexec.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lxc/cmd/lxc_usernsexec.c b/src/lxc/cmd/lxc_usernsexec.c
index 63a59a8d4c..1166ef3977 100644
--- a/src/lxc/cmd/lxc_usernsexec.c
+++ b/src/lxc/cmd/lxc_usernsexec.c
@@ -87,7 +87,7 @@ static int do_child(void *vargv)
int ret;
char **argv = (char **)vargv;
- if (!lxc_setgroups(0, NULL))
+ if (setgroups(0, NULL) && errno != EPERM)
return -1;
/* Assume we want to become root */
From 221a471445df22453a902279162a9425893191ec Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 21 May 2020 10:34:30 +0200
Subject: [PATCH 4/5] lxc-usernsexec: remove outdated comment
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
src/lxc/cmd/lxc_usernsexec.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/lxc/cmd/lxc_usernsexec.c b/src/lxc/cmd/lxc_usernsexec.c
index 1166ef3977..65dc82e311 100644
--- a/src/lxc/cmd/lxc_usernsexec.c
+++ b/src/lxc/cmd/lxc_usernsexec.c
@@ -80,7 +80,6 @@ static void opentty(const char *tty, int which)
close(fd);
}
}
-/* Code copy end */
static int do_child(void *vargv)
{
From 780abb703c33b44790c38e9554aae9893657a8c9 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 21 May 2020 11:53:03 +0200
Subject: [PATCH 5/5] lxc-usernsexec: introduce and use userns_exec() and
implement -b, -u, and -g arguments
lxc-usernsexec -mb1:1000:1 -b 1
lxc-usernsexec -mb1:1000:1 -u 1 -g 1
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
src/lxc/cmd/lxc_usernsexec.c | 199 ++++++++++++-----------------------
src/lxc/conf.c | 80 ++++++++++++++
src/lxc/conf.h | 3 +
3 files changed, 150 insertions(+), 132 deletions(-)
diff --git a/src/lxc/cmd/lxc_usernsexec.c b/src/lxc/cmd/lxc_usernsexec.c
index 65dc82e311..86b980cbdb 100644
--- a/src/lxc/cmd/lxc_usernsexec.c
+++ b/src/lxc/cmd/lxc_usernsexec.c
@@ -41,6 +41,12 @@ static void usage(const char *name)
printf("\n");
printf(" -m <uid-maps> uid maps to use\n");
printf("\n");
+ printf(" -b <id> uid and gid to use\n");
+ printf("\n");
+ printf(" -u <id> uid to use\n");
+ printf("\n");
+ printf(" -g <id> gid to use\n");
+ printf("\n");
printf(" -s: map self\n");
printf(" uid-maps: [u|g|b]:ns_id:host_id:range\n");
printf(" [u|g|b]: map user id, group id, or both\n");
@@ -81,32 +87,28 @@ static void opentty(const char *tty, int which)
}
}
-static int do_child(void *vargv)
-{
- int ret;
- char **argv = (char **)vargv;
+struct child_args {
+ char **argv;
+ char *tty_stdin;
+ char *tty_stdout;
+ char *tty_stderr;
+};
- if (setgroups(0, NULL) && errno != EPERM)
- return -1;
-
- /* Assume we want to become root */
- if (!lxc_switch_uid_gid(0, 0))
- return -1;
+static int do_child(void *payload)
+{
+ struct child_args *args = payload;
- ret = unshare(CLONE_NEWNS);
- if (ret < 0) {
- CMD_SYSERROR("Failed to unshare mount namespace");
+ if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) {
+ CMD_SYSERROR("Failed to remount \"/\"");
return -1;
}
- ret = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL);
- if (ret) {
- CMD_SYSINFO("Failed to make \"/\" rslave");
- return -1;
- }
+ opentty(args->tty_stdin, STDIN_FILENO);
+ opentty(args->tty_stdout, STDOUT_FILENO);
+ opentty(args->tty_stderr, STDERR_FILENO);
- execvp(argv[0], argv);
- CMD_SYSERROR("Failed to execute \"%s\"", argv[0]);
+ execvp(args->argv[0], args->argv);
+ CMD_SYSERROR("Failed to execute \"%s\"", args->argv[0]);
return -1;
}
@@ -301,14 +303,17 @@ static bool do_map_self(void)
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;
+ int c, ret;
char ttyname0[256] = {0}, ttyname1[256] = {0}, ttyname2[256] = {0};
char *default_args[] = {"/bin/sh", NULL};
bool map_self = false;
+ uid_t nsuid = 0;
+ gid_t nsgid = 0;
+ struct child_args args = {
+ .tty_stdin = ttyname0,
+ .tty_stdout = ttyname1,
+ .tty_stderr = ttyname2,
+ };
lxc_log_fd = STDERR_FILENO;
@@ -334,8 +339,29 @@ int main(int argc, char *argv[])
lxc_list_init(&active_map);
- while ((c = getopt(argc, argv, "m:hs")) != EOF) {
+ while ((c = getopt(argc, argv, "b:g:hm:su:")) != EOF) {
+ int val;
switch (c) {
+ case 'b':
+ ret = lxc_safe_int(optarg, &val);
+ if (ret) {
+ fprintf(stderr, "%m - Failed to parse gid %s\n", optarg);
+ _exit(EXIT_FAILURE);
+ }
+ nsuid = (val < 0) ? LXC_INVALID_UID : val;
+ nsgid = nsuid;
+ break;
+ case 'g':
+ ret = lxc_safe_int(optarg, &val);
+ if (ret) {
+ fprintf(stderr, "%m - Failed to parse gid %s\n", optarg);
+ _exit(EXIT_FAILURE);
+ }
+ nsgid = (val < 0) ? LXC_INVALID_GID : val;
+ break;
+ case 'h':
+ usage(argv[0]);
+ _exit(EXIT_SUCCESS);
case 'm':
ret = parse_map(optarg);
if (ret < 0) {
@@ -343,12 +369,17 @@ int main(int argc, char *argv[])
_exit(EXIT_FAILURE);
}
break;
- case 'h':
- usage(argv[0]);
- _exit(EXIT_SUCCESS);
case 's':
map_self = true;
break;
+ case 'u':
+ ret = lxc_safe_int(optarg, &val);
+ if (ret) {
+ fprintf(stderr, "%m - Failed to parse uid %s\n", optarg);
+ _exit(EXIT_FAILURE);
+ }
+ nsuid = (val < 0) ? LXC_INVALID_UID : val;
+ break;
default:
usage(argv[0]);
_exit(EXIT_FAILURE);
@@ -363,8 +394,10 @@ int main(int argc, char *argv[])
}
}
- // Do we want to support map-self with no other allocations?
- // If so we should move this above the previous block.
+ /*
+ * Do we want to support map-self with no other allocations?
+ * If so we should move this above the previous block.
+ */
if (map_self) {
if (!do_map_self()) {
fprintf(stderr, "Failed mapping own uid\n");
@@ -376,106 +409,8 @@ int main(int argc, char *argv[])
argc = argc - optind;
if (argc < 1)
argv = default_args;
+ args.argv = argv;
- 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]);
- _exit(EXIT_FAILURE);
- }
-
- 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));
+ _exit(userns_exec(CLONE_NEWUSER | CLONE_NEWNS, &active_map, do_child,
+ &args, nsuid, nsgid));
}
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index e4408201d4..ae15cf81c7 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -4074,6 +4074,86 @@ static struct lxc_list *get_minimal_idmap(const struct lxc_conf *conf,
return move_ptr(idmap);
}
+int userns_exec(unsigned int flags, struct lxc_list *idmap,
+ int (*fn_child)(void *), void *fn_child_data,
+ uid_t nsuid, gid_t nsgid)
+{
+ char c = '1';
+ ssize_t ret;
+ pid_t pid;
+ int sock_fds[2];
+
+ if (!fn_child)
+ return ret_errno(EINVAL);
+
+ ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, sock_fds);
+ if (ret < 0)
+ return -errno;
+
+ pid = lxc_raw_clone(CLONE_NEWUSER | CLONE_NEWNS, NULL);
+ if (pid < 0) {
+ SYSERROR("Failed to create new process");
+ goto on_error;
+ }
+
+ if (pid == 0) {
+ close_prot_errno_disarm(sock_fds[1]);
+
+ ret = lxc_read_nointr(sock_fds[0], &c, 1);
+ if (ret != 1)
+ _exit(EXIT_FAILURE);
+
+ close_prot_errno_disarm(sock_fds[0]);
+
+ if (!setgroups(0, NULL) && errno != EPERM)
+ _exit(EXIT_FAILURE);
+
+ if (gid_valid(nsgid) && setresgid(nsgid, nsgid, nsgid)) {
+ SYSERROR("Failed to setresgid(%d, %d, %d)", nsgid, nsgid, nsgid);
+ _exit(EXIT_FAILURE);
+ }
+
+ if (uid_valid(nsuid) && setresuid(nsuid, nsuid, nsuid)) {
+ SYSERROR("Failed to setresuid(%d, %d, %d)", nsuid, nsuid, nsuid);
+ _exit(EXIT_FAILURE);
+ }
+
+ ret = fn_child(fn_child_data);
+ if (ret) {
+ SYSERROR("Running function in new user namespace failed");
+ _exit(EXIT_FAILURE);
+ }
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ close_prot_errno_disarm(sock_fds[0]);
+
+ /* Set up {g,u}id mapping for user namespace of child process. */
+ ret = lxc_map_ids(idmap, pid);
+ if (ret < 0) {
+ ERROR("Error setting up {g,u}id mappings for child process \"%d\"", pid);
+ goto on_error;
+ }
+
+ /* Tell child to proceed. */
+ ret = lxc_write_nointr(sock_fds[1], &c, 1);
+ if (ret != 1) {
+ SYSERROR("Failed telling child process \"%d\" to proceed", pid);
+ goto on_error;
+ }
+
+on_error:
+ close_prot_errno_disarm(sock_fds[0]);
+ close_prot_errno_disarm(sock_fds[1]);
+
+ /* Wait for child to finish. */
+ if (pid < 0)
+ return -1;
+
+ return wait_for_pid(pid);
+}
+
/*
* Run a function in a new user namespace.
* The caller's euid/egid will be mapped if it is not already.
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 63d6e8cfb7..11a989a2b9 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -476,5 +476,8 @@ extern int userns_exec_minimal(const struct lxc_conf *conf,
int (*fn_child)(void *), void *fn_child_data);
extern int userns_exec_mapped_root(const char *path, int path_fd,
const struct lxc_conf *conf);
+extern int userns_exec(unsigned int flags, struct lxc_list *idmap,
+ int (*fn_child)(void *), void *fn_child_data,
+ uid_t uid, gid_t gid);
#endif /* __LXC_CONF_H */
More information about the lxc-devel
mailing list