]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
run: allow connecting to capsule instances with --capsule=/-C
authorLennart Poettering <lennart@poettering.net>
Thu, 26 Oct 2023 07:19:04 +0000 (09:19 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 14 Mar 2024 10:34:04 +0000 (11:34 +0100)
src/run/run.c

index c0c65b2ea1fa12eb5faf8129a7a3f550a9c57003..e4b89d84752cdb7d8bbd3228409d94bf170a1ec2 100644 (file)
 #include "bus-unit-util.h"
 #include "bus-wait-for-jobs.h"
 #include "calendarspec.h"
+#include "capsule-util.h"
+#include "chase.h"
 #include "env-util.h"
 #include "escape.h"
 #include "exit-status.h"
 #include "fd-util.h"
 #include "format-util.h"
+#include "fs-util.h"
 #include "hostname-util.h"
 #include "main-func.h"
 #include "parse-argument.h"
@@ -35,6 +38,7 @@
 #include "special.h"
 #include "strv.h"
 #include "terminal-util.h"
+#include "uid-classification.h"
 #include "unit-def.h"
 #include "unit-name.h"
 #include "user-util.h"
@@ -265,6 +269,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "version",            no_argument,       NULL, ARG_VERSION            },
                 { "user",               no_argument,       NULL, ARG_USER               },
                 { "system",             no_argument,       NULL, ARG_SYSTEM             },
+                { "capsule",            required_argument, NULL, 'C'                    },
                 { "scope",              no_argument,       NULL, ARG_SCOPE              },
                 { "unit",               required_argument, NULL, 'u'                    },
                 { "description",        required_argument, NULL, ARG_DESCRIPTION        },
@@ -317,7 +322,7 @@ static int parse_argv(int argc, char *argv[]) {
         /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
          * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
         optind = 0;
-        while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tPqGdSu:", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "+hrC:H:M:E:p:tPqGdSu:", options, NULL)) >= 0)
 
                 switch (c) {
 
@@ -339,6 +344,18 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
                         break;
 
+                case 'C':
+                        r = capsule_name_is_valid(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Unable to validate capsule name '%s': %m", optarg);
+                        if (r == 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid capsule name: %s", optarg);
+
+                        arg_host = optarg;
+                        arg_transport = BUS_TRANSPORT_CAPSULE;
+                        arg_runtime_scope = RUNTIME_SCOPE_USER;
+                        break;
+
                 case ARG_SCOPE:
                         arg_scope = true;
                         break;
@@ -1603,6 +1620,28 @@ static void set_window_title(PTYForward *f) {
         (void) pty_forward_set_title_prefix(f, dot);
 }
 
+static int chown_to_capsule(const char *path, const char *capsule) {
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        assert(path);
+        assert(capsule);
+
+        p = path_join("/run/capsules/", capsule);
+        if (!p)
+                return -ENOMEM;
+
+        struct stat st;
+        r = chase_and_stat(p, /* root= */ NULL, CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS, /* ret_path= */ NULL, &st);
+        if (r < 0)
+                return r;
+
+        if (uid_is_system(st.st_uid) || gid_is_system(st.st_gid)) /* paranoid safety check */
+                return -EPERM;
+
+        return chmod_and_chown(path, 0600, st.st_uid, st.st_gid);
+}
+
 static int start_transient_service(sd_bus *bus) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -1615,7 +1654,7 @@ static int start_transient_service(sd_bus *bus) {
 
         if (arg_stdio == ARG_STDIO_PTY) {
 
-                if (arg_transport == BUS_TRANSPORT_LOCAL) {
+                if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)) {
                         master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
                         if (master < 0)
                                 return log_error_errno(errno, "Failed to acquire pseudo tty: %m");
@@ -1624,6 +1663,14 @@ static int start_transient_service(sd_bus *bus) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to determine tty name: %m");
 
+                        if (arg_transport == BUS_TRANSPORT_CAPSULE) {
+                                /* If we are in capsule mode, we must give the capsule UID/GID access to the PTY we just allocated first. */
+
+                                r = chown_to_capsule(pty_path, arg_host);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to chown tty to capsule UID/GID: %m");
+                        }
+
                         if (unlockpt(master) < 0)
                                 return log_error_errno(errno, "Failed to unlock tty: %m");
 
@@ -2311,7 +2358,7 @@ static int run(int argc, char* argv[]) {
          * limited direct connection */
         if (arg_wait ||
             arg_stdio != ARG_STDIO_NONE ||
-            (arg_runtime_scope == RUNTIME_SCOPE_USER && arg_transport != BUS_TRANSPORT_LOCAL))
+            (arg_runtime_scope == RUNTIME_SCOPE_USER && !IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)))
                 r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
         else
                 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus);