diff options
author | Lennart Poettering <lennart@poettering.net> | 2013-12-13 22:02:47 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2013-12-14 05:10:25 +0100 |
commit | bc9fd78c7bfc39881e19457e476393635f8b0442 (patch) | |
tree | 3c4dc6461460a2b4094516eb35424d36f52f455a | |
parent | 3fa5dd6de798e17d93531bc900b8e2dc587c38f3 (diff) |
bus: when connecting to a container's kdbus instance, enter namespace first
Previously we'd open the connection in the originating namespace, which
meant most peers of the bus would not be able to make sense of the
PID/UID/... identity of us since we didn't exist in the namespace they
run in. However they require this identity for privilege decisions,
hence disallowing access to anything from the host.
Instead, when connecting to a container, create a temporary subprocess,
make it join the container's namespace and then connect from there to
the kdbus instance. This is similar to how we do it for socket
conections already.
THis also unifies the namespacing code used by machinectl and the bus
APIs.
-rw-r--r-- | src/core/load-fragment.c | 13 | ||||
-rw-r--r-- | src/libsystemd-bus/bus-container.c | 149 | ||||
-rw-r--r-- | src/libsystemd-bus/bus-container.h | 3 | ||||
-rw-r--r-- | src/libsystemd-bus/sd-bus.c | 69 | ||||
-rw-r--r-- | src/machine/machinectl.c | 41 | ||||
-rw-r--r-- | src/shared/util.c | 73 | ||||
-rw-r--r-- | src/shared/util.h | 5 |
7 files changed, 266 insertions, 87 deletions
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 99cf20dcce..183c43d58f 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2680,31 +2680,28 @@ int unit_load_fragment(Unit *u) { /* Look for a template */ if (u->load_state == UNIT_STUB && u->instance) { - char *k; + _cleanup_free_ char *k; k = unit_name_template(u->id); if (!k) return -ENOMEM; r = load_from_path(u, k); - free(k); - if (r < 0) return r; if (u->load_state == UNIT_STUB) SET_FOREACH(t, u->names, i) { + _cleanup_free_ char *z = NULL; if (t == u->id) continue; - k = unit_name_template(t); - if (!k) + z = unit_name_template(t); + if (!z) return -ENOMEM; - r = load_from_path(u, k); - free(k); - + r = load_from_path(u, z); if (r < 0) return r; diff --git a/src/libsystemd-bus/bus-container.c b/src/libsystemd-bus/bus-container.c index 33478c02de..5d31f5afa7 100644 --- a/src/libsystemd-bus/bus-container.c +++ b/src/libsystemd-bus/bus-container.c @@ -28,51 +28,23 @@ #include "bus-socket.h" #include "bus-container.h" -int bus_container_connect(sd_bus *b) { - _cleanup_free_ char *s = NULL, *ns = NULL, *root = NULL, *class = NULL; +int bus_container_connect_socket(sd_bus *b) { _cleanup_close_ int nsfd = -1, rootfd = -1; - char *p; - siginfo_t si; pid_t leader, child; + siginfo_t si; int r; assert(b); assert(b->input_fd < 0); assert(b->output_fd < 0); - p = strappenda("/run/systemd/machines/", b->machine); - r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); - if (r == -ENOENT) - return -EHOSTDOWN; + r = container_get_leader(b->machine, &leader); if (r < 0) return r; - if (!s) - return -EIO; - - if (!streq_ptr(class, "container")) - return -EIO; - r = parse_pid(s, &leader); + r = namespace_open(leader, &nsfd, &rootfd); if (r < 0) return r; - if (leader <= 1) - return -EIO; - - r = asprintf(&ns, "/proc/%lu/ns/mnt", (unsigned long) leader); - if (r < 0) - return -ENOMEM; - - nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (nsfd < 0) - return -errno; - - r = asprintf(&root, "/proc/%lu/root", (unsigned long) leader); - if (r < 0) - return -ENOMEM; - - rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (rootfd < 0) - return -errno; b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (b->input_fd < 0) @@ -89,14 +61,9 @@ int bus_container_connect(sd_bus *b) { return -errno; if (child == 0) { - r = setns(nsfd, CLONE_NEWNS); - if (r < 0) - _exit(255); - - if (fchdir(rootfd) < 0) - _exit(255); - if (chroot(".") < 0) + r = namespace_enter(nsfd, rootfd); + if (r < 0) _exit(255); r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); @@ -107,7 +74,7 @@ int bus_container_connect(sd_bus *b) { _exit(255); } - _exit(0); + _exit(EXIT_SUCCESS); } r = wait_for_terminate(child, &si); @@ -120,8 +87,108 @@ int bus_container_connect(sd_bus *b) { if (si.si_status == 1) return 1; - if (si.si_status != 0) + if (si.si_status != EXIT_SUCCESS) return -EIO; return bus_socket_start_auth(b); } + +int bus_container_connect_kernel(sd_bus *b) { + _cleanup_close_pipe_ int pair[2] = { -1, -1 }; + _cleanup_close_ int nsfd = -1, rootfd = -1; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + pid_t leader, child; + siginfo_t si; + int r; + _cleanup_close_ int fd = -1; + + assert(b); + assert(b->input_fd < 0); + assert(b->output_fd < 0); + + r = container_get_leader(b->machine, &leader); + if (r < 0) + return r; + + r = namespace_open(leader, &nsfd, &rootfd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return -errno; + + if (child == 0) { + close_nointr_nofail(pair[0]); + pair[0] = -1; + + r = namespace_enter(nsfd, rootfd); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + _exit(EXIT_FAILURE); + + cmsg = CMSG_FIRSTHDR(&mh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + + mh.msg_controllen = cmsg->cmsg_len; + + if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + close_nointr_nofail(pair[1]); + pair[1] = -1; + + if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0) + return -errno; + + for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + int *fds; + unsigned n_fds; + + fds = (int*) CMSG_DATA(cmsg); + n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + + if (n_fds != 1) { + close_many(fds, n_fds); + return -EIO; + } + + fd = fds[0]; + } + + r = wait_for_terminate(child, &si); + if (r < 0) + return r; + + if (si.si_code != CLD_EXITED) + return -EIO; + + if (si.si_status != EXIT_SUCCESS) + return -EIO; + + b->input_fd = b->output_fd = fd; + fd = -1; + + return bus_kernel_take_fd(b); +} diff --git a/src/libsystemd-bus/bus-container.h b/src/libsystemd-bus/bus-container.h index 65f43ab4fd..c6f757a99b 100644 --- a/src/libsystemd-bus/bus-container.h +++ b/src/libsystemd-bus/bus-container.h @@ -23,4 +23,5 @@ #include "sd-bus.h" -int bus_container_connect(sd_bus *b); +int bus_container_connect_socket(sd_bus *b); +int bus_container_connect_kernel(sd_bus *b); diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index 932bf226c5..4eaceeff04 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -740,7 +740,7 @@ static int parse_kernel_address(sd_bus *b, const char **p, char **guid) { return 0; } -static int parse_container_address(sd_bus *b, const char **p, char **guid) { +static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) { _cleanup_free_ char *machine = NULL; int r; @@ -782,6 +782,49 @@ static int parse_container_address(sd_bus *b, const char **p, char **guid) { return 0; } +static int parse_container_kernel_address(sd_bus *b, const char **p, char **guid) { + _cleanup_free_ char *machine = NULL; + int r; + + assert(b); + assert(p); + assert(*p); + assert(guid); + + while (**p != 0 && **p != ';') { + r = parse_address_key(p, "guid", guid); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "machine", &machine); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!machine) + return -EINVAL; + + if (!filename_is_safe(machine)) + return -EINVAL; + + free(b->machine); + b->machine = machine; + machine = NULL; + + free(b->kernel); + b->kernel = strdup("/dev/kdbus/0-system/bus"); + if (!b->kernel) + return -ENOMEM; + + return 0; +} + static void bus_reset_parsed_address(sd_bus *b) { assert(b); @@ -855,10 +898,18 @@ static int bus_parse_next_address(sd_bus *b) { return r; break; - } else if (startswith(a, "x-container:")) { + } else if (startswith(a, "x-container-unix:")) { + + a += 17; + r = parse_container_unix_address(b, &a, &guid); + if (r < 0) + return r; + + break; + } else if (startswith(a, "x-container-kernel:")) { - a += 12; - r = parse_container_address(b, &a, &guid); + a += 19; + r = parse_container_kernel_address(b, &a, &guid); if (r < 0) return r; @@ -892,10 +943,12 @@ static int bus_start_address(sd_bus *b) { if (b->exec_path) r = bus_socket_exec(b); + else if (b->machine && b->kernel) + r = bus_container_connect_kernel(b); + else if (b->machine && b->sockaddr.sa.sa_family != AF_UNSPEC) + r = bus_container_connect_socket(b); else if (b->kernel) r = bus_kernel_connect(b); - else if (b->machine) - r = bus_container_connect(b); else if (b->sockaddr.sa.sa_family != AF_UNSPEC) r = bus_socket_connect(b); else @@ -1144,9 +1197,9 @@ _public_ int sd_bus_open_system_container(const char *machine, sd_bus **ret) { return -ENOMEM; #ifdef ENABLE_KDBUS - p = strjoin("kernel:path=/dev/kdbus/ns/machine-", e, "/0-system/bus;x-container:machine=", e, NULL); + p = strjoin("x-container-kernel:machine=", e, ";x-container-unix:machine=", e, NULL); #else - p = strjoin("x-container:machine=", e, NULL); + p = strjoin("x-container-unix:machine=", e, NULL); #endif if (!p) return -ENOMEM; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 7bb7086056..f5485b3d42 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -398,8 +398,8 @@ static int terminate_machine(sd_bus *bus, char **args, unsigned n) { } static int openpt_in_namespace(pid_t pid, int flags) { + _cleanup_close_pipe_ int pair[2] = { -1, -1 }; _cleanup_close_ int nsfd = -1, rootfd = -1; - _cleanup_close_pipe_ int sock[2] = { -1, -1 }; union { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(int))]; @@ -410,23 +410,14 @@ static int openpt_in_namespace(pid_t pid, int flags) { }; struct cmsghdr *cmsg; int master = -1, r; - char *ns, *root; pid_t child; siginfo_t si; - ns = procfs_file_alloca(pid, "ns/mnt"); - - nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (nsfd < 0) - return -errno; - - root = procfs_file_alloca(pid, "root"); - - rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (rootfd < 0) - return -errno; + r = namespace_open(pid, &nsfd, &rootfd); + if (r < 0) + return r; - if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0) + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) return -errno; child = fork(); @@ -434,19 +425,13 @@ static int openpt_in_namespace(pid_t pid, int flags) { return -errno; if (child == 0) { - close_nointr_nofail(sock[0]); - sock[0] = -1; + close_nointr_nofail(pair[0]); + pair[0] = -1; - r = setns(nsfd, CLONE_NEWNS); + r = namespace_enter(nsfd, rootfd); if (r < 0) _exit(EXIT_FAILURE); - if (fchdir(rootfd) < 0) - _exit(EXIT_FAILURE); - - if (chroot(".") < 0) - _exit(EXIT_FAILURE); - master = posix_openpt(flags); if (master < 0) _exit(EXIT_FAILURE); @@ -459,18 +444,16 @@ static int openpt_in_namespace(pid_t pid, int flags) { mh.msg_controllen = cmsg->cmsg_len; - r = sendmsg(sock[1], &mh, MSG_NOSIGNAL); - close_nointr_nofail(master); - if (r < 0) + if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0) _exit(EXIT_FAILURE); _exit(EXIT_SUCCESS); } - close_nointr_nofail(sock[1]); - sock[1] = -1; + close_nointr_nofail(pair[1]); + pair[1] = -1; - if (recvmsg(sock[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0) + if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0) return -errno; for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) diff --git a/src/shared/util.c b/src/shared/util.c index 1c35edfbb1..66276aa54e 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -5951,3 +5951,76 @@ int proc_cmdline(char **ret) { return 1; } + +int container_get_leader(const char *machine, pid_t *pid) { + _cleanup_free_ char *s = NULL, *class = NULL; + const char *p; + pid_t leader; + int r; + + assert(machine); + assert(pid); + + p = strappenda("/run/systemd/machines/", machine); + r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); + if (r == -ENOENT) + return -EHOSTDOWN; + if (r < 0) + return r; + if (!s) + return -EIO; + + if (!streq_ptr(class, "container")) + return -EIO; + + r = parse_pid(s, &leader); + if (r < 0) + return r; + if (leader <= 1) + return -EIO; + + *pid = leader; + return 0; +} + +int namespace_open(pid_t pid, int *namespace_fd, int *root_fd) { + _cleanup_close_ int nsfd = -1; + const char *ns, *root; + int rfd; + + assert(pid >= 0); + assert(namespace_fd); + assert(root_fd); + + ns = procfs_file_alloca(pid, "ns/mnt"); + nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (nsfd < 0) + return -errno; + + root = procfs_file_alloca(pid, "root"); + rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (rfd < 0) + return -errno; + + *namespace_fd = nsfd; + *root_fd = rfd; + nsfd = -1; + + return 0; +} + +int namespace_enter(int namespace_fd, int root_fd) { + assert(namespace_fd >= 0); + assert(root_fd >= 0); + + if (setns(namespace_fd, CLONE_NEWNS) < 0) + return -errno; + + if (fchdir(root_fd) < 0) + return -errno; + + if (chroot(".") < 0) + return -errno; + + return 0; +} diff --git a/src/shared/util.h b/src/shared/util.h index 504f63a28f..d5fa81c6a5 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -789,3 +789,8 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, } int proc_cmdline(char **ret); + +int container_get_leader(const char *machine, pid_t *pid); + +int namespace_open(pid_t pid, int *namespace_fd, int *root_fd); +int namespace_enter(int namespace_fd, int root_fd); |