From fbe550738d03b178bb004a1390e74115e904118a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Aug 2015 21:05:09 +0200 Subject: machined: introduce pseudo-machine ".host" refererring to the host system Some of the operations machined/machinectl implement are also very useful when applied to the host system (such as machinectl login, machinectl shell or machinectl status), hence introduce a pseudo-machine by the name of ".host" in machined that refers to the host system, and may be used top execute operations on the host system with. This copies the pseudo-image ".host" machined already implements for image related commands. (This commit also adds a PK privilege for opening a PTY in a container, which was previously not accessible for non-root.) --- src/machine/machine-dbus.c | 416 ++++++++++++++++++++++++++------------------- 1 file changed, 244 insertions(+), 172 deletions(-) (limited to 'src/machine/machine-dbus.c') diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index ad3dd8facf..b89bb2cba1 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -186,141 +186,179 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_free_ char *us = NULL, *them = NULL; - _cleanup_close_ int netns_fd = -1; Machine *m = userdata; - const char *p; - siginfo_t si; - pid_t child; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); - - r = readlink_malloc("/proc/self/ns/net", &us); - if (r < 0) - return r; - - p = procfs_file_alloca(m->leader, "ns/net"); - r = readlink_malloc(p, &them); + r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; - if (streq(us, them)) - return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); - - r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL); + r = sd_bus_message_open_container(reply, 'a', "(iay)"); if (r < 0) return r; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + switch (m->class) { - if (child == 0) { + case MACHINE_HOST: { _cleanup_free_ struct local_address *addresses = NULL; struct local_address *a; - int i, n; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(-1, -1, netns_fd, -1, -1); - if (r < 0) - _exit(EXIT_FAILURE); + int n, i; n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); if (n < 0) - _exit(EXIT_FAILURE); + return n; for (a = addresses, i = 0; i < n; a++, i++) { - struct iovec iov[2] = { - { .iov_base = &a->family, .iov_len = sizeof(a->family) }, - { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, - }; - r = writev(pair[1], iov, 2); + r = sd_bus_message_open_container(reply, 'r', "iay"); if (r < 0) - _exit(EXIT_FAILURE); - } - - pair[1] = safe_close(pair[1]); + return r; - _exit(EXIT_SUCCESS); - } - - pair[1] = safe_close(pair[1]); + r = sd_bus_message_append(reply, "i", addresses[i].family); + if (r < 0) + return r; - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; + r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family)); + if (r < 0) + return r; - r = sd_bus_message_open_container(reply, 'a', "(iay)"); - if (r < 0) - return r; + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } - for (;;) { - int family; - ssize_t n; - union in_addr_union in_addr; - struct iovec iov[2]; - struct msghdr mh = { - .msg_iov = iov, - .msg_iovlen = 2, - }; + break; + } - iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; - iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; + case MACHINE_CONTAINER: { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_free_ char *us = NULL, *them = NULL; + _cleanup_close_ int netns_fd = -1; + const char *p; + siginfo_t si; + pid_t child; - n = recvmsg(pair[0], &mh, 0); - if (n < 0) - return -errno; - if ((size_t) n < sizeof(family)) - break; + r = readlink_malloc("/proc/self/ns/net", &us); + if (r < 0) + return r; - r = sd_bus_message_open_container(reply, 'r', "iay"); + p = procfs_file_alloca(m->leader, "ns/net"); + r = readlink_malloc(p, &them); if (r < 0) return r; - r = sd_bus_message_append(reply, "i", family); + if (streq(us, them)) + return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); + + r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL); if (r < 0) return r; - switch (family) { + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_free_ struct local_address *addresses = NULL; + struct local_address *a; + int i, n; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, -1, netns_fd, -1, -1); + if (r < 0) + _exit(EXIT_FAILURE); - case AF_INET: - if (n != sizeof(struct in_addr) + sizeof(family)) - return -EIO; + n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); + if (n < 0) + _exit(EXIT_FAILURE); - r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); - break; + for (a = addresses, i = 0; i < n; a++, i++) { + struct iovec iov[2] = { + { .iov_base = &a->family, .iov_len = sizeof(a->family) }, + { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, + }; - case AF_INET6: - if (n != sizeof(struct in6_addr) + sizeof(family)) - return -EIO; + r = writev(pair[1], iov, 2); + if (r < 0) + _exit(EXIT_FAILURE); + } - r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); - break; + pair[1] = safe_close(pair[1]); + + _exit(EXIT_SUCCESS); } - if (r < 0) - return r; - r = sd_bus_message_close_container(reply); + pair[1] = safe_close(pair[1]); + + for (;;) { + int family; + ssize_t n; + union in_addr_union in_addr; + struct iovec iov[2]; + struct msghdr mh = { + .msg_iov = iov, + .msg_iovlen = 2, + }; + + iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; + iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; + + n = recvmsg(pair[0], &mh, 0); + if (n < 0) + return -errno; + if ((size_t) n < sizeof(family)) + break; + + r = sd_bus_message_open_container(reply, 'r', "iay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "i", family); + if (r < 0) + return r; + + switch (family) { + + case AF_INET: + if (n != sizeof(struct in_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); + break; + + case AF_INET6: + if (n != sizeof(struct in6_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); + break; + } + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } + + r = wait_for_terminate(child, &si); if (r < 0) - return r; + return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + break; } - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); + } r = sd_bus_message_close_container(reply); if (r < 0) @@ -331,73 +369,88 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_close_ int mntns_fd = -1, root_fd = -1; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_strv_free_ char **l = NULL; - _cleanup_fclose_ FILE *f = NULL; Machine *m = userdata; char **k, **v; - siginfo_t si; - pid_t child; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); + switch (m->class) { - r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); - if (r < 0) - return r; + case MACHINE_HOST: + r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l); + if (r < 0) + return r; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; + break; - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + case MACHINE_CONTAINER: { + _cleanup_close_ int mntns_fd = -1, root_fd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_fclose_ FILE *f = NULL; + siginfo_t si; + pid_t child; - if (child == 0) { - _cleanup_close_ int fd = -1; + r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); + if (r < 0) + return r; - pair[0] = safe_close(pair[0]); + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; - r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); - if (r < 0) - _exit(EXIT_FAILURE); + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_close_ int fd = -1; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) { + fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) + _exit(EXIT_FAILURE); + } - fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) { - fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) + r = copy_bytes(fd, pair[1], (off_t) -1, false); + if (r < 0) _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); } - r = copy_bytes(fd, pair[1], (off_t) -1, false); - if (r < 0) - _exit(EXIT_FAILURE); + pair[1] = safe_close(pair[1]); - _exit(EXIT_SUCCESS); - } + f = fdopen(pair[0], "re"); + if (!f) + return -errno; - pair[1] = safe_close(pair[1]); + pair[0] = -1; - f = fdopen(pair[0], "re"); - if (!f) - return -errno; + r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); + if (r < 0) + return r; - pair[0] = -1; + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); - r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); - if (r < 0) - return r; + break; + } - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); + } r = sd_bus_message_new_method_return(message, &reply); if (r < 0) @@ -430,10 +483,20 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening pseudo TTYs is only supported on container machines."); + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.open-pty", + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; @@ -453,31 +516,45 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ } static int container_bus_new(Machine *m, sd_bus **ret) { - _cleanup_bus_unref_ sd_bus *bus = NULL; - char *address; int r; assert(m); assert(ret); - r = sd_bus_new(&bus); - if (r < 0) - return r; + switch (m->class) { - if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0) - return -ENOMEM; + case MACHINE_HOST: + *ret = NULL; + break; - bus->address = address; - bus->bus_client = true; - bus->trusted = false; - bus->is_system = true; + case MACHINE_CONTAINER: { + _cleanup_bus_unref_ sd_bus *bus = NULL; + char *address; - r = sd_bus_start(bus); - if (r < 0) - return r; + r = sd_bus_new(&bus); + if (r < 0) + return r; - *ret = bus; - bus = NULL; + if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0) + return -ENOMEM; + + bus->address = address; + bus->bus_client = true; + bus->trusted = false; + bus->is_system = true; + + r = sd_bus_start(bus); + if (r < 0) + return r; + + *ret = bus; + bus = NULL; + break; + } + + default: + return -EOPNOTSUPP; + } return 0; } @@ -485,8 +562,9 @@ static int container_bus_new(Machine *m, sd_bus **ret) { int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_free_ char *pty_name = NULL; - _cleanup_bus_unref_ sd_bus *container_bus = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL; _cleanup_close_ int master = -1; + sd_bus *container_bus = NULL; Machine *m = userdata; const char *p, *getty; int r; @@ -494,9 +572,6 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines."); - r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, @@ -510,7 +585,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (r == 0) return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; @@ -525,10 +600,12 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (unlockpt(master) < 0) return -errno; - r = container_bus_new(m, &container_bus); + r = container_bus_new(m, &allocated_bus); if (r < 0) return r; + container_bus = allocated_bus ?: m->manager->bus; + getty = strjoina("container-getty@", p, ".service"); r = sd_bus_call_method( @@ -542,8 +619,6 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; - container_bus = sd_bus_unref(container_bus); - r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; @@ -558,7 +633,8 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *tm = NULL; _cleanup_free_ char *pty_name = NULL; - _cleanup_bus_unref_ sd_bus *container_bus = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL; + sd_bus *container_bus = NULL; _cleanup_close_ int master = -1; _cleanup_strv_free_ char **env = NULL, **args = NULL; Machine *m = userdata; @@ -597,9 +673,6 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (!strv_env_is_valid(env)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening shells is only supported on container machines."); - r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, @@ -613,7 +686,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (r == 0) return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; @@ -631,10 +704,12 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (unlockpt(master) < 0) return -errno; - r = container_bus_new(m, &container_bus); + r = container_bus_new(m, &allocated_bus); if (r < 0) return r; + container_bus = allocated_bus ?: m->manager->bus; + r = sd_bus_message_new_method_call( container_bus, &tm, @@ -645,9 +720,8 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; - unit = strjoina("container-shell@", p, ".service", NULL); - /* Name and mode */ + unit = strjoina("container-shell@", p, ".service", NULL); r = sd_bus_message_append(tm, "ss", unit, "fail"); if (r < 0) return r; @@ -768,8 +842,6 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; - container_bus = sd_bus_unref(container_bus); - r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; @@ -1207,7 +1279,7 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0), + SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), -- cgit v1.2.3-54-g00ecf