From 49af9e1368571f4e423cde0fd45ee284451434d1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2015 13:20:58 +0200 Subject: machined: add new OpenShell() bus call This new bus call opens an interactive shell in a container. It works like the existing OpenLogin() call, but does not involve getty, and instead opens an arbitrary command line. This is similar to "systemd-run -t -M" but is controlled by a specific PolicyKit privilege. --- src/machine/machine-dbus.c | 280 +++++++++++++++++++++++-- src/machine/machine-dbus.h | 1 + src/machine/machined-dbus.c | 22 ++ src/machine/org.freedesktop.machine1.conf | 8 + src/machine/org.freedesktop.machine1.policy.in | 10 + 5 files changed, 302 insertions(+), 19 deletions(-) (limited to 'src/machine') diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index b62a9bd813..ad3dd8facf 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -44,6 +44,7 @@ #include "machine-dbus.h" #include "formats-util.h" #include "process-util.h" +#include "env-util.h" static int property_get_id( sd_bus *bus, @@ -451,14 +452,43 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_send(NULL, reply, NULL); } +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; + + 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; + + return 0; +} + 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, *getty = NULL; + _cleanup_free_ char *pty_name = NULL; _cleanup_bus_unref_ sd_bus *container_bus = NULL; _cleanup_close_ int master = -1; Machine *m = userdata; - const char *p; - char *address; + const char *p, *getty; int r; assert(message); @@ -495,35 +525,246 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (unlockpt(master) < 0) return -errno; - r = sd_bus_new(&container_bus); + r = container_bus_new(m, &container_bus); if (r < 0) return r; -# define ADDRESS_FMT "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI - if (asprintf(&address, ADDRESS_FMT, m->leader) < 0) - return log_oom(); + getty = strjoina("container-getty@", p, ".service"); - container_bus->address = address; - container_bus->bus_client = true; - container_bus->trusted = false; - container_bus->is_system = true; + r = sd_bus_call_method( + container_bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + error, NULL, + "ss", getty, "replace"); + if (r < 0) + return r; + + container_bus = sd_bus_unref(container_bus); - r = sd_bus_start(container_bus); + r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; - getty = strjoin("container-getty@", p, ".service", NULL); - if (!getty) - return log_oom(); + r = sd_bus_message_append(reply, "hs", master, pty_name); + if (r < 0) + return r; - r = sd_bus_call_method( + return sd_bus_send(NULL, reply, NULL); +} + +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_close_ int master = -1; + _cleanup_strv_free_ char **env = NULL, **args = NULL; + Machine *m = userdata; + const char *p, *unit, *user, *path, *description, *utmp_id; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ss", &user, &path); + if (r < 0) + return r; + if (isempty(user)) + user = NULL; + if (isempty(path)) + path = "/bin/sh"; + if (!path_is_absolute(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path); + + r = sd_bus_message_read_strv(message, &args); + if (r < 0) + return r; + if (strv_isempty(args)) { + args = strv_free(args); + + args = strv_new(path, NULL); + if (!args) + return -ENOMEM; + + args[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */ + } + + r = sd_bus_message_read_strv(message, &env); + if (r < 0) + return r; + 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, + "org.freedesktop.machine1.shell", + 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); + if (master < 0) + return master; + + r = ptsname_malloc(master, &pty_name); + if (r < 0) + return r; + + p = path_startswith(pty_name, "/dev/pts/"); + if (!p) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name); + + utmp_id = path_startswith(pty_name, "/dev/"); + assert(utmp_id); + + if (unlockpt(master) < 0) + return -errno; + + r = container_bus_new(m, &container_bus); + if (r < 0) + return r; + + r = sd_bus_message_new_method_call( container_bus, + &tm, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "StartUnit", - error, NULL, - "ss", getty, "replace"); + "StartTransientUnit"); + if (r < 0) + return r; + + unit = strjoina("container-shell@", p, ".service", NULL); + + /* Name and mode */ + r = sd_bus_message_append(tm, "ss", unit, "fail"); + if (r < 0) + return r; + + /* Properties */ + r = sd_bus_message_open_container(tm, 'a', "(sv)"); + if (r < 0) + return r; + + description = strjoina("Shell for User ", isempty(user) ? "root" : user); + r = sd_bus_message_append(tm, + "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)", + "Description", "s", description, + "StandardInput", "s", "tty", + "StandardOutput", "s", "tty", + "StandardError", "s", "tty", + "TTYPath", "s", pty_name, + "SendSIGHUP", "b", true, + "IgnoreSIGPIPE", "b", false, + "KillMode", "s", "mixed", + "TTYVHangup", "b", true, + "TTYReset", "b", true, + "UtmpIdentifier", "s", utmp_id, + "UtmpMode", "s", "user", + "PAMName", "s", "login"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "(sv)", "User", "s", isempty(user) ? "root" : user); + if (r < 0) + return r; + + if (!strv_isempty(env)) { + r = sd_bus_message_open_container(tm, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", "Environment"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'v', "as"); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(tm, env); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + } + + /* Exec container */ + r = sd_bus_message_open_container(tm, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", "ExecStart"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'v', "a(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'a', "(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'r', "sasb"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", path); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(tm, args); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "b", true); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + /* Auxiliary units */ + r = sd_bus_message_append(tm, "a(sa(sv))", 0); + if (r < 0) + return r; + + r = sd_bus_call(container_bus, tm, 0, error, NULL); if (r < 0) return r; @@ -968,6 +1209,7 @@ const sd_bus_vtable machine_vtable[] = { 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("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), SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index d309131860..38b46ad936 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -35,6 +35,7 @@ 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); int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 08a7f58ef5..da3ab26e89 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -637,6 +637,27 @@ static int method_open_machine_login(sd_bus_message *message, void *userdata, sd return bus_machine_method_open_login(message, machine, error); } +static int method_open_machine_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_shell(message, machine, error); +} + static int method_bind_mount_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; Machine *machine; @@ -1085,6 +1106,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0), SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenMachineShell", "sssasas", "hs", method_open_machine_shell, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf index d58f01507b..9d40b90151 100644 --- a/src/machine/org.freedesktop.machine1.conf +++ b/src/machine/org.freedesktop.machine1.conf @@ -68,6 +68,10 @@ send_interface="org.freedesktop.machine1.Manager" send_member="OpenMachineLogin"/> + + @@ -140,6 +144,10 @@ send_interface="org.freedesktop.machine1.Machine" send_member="OpenLogin"/> + + diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in index 02714e83ae..b3b2fa29c1 100644 --- a/src/machine/org.freedesktop.machine1.policy.in +++ b/src/machine/org.freedesktop.machine1.policy.in @@ -26,6 +26,16 @@ + + <_description>Acquire a shell in a local container + <_message>Authentication is required to acquire a shell in a local container. + + auth_admin + auth_admin + auth_admin_keep + + + <_description>Manage local virtual machines and containers <_message>Authentication is required to manage local virtual machines and containers. -- cgit v1.2.3-54-g00ecf