chiark / gitweb /
service: hook up custom endpoint logic
authorDaniel Mack <zonque@gmail.com>
Fri, 22 Aug 2014 17:02:03 +0000 (19:02 +0200)
committerDaniel Mack <daniel@zonque.org>
Mon, 8 Sep 2014 12:15:02 +0000 (14:15 +0200)
If BusPolicy= was passed, the parser function will have created
an ExecContext->bus_endpoint object, along with policy information.

In that case, create a kdbus endpoint, and pass its path name to the
namespace logic, to it will be mounted over the actual 'bus' node.

At endpoint creation time, no policy is updloaded. That is done after
fork(), through a separate call. This is necessary because we don't
know the real uid of the process earlier than that.

src/core/execute.c
src/core/execute.h
src/core/service.c
src/core/service.h

index 96cabe6d99c7bb8d5bf95910c18962550cb4fbcf..2b16b36c19b187853061f90c0918506086c94716 100644 (file)
@@ -83,6 +83,7 @@
 #include "af-list.h"
 #include "mkdir.h"
 #include "apparmor-util.h"
+#include "bus-kernel.h"
 
 #ifdef HAVE_SECCOMP
 #include "seccomp-util.h"
@@ -1236,7 +1237,7 @@ static int exec_child(ExecCommand *command,
         _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
         const char *username = NULL, *home = NULL, *shell = NULL;
         unsigned n_dont_close = 0;
-        int dont_close[n_fds + 3];
+        int dont_close[n_fds + 4];
         uid_t uid = (uid_t) -1;
         gid_t gid = (gid_t) -1;
         int i, err;
@@ -1279,6 +1280,8 @@ static int exec_child(ExecCommand *command,
                 memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds);
                 n_dont_close += n_fds;
         }
+        if (params->bus_endpoint_fd >= 0)
+                dont_close[n_dont_close++] = params->bus_endpoint_fd;
         if (runtime) {
                 if (runtime->netns_storage_socket[0] >= 0)
                         dont_close[n_dont_close++] = runtime->netns_storage_socket[0];
@@ -1428,6 +1431,18 @@ static int exec_child(ExecCommand *command,
                 }
         }
 
+#ifdef ENABLE_KDBUS
+        if (params->bus_endpoint_fd >= 0 && context->bus_endpoint) {
+                uid_t ep_uid = (uid == (uid_t) -1) ? 0 : uid;
+
+                err = bus_kernel_set_endpoint_policy(params->bus_endpoint_fd, ep_uid, context->bus_endpoint);
+                if (err < 0) {
+                        *error = EXIT_BUS_ENDPOINT;
+                        return err;
+                }
+        }
+#endif
+
 #ifdef HAVE_PAM
         if (params->cgroup_path && context->user && context->pam_name) {
                 err = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0644, uid, gid);
@@ -1498,6 +1513,7 @@ static int exec_child(ExecCommand *command,
             !strv_isempty(context->inaccessible_dirs) ||
             context->mount_flags != 0 ||
             (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir)) ||
+            params->bus_endpoint_path ||
             context->private_devices ||
             context->protect_system != PROTECT_SYSTEM_NO ||
             context->protect_home != PROTECT_HOME_NO) {
@@ -1523,7 +1539,7 @@ static int exec_child(ExecCommand *command,
                                 context->inaccessible_dirs,
                                 tmp,
                                 var,
-                                NULL,
+                                params->bus_endpoint_path,
                                 context->private_devices,
                                 context->protect_home,
                                 context->protect_system,
@@ -1564,7 +1580,9 @@ static int exec_child(ExecCommand *command,
         /* We repeat the fd closing here, to make sure that
          * nothing is leaked from the PAM modules. Note that
          * we are more aggressive this time since socket_fd
-         * and the netns fds we don#t need anymore. */
+         * and the netns fds we don't need anymore. The custom
+         * endpoint fd was needed to upload the policy and can
+         * now be closed as well. */
         err = close_all_fds(fds, n_fds);
         if (err >= 0)
                 err = shift_fds(fds, n_fds);
index e3cebfd72cac165d7e0f1b0453b0473fe147fe6f..9c1f249cd4a59e5ddb655ae85e3f4f2945b08cee 100644 (file)
@@ -210,6 +210,8 @@ struct ExecParameters {
         const char *unit_id;
         usec_t watchdog_usec;
         int *idle_pipe;
+        char *bus_endpoint_path;
+        int bus_endpoint_fd;
 };
 
 int exec_spawn(ExecCommand *command,
index f3775f24c40e8f55c6ef089f7e8168918643c40c..3f6595c5c820fd184a84e13ff4af4eebaec913d9 100644 (file)
@@ -45,6 +45,7 @@
 #include "fileio.h"
 #include "bus-error.h"
 #include "bus-util.h"
+#include "bus-kernel.h"
 
 static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
         [SERVICE_DEAD] = UNIT_INACTIVE,
@@ -102,6 +103,7 @@ static void service_init(Unit *u) {
         s->restart_usec = u->manager->default_restart_usec;
         s->type = _SERVICE_TYPE_INVALID;
         s->socket_fd = -1;
+        s->bus_endpoint_fd = -1;
         s->guess_main_pid = true;
 
         RATELIMIT_INIT(s->start_limit, u->manager->default_start_limit_interval, u->manager->default_start_limit_burst);
@@ -273,6 +275,7 @@ static void service_done(Unit *u) {
                 s->bus_name = NULL;
         }
 
+        s->bus_endpoint_fd = safe_close(s->bus_endpoint_fd);
         service_close_socket_fd(s);
         service_connection_unref(s);
 
@@ -889,6 +892,7 @@ static int service_spawn(
         int *fds = NULL;
         _cleanup_free_ int *fdsbuf = NULL;
         unsigned n_fds = 0, n_env = 0;
+        _cleanup_free_ char *bus_endpoint_path = NULL;
         _cleanup_strv_free_ char
                 **argv = NULL, **final_env = NULL, **our_env = NULL;
         const char *path;
@@ -896,6 +900,7 @@ static int service_spawn(
                 .apply_permissions = apply_permissions,
                 .apply_chroot      = apply_chroot,
                 .apply_tty_stdin   = apply_tty_stdin,
+                .bus_endpoint_fd   = -1,
         };
 
         assert(s);
@@ -972,6 +977,20 @@ static int service_spawn(
         } else
                 path = UNIT(s)->cgroup_path;
 
+#ifdef ENABLE_KDBUS
+        if (s->exec_context.bus_endpoint) {
+                r = bus_kernel_create_endpoint(UNIT(s)->manager->running_as == SYSTEMD_SYSTEM ? "system" : "user",
+                                               UNIT(s)->id, &bus_endpoint_path);
+                if (r < 0)
+                        goto fail;
+
+                /* Pass the fd to the exec_params so that the child process can upload the policy.
+                 * Keep a reference to the fd in the service, so the endpoint is kept alive as long
+                 * as the service is running. */
+                exec_params.bus_endpoint_fd = s->bus_endpoint_fd = r;
+        }
+#endif
+
         exec_params.argv = argv;
         exec_params.fds = fds;
         exec_params.n_fds = n_fds;
@@ -982,6 +1001,7 @@ static int service_spawn(
         exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager);
         exec_params.unit_id = UNIT(s)->id;
         exec_params.watchdog_usec = s->watchdog_usec;
+        exec_params.bus_endpoint_path = bus_endpoint_path;
         if (s->type == SERVICE_IDLE)
                 exec_params.idle_pipe = UNIT(s)->manager->idle_pipe;
 
@@ -1770,6 +1790,15 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
                 unit_serialize_item_format(u, f, "socket-fd", "%i", copy);
         }
 
+        if (s->bus_endpoint_fd >= 0) {
+                int copy;
+
+                if ((copy = fdset_put_dup(fds, s->bus_endpoint_fd)) < 0)
+                        return copy;
+
+                unit_serialize_item_format(u, f, "endpoint-fd", "%i", copy);
+        }
+
         if (s->main_exec_status.pid > 0) {
                 unit_serialize_item_format(u, f, "main-exec-status-pid", PID_FMT,
                                            s->main_exec_status.pid);
@@ -1879,10 +1908,18 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                 if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
                         log_debug_unit(u->id, "Failed to parse socket-fd value %s", value);
                 else {
-
                         asynchronous_close(s->socket_fd);
                         s->socket_fd = fdset_remove(fds, fd);
                 }
+        } else if (streq(key, "endpoint-fd")) {
+                int fd;
+
+                if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+                        log_debug_unit(u->id, "Failed to parse endpoint-fd value %s", value);
+                else {
+                        safe_close(s->bus_endpoint_fd);
+                        s->bus_endpoint_fd = fdset_remove(fds, fd);
+                }
         } else if (streq(key, "main-exec-status-pid")) {
                 pid_t pid;
 
index 5bcfd143395afaae093f528f6de09bb060620af4..ad0b3b381e4d892f9322180debb5166394e327fa 100644 (file)
@@ -162,6 +162,8 @@ struct Service {
         pid_t main_pid, control_pid;
         int socket_fd;
 
+        int bus_endpoint_fd;
+
         bool permissions_start_only;
         bool root_directory_start_only;
         bool remain_after_exit;