]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pid1: add support for encrypted credentials
authorLennart Poettering <lennart@poettering.net>
Mon, 21 Jun 2021 12:19:07 +0000 (14:19 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 8 Jul 2021 07:30:56 +0000 (09:30 +0200)
man/org.freedesktop.systemd1.xml
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.in
src/core/load-fragment.c
src/shared/bus-unit-util.c
test/fuzz/fuzz-unit-file/directives.mount
test/fuzz/fuzz-unit-file/directives.service
test/fuzz/fuzz-unit-file/directives.socket
test/fuzz/fuzz-unit-file/directives.swap

index 74a920273afbbf9b64a818215c6e8592505c0ba1..3a54375400223b6abec49b14ea3186252053ba0a 100644 (file)
@@ -2711,8 +2711,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(say) SetCredential = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(say) SetCredentialEncrypted = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(ss) LoadCredential = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ss) LoadCredentialEncrypted = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly as SupplementaryGroups = ['...', ...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s PAMName = '...';
@@ -3224,8 +3228,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property SetCredential is not documented!-->
 
+    <!--property SetCredentialEncrypted is not documented!-->
+
     <!--property LoadCredential is not documented!-->
 
+    <!--property LoadCredentialEncrypted is not documented!-->
+
     <!--property SupplementaryGroups is not documented!-->
 
     <!--property PAMName is not documented!-->
@@ -3812,8 +3820,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="SetCredentialEncrypted"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="LoadCredential"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@@ -4511,8 +4523,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(say) SetCredential = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(say) SetCredentialEncrypted = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(ss) LoadCredential = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ss) LoadCredentialEncrypted = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly as SupplementaryGroups = ['...', ...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s PAMName = '...';
@@ -5052,8 +5068,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--property SetCredential is not documented!-->
 
+    <!--property SetCredentialEncrypted is not documented!-->
+
     <!--property LoadCredential is not documented!-->
 
+    <!--property LoadCredentialEncrypted is not documented!-->
+
     <!--property SupplementaryGroups is not documented!-->
 
     <!--property PAMName is not documented!-->
@@ -5638,8 +5658,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="SetCredentialEncrypted"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="LoadCredential"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@@ -6234,8 +6258,12 @@ node /org/freedesktop/systemd1/unit/home_2emount {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(say) SetCredential = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(say) SetCredentialEncrypted = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(ss) LoadCredential = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ss) LoadCredentialEncrypted = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly as SupplementaryGroups = ['...', ...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s PAMName = '...';
@@ -6703,8 +6731,12 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <!--property SetCredential is not documented!-->
 
+    <!--property SetCredentialEncrypted is not documented!-->
+
     <!--property LoadCredential is not documented!-->
 
+    <!--property LoadCredentialEncrypted is not documented!-->
+
     <!--property SupplementaryGroups is not documented!-->
 
     <!--property PAMName is not documented!-->
@@ -7207,8 +7239,12 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="SetCredentialEncrypted"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="LoadCredential"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@@ -7924,8 +7960,12 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(say) SetCredential = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(say) SetCredentialEncrypted = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(ss) LoadCredential = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ss) LoadCredentialEncrypted = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly as SupplementaryGroups = ['...', ...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s PAMName = '...';
@@ -8379,8 +8419,12 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <!--property SetCredential is not documented!-->
 
+    <!--property SetCredentialEncrypted is not documented!-->
+
     <!--property LoadCredential is not documented!-->
 
+    <!--property LoadCredentialEncrypted is not documented!-->
+
     <!--property SupplementaryGroups is not documented!-->
 
     <!--property PAMName is not documented!-->
@@ -8869,8 +8913,12 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="SetCredentialEncrypted"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="LoadCredential"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
index 50daef67021e748af9f375bd6fba202e504d1dca..f6783e924ab356e52747f3b44bc12dc599cfdd69 100644 (file)
@@ -820,6 +820,9 @@ static int property_get_set_credential(
 
         HASHMAP_FOREACH(sc, c->set_credentials) {
 
+                if (sc->encrypted != streq(property, "SetCredentialEncrypted"))
+                        continue;
+
                 r = sd_bus_message_open_container(reply, 'r', "say");
                 if (r < 0)
                         return r;
@@ -850,7 +853,7 @@ static int property_get_load_credential(
                 sd_bus_error *error) {
 
         ExecContext *c = userdata;
-        char **i, **j;
+        ExecLoadCredential *lc;
         int r;
 
         assert(bus);
@@ -862,8 +865,12 @@ static int property_get_load_credential(
         if (r < 0)
                 return r;
 
-        STRV_FOREACH_PAIR(i, j, c->load_credentials) {
-                r = sd_bus_message_append(reply, "(ss)", *i, *j);
+        HASHMAP_FOREACH(lc, c->load_credentials) {
+
+                if (lc->encrypted != streq(property, "LoadCredentialEncrypted"))
+                        continue;
+
+                r = sd_bus_message_append(reply, "(ss)", lc->id, lc->path);
                 if (r < 0)
                         return r;
         }
@@ -1144,7 +1151,9 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SetCredential", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("SetCredentialEncrypted", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LoadCredential", "a(ss)", property_get_load_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LoadCredentialEncrypted", "a(ss)", property_get_load_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ReadWritePaths", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1916,7 +1925,7 @@ int bus_exec_context_set_transient_property(
 
                 return 1;
 
-        } else if (streq(name, "SetCredential")) {
+        } else if (STR_IN_SET(name, "SetCredential", "SetCredentialEncrypted")) {
                 bool isempty = true;
 
                 r = sd_bus_message_enter_container(message, 'a', "(say)");
@@ -1964,20 +1973,24 @@ int bus_exec_context_set_transient_property(
                                 if (old) {
                                         free_and_replace(old->data, copy);
                                         old->size = sz;
+                                        old->encrypted = streq(name, "SetCredentialEncrypted");
                                 } else {
                                         _cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL;
 
-                                        sc = new0(ExecSetCredential, 1);
+                                        sc = new(ExecSetCredential, 1);
                                         if (!sc)
                                                 return -ENOMEM;
 
-                                        sc->id = strdup(id);
+                                        *sc = (ExecSetCredential) {
+                                                .id = strdup(id),
+                                                .data = TAKE_PTR(copy),
+                                                .size = sz,
+                                                .encrypted = streq(name, "SetCredentialEncrypted"),
+                                        };
+
                                         if (!sc->id)
                                                 return -ENOMEM;
 
-                                        sc->data = TAKE_PTR(copy);
-                                        sc->size = sz;
-
                                         r = hashmap_ensure_put(&c->set_credentials, &exec_set_credential_hash_ops, sc->id, sc);
                                         if (r < 0)
                                                 return r;
@@ -2008,7 +2021,7 @@ int bus_exec_context_set_transient_property(
 
                 return 1;
 
-        } else if (streq(name, "LoadCredential")) {
+        } else if (STR_IN_SET(name, "LoadCredential", "LoadCredentialEncrypted")) {
                 bool isempty = true;
 
                 r = sd_bus_message_enter_container(message, 'a', "(ss)");
@@ -2033,9 +2046,39 @@ int bus_exec_context_set_transient_property(
                         isempty = false;
 
                         if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-                                r = strv_extend_strv(&c->load_credentials, STRV_MAKE(id, source), /* filter_duplicates = */ false);
-                                if (r < 0)
-                                        return r;
+                                _cleanup_free_ char *copy = NULL;
+                                ExecLoadCredential *old;
+
+                                copy = strdup(source);
+                                if (!copy)
+                                        return -ENOMEM;
+
+                                old = hashmap_get(c->load_credentials, id);
+                                if (old) {
+                                        free_and_replace(old->path, copy);
+                                        old->encrypted = streq(name, "LoadCredentialEncrypted");
+                                } else {
+                                        _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
+
+                                        lc = new(ExecLoadCredential, 1);
+                                        if (!lc)
+                                                return -ENOMEM;
+
+                                        *lc = (ExecLoadCredential) {
+                                                .id = strdup(id),
+                                                .path = TAKE_PTR(copy),
+                                                .encrypted = streq(name, "LoadCredentialEncrypted"),
+                                        };
+
+                                        if (!lc->id)
+                                                return -ENOMEM;
+
+                                        r = hashmap_ensure_put(&c->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
+                                        if (r < 0)
+                                                return r;
+
+                                        TAKE_PTR(lc);
+                                }
 
                                 (void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s:%s", name, id, source);
                         }
@@ -2046,7 +2089,7 @@ int bus_exec_context_set_transient_property(
                         return r;
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags) && isempty) {
-                        c->load_credentials = strv_free(c->load_credentials);
+                        c->load_credentials = hashmap_free(c->load_credentials);
                         (void) unit_write_settingf(u, flags, name, "%s=", name);
                 }
 
index 2a337b55a2a34f12d711896869428d47cbcc73ab..f760d6ae07aefbad8adf2b70e588fdfc447be7a5 100644 (file)
@@ -46,6 +46,7 @@
 #include "cgroup-setup.h"
 #include "chown-recursive.h"
 #include "cpu-set-util.h"
+#include "creds-util.h"
 #include "data-fd-util.h"
 #include "def.h"
 #include "env-file.h"
@@ -1454,7 +1455,7 @@ static bool exec_context_has_credentials(const ExecContext *context) {
         assert(context);
 
         return !hashmap_isempty(context->set_credentials) ||
-                context->load_credentials;
+                !hashmap_isempty(context->load_credentials);
 }
 
 #if HAVE_SECCOMP
@@ -2486,7 +2487,7 @@ static int write_credential(
                 return -errno;
         }
 
-        r = loop_write(fd, data, size, /* do_pool = */ false);
+        r = loop_write(fd, data, size, /* do_poll = */ false);
         if (r < 0)
                 return r;
 
@@ -2519,8 +2520,6 @@ static int write_credential(
         return 0;
 }
 
-#define CREDENTIALS_BYTES_MAX (1024LU * 1024LU) /* Refuse to pass more than 1M, after all this is unswappable memory */
-
 static int acquire_credentials(
                 const ExecContext *context,
                 const ExecParameters *params,
@@ -2529,10 +2528,10 @@ static int acquire_credentials(
                 uid_t uid,
                 bool ownership_ok) {
 
-        uint64_t left = CREDENTIALS_BYTES_MAX;
+        uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX;
         _cleanup_close_ int dfd = -1;
+        ExecLoadCredential *lc;
         ExecSetCredential *sc;
-        char **id, **fn;
         int r;
 
         assert(context);
@@ -2542,39 +2541,23 @@ static int acquire_credentials(
         if (dfd < 0)
                 return -errno;
 
-        /* First we use the literally specified credentials. Note that they might be overridden again below,
-         * and thus act as a "default" if the same credential is specified multiple times */
-        HASHMAP_FOREACH(sc, context->set_credentials) {
-                size_t add;
-
-                add = strlen(sc->id) + sc->size;
-                if (add > left)
-                        return -E2BIG;
-
-                r = write_credential(dfd, sc->id, sc->data, sc->size, uid, ownership_ok);
-                if (r < 0)
-                        return r;
-
-                left -= add;
-        }
-
-        /* Then, load credential off disk (or acquire via AF_UNIX socket) */
-        STRV_FOREACH_PAIR(id, fn, context->load_credentials) {
-                ReadFullFileFlags flags = READ_FULL_FILE_SECURE;
+        /* First, load credentials off disk (or acquire via AF_UNIX socket) */
+        HASHMAP_FOREACH(lc, context->load_credentials) {
+                ReadFullFileFlags flags = READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER;
                 _cleanup_(erase_and_freep) char *data = NULL;
                 _cleanup_free_ char *j = NULL, *bindname = NULL;
                 bool missing_ok = true;
                 const char *source;
                 size_t size, add;
 
-                if (path_is_absolute(*fn)) {
+                if (path_is_absolute(lc->path)) {
                         /* If this is an absolute path, read the data directly from it, and support AF_UNIX sockets */
-                        source = *fn;
+                        source = lc->path;
                         flags |= READ_FULL_FILE_CONNECT_SOCKET;
 
                         /* Pass some minimal info about the unit and the credential name we are looking to acquire
                          * via the source socket address in case we read off an AF_UNIX socket. */
-                        if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, *id) < 0)
+                        if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, lc->id) < 0)
                                 return -ENOMEM;
 
                         missing_ok = false;
@@ -2583,7 +2566,7 @@ static int acquire_credentials(
                         /* If this is a relative path, take it relative to the credentials we received
                          * ourselves. We don't support the AF_UNIX stuff in this mode, since we are operating
                          * on a credential store, i.e. this is guaranteed to be regular files. */
-                        j = path_join(params->received_credentials, *fn);
+                        j = path_join(params->received_credentials, lc->path);
                         if (!j)
                                 return -ENOMEM;
 
@@ -2592,34 +2575,87 @@ static int acquire_credentials(
                         source = NULL;
 
                 if (source)
-                        r = read_full_file_full(AT_FDCWD, source, UINT64_MAX, SIZE_MAX, flags, bindname, &data, &size);
+                        r = read_full_file_full(
+                                        AT_FDCWD, source,
+                                        UINT64_MAX,
+                                        lc->encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX,
+                                        flags | (lc->encrypted ? READ_FULL_FILE_UNBASE64 : 0),
+                                        bindname,
+                                        &data, &size);
                 else
                         r = -ENOENT;
-                if (r == -ENOENT && (missing_ok || faccessat(dfd, *id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)) {
+                if (r == -ENOENT && (missing_ok || hashmap_contains(context->set_credentials, lc->id))) {
                         /* Make a missing inherited credential non-fatal, let's just continue. After all apps
                          * will get clear errors if we don't pass such a missing credential on as they
                          * themselves will get ENOENT when trying to read them, which should not be much
                          * worse than when we handle the error here and make it fatal.
                          *
-                         * Also, if the source file doesn't exist, but we already acquired the key otherwise,
-                         * then don't fail either. */
-                        log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", *fn);
+                         * Also, if the source file doesn't exist, but a fallback is set via SetCredentials=
+                         * we are fine, too. */
+                        log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", lc->path);
                         continue;
                 }
                 if (r < 0)
-                        return log_debug_errno(r, "Failed to read credential '%s': %m", *fn);
+                        return log_debug_errno(r, "Failed to read credential '%s': %m", lc->path);
+
+                if (lc->encrypted) {
+                        _cleanup_free_ void *plaintext = NULL;
+                        size_t plaintext_size = 0;
+
+                        r = decrypt_credential_and_warn(lc->id, now(CLOCK_REALTIME), NULL, data, size, &plaintext, &plaintext_size);
+                        if (r < 0)
+                                return r;
 
-                add = strlen(*id) + size;
+                        free_and_replace(data, plaintext);
+                        size = plaintext_size;
+                }
+
+                add = strlen(lc->id) + size;
                 if (add > left)
                         return -E2BIG;
 
-                r = write_credential(dfd, *id, data, size, uid, ownership_ok);
+                r = write_credential(dfd, lc->id, data, size, uid, ownership_ok);
                 if (r < 0)
                         return r;
 
                 left -= add;
         }
 
+        /* First we use the literally specified credentials. Note that they might be overridden again below,
+         * and thus act as a "default" if the same credential is specified multiple times */
+        HASHMAP_FOREACH(sc, context->set_credentials) {
+                _cleanup_(erase_and_freep) void *plaintext = NULL;
+                const char *data;
+                size_t size, add;
+
+                if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
+                        continue;
+                if (errno != ENOENT)
+                        return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sc->id);
+
+                if (sc->encrypted) {
+                        r = decrypt_credential_and_warn(sc->id, now(CLOCK_REALTIME), NULL, sc->data, sc->size, &plaintext, &size);
+                        if (r < 0)
+                                return r;
+
+                        data = plaintext;
+                } else {
+                        data = sc->data;
+                        size = sc->size;
+                }
+
+                add = strlen(sc->id) + size;
+                if (add > left)
+                        return -E2BIG;
+
+                r = write_credential(dfd, sc->id, data, size, uid, ownership_ok);
+                if (r < 0)
+                        return r;
+
+
+                left -= add;
+        }
+
         if (fchmod(dfd, 0500) < 0) /* Now take away the "w" bit */
                 return -errno;
 
@@ -2715,7 +2751,7 @@ static int setup_credentials_internal(
                         } else if (try == 1) {
                                 _cleanup_free_ char *opts = NULL;
 
-                                if (asprintf(&opts, "mode=0700,nr_inodes=1024,size=%lu", CREDENTIALS_BYTES_MAX) < 0)
+                                if (asprintf(&opts, "mode=0700,nr_inodes=1024,size=%zu", (size_t) CREDENTIALS_TOTAL_SIZE_MAX) < 0)
                                         return -ENOMEM;
 
                                 /* Fall back to "tmpfs" otherwise */
@@ -4928,7 +4964,7 @@ void exec_context_done(ExecContext *c) {
 
         c->log_namespace = mfree(c->log_namespace);
 
-        c->load_credentials = strv_free(c->load_credentials);
+        c->load_credentials = hashmap_free(c->load_credentials);
         c->set_credentials = hashmap_free(c->set_credentials);
 }
 
@@ -6597,7 +6633,17 @@ ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc) {
         return mfree(sc);
 }
 
+ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc) {
+        if (!lc)
+                return NULL;
+
+        free(lc->id);
+        free(lc->path);
+        return mfree(lc);
+}
+
 DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_set_credential_hash_ops, char, string_hash_func, string_compare_func, ExecSetCredential, exec_set_credential_free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_load_credential_hash_ops, char, string_hash_func, string_compare_func, ExecLoadCredential, exec_load_credential_free);
 
 static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
         [EXEC_INPUT_NULL] = "null",
index 4c7a5b874f364ed5dbcfe5f40691f6132a59f5ae..fceb8bb6f79e1893f13d2fe70b518dd43c51824c 100644 (file)
@@ -150,9 +150,16 @@ typedef enum ExecCleanMask {
         _EXEC_CLEAN_MASK_INVALID = -EINVAL,
 } ExecCleanMask;
 
+/* A credential configured with LoadCredential= */
+typedef struct ExecLoadCredential {
+        char *id, *path;
+        bool encrypted;
+} ExecLoadCredential;
+
 /* A credential configured with SetCredential= */
 typedef struct ExecSetCredential {
         char *id;
+        bool encrypted;
         void *data;
         size_t size;
 } ExecSetCredential;
@@ -325,7 +332,7 @@ struct ExecContext {
         usec_t timeout_clean_usec;
 
         Hashmap *set_credentials; /* output id â†’ ExecSetCredential */
-        char **load_credentials; /* pairs of output id, path/input id */
+        Hashmap *load_credentials; /* output id â†’ ExecLoadCredential */
 };
 
 static inline bool exec_context_restrict_namespaces_set(const ExecContext *c) {
@@ -458,7 +465,11 @@ bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c);
 ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc);
 DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSetCredential*, exec_set_credential_free);
 
+ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ExecLoadCredential*, exec_load_credential_free);
+
 extern const struct hash_ops exec_set_credential_hash_ops;
+extern const struct hash_ops exec_load_credential_hash_ops;
 
 const char* exec_output_to_string(ExecOutput i) _const_;
 ExecOutput exec_output_from_string(const char *s) _pure_;
index 42441eab6ef59d4d3d31e182f3c02506cea1887f..96507907f67173e3e4c4f21863e92a821fa61b32 100644 (file)
 {{type}}.ConfigurationDirectoryMode,       config_parse_mode,                           0,                                  offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].mode)
 {{type}}.ConfigurationDirectory,           config_parse_exec_directories,               0,                                  offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].paths)
 {{type}}.SetCredential,                    config_parse_set_credential,                 0,                                  offsetof({{type}}, exec_context)
+{{type}}.SetCredentialEncrypted,           config_parse_set_credential,                 1,                                  offsetof({{type}}, exec_context)
 {{type}}.LoadCredential,                   config_parse_load_credential,                0,                                  offsetof({{type}}, exec_context)
+{{type}}.LoadCredentialEncrypted,          config_parse_load_credential,                1,                                  offsetof({{type}}, exec_context)
 {{type}}.TimeoutCleanSec,                  config_parse_sec,                            0,                                  offsetof({{type}}, exec_context.timeout_clean_usec)
 {% if HAVE_PAM %}
 {{type}}.PAMName,                          config_parse_unit_string_printf,             0,                                  offsetof({{type}}, exec_context.pam_name)
index 8fb3c378ee4e4ac6a1f01fded44e200b64443b87..c72f5c22da79a48093fd7ed5611dd502c5bb2f1c 100644 (file)
@@ -4469,12 +4469,15 @@ int config_parse_set_credential(
                 void *data,
                 void *userdata) {
 
-        _cleanup_free_ char *word = NULL, *k = NULL, *unescaped = NULL;
+        _cleanup_free_ char *word = NULL, *k = NULL;
+        _cleanup_free_ void *d = NULL;
         ExecContext *context = data;
         ExecSetCredential *old;
         Unit *u = userdata;
-        const char *p;
-        int r, l;
+        bool encrypted = ltype;
+        const char *p = rvalue;
+        size_t size;
+        int r;
 
         assert(filename);
         assert(lvalue);
@@ -4487,7 +4490,6 @@ int config_parse_set_credential(
                 return 0;
         }
 
-        p = rvalue;
         r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
         if (r == -ENOMEM)
                 return log_oom();
@@ -4506,33 +4508,51 @@ int config_parse_set_credential(
                 return 0;
         }
 
-        /* We support escape codes here, so that users can insert trailing \n if they like */
-        l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
-        if (l < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, l, "Can't unescape \"%s\", ignoring: %m", p);
-                return 0;
+        if (encrypted) {
+                r = unbase64mem_full(p, SIZE_MAX, true, &d, &size);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r, "Encrypted credential data not valid Base64 data, ignoring.");
+                        return 0;
+                }
+        } else {
+                char *unescaped = NULL;
+                int l;
+
+                /* We support escape codes here, so that users can insert trailing \n if they like */
+                l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
+                if (l < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, l, "Can't unescape \"%s\", ignoring: %m", p);
+                        return 0;
+                }
+
+                d = unescaped;
+                size = l;
         }
 
         old = hashmap_get(context->set_credentials, k);
         if (old) {
-                free_and_replace(old->data, unescaped);
-                old->size = l;
+                free_and_replace(old->data, d);
+                old->size = size;
+                old->encrypted = encrypted;
         } else {
                 _cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL;
 
-                sc = new0(ExecSetCredential, 1);
+                sc = new(ExecSetCredential, 1);
                 if (!sc)
                         return log_oom();
 
-                sc->id = TAKE_PTR(k);
-                sc->data = TAKE_PTR(unescaped);
-                sc->size = l;
+                *sc = (ExecSetCredential) {
+                        .id = TAKE_PTR(k),
+                        .data = TAKE_PTR(d),
+                        .size = size,
+                        .encrypted = encrypted,
+                };
 
                 r = hashmap_ensure_put(&context->set_credentials, &exec_set_credential_hash_ops, sc->id, sc);
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, l,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Duplicated credential value '%s', ignoring assignment: %s", sc->id, rvalue);
                         return 0;
                 }
@@ -4557,6 +4577,8 @@ int config_parse_load_credential(
 
         _cleanup_free_ char *word = NULL, *k = NULL, *q = NULL;
         ExecContext *context = data;
+        ExecLoadCredential *old;
+        bool encrypted = ltype;
         Unit *u = userdata;
         const char *p;
         int r;
@@ -4568,7 +4590,7 @@ int config_parse_load_credential(
 
         if (isempty(rvalue)) {
                 /* Empty assignment resets the list */
-                context->load_credentials = strv_free(context->load_credentials);
+                context->load_credentials = hashmap_free(context->load_credentials);
                 return 0;
         }
 
@@ -4604,14 +4626,39 @@ int config_parse_load_credential(
                         return 0;
                 }
                 if (path_is_absolute(q) ? !path_is_normalized(q) : !credential_name_valid(q)) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r, "Credential source \"%s\" not valid, ignoring.", q);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential source \"%s\" not valid, ignoring.", q);
                         return 0;
                 }
         }
 
-        r = strv_consume_pair(&context->load_credentials, TAKE_PTR(k), TAKE_PTR(q));
-        if (r < 0)
-                return log_oom();
+        old = hashmap_get(context->load_credentials, k);
+        if (old) {
+                free_and_replace(old->path, q);
+                old->encrypted = encrypted;
+        } else {
+                _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
+
+                lc = new(ExecLoadCredential, 1);
+                if (!lc)
+                        return log_oom();
+
+                *lc = (ExecLoadCredential) {
+                        .id = TAKE_PTR(k),
+                        .path = TAKE_PTR(q),
+                        .encrypted = encrypted,
+                };
+
+                r = hashmap_ensure_put(&context->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Duplicated credential value '%s', ignoring assignment: %s", lc->id, rvalue);
+                        return 0;
+                }
+
+                TAKE_PTR(lc);
+        }
 
         return 0;
 }
index d3a5b25d186bf67e5a5f4d39ec9c5c9ee8cf0d60..a97ffbcea45410d1392a6c81725ffba45dcb6750 100644 (file)
@@ -1046,12 +1046,12 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                 return 1;
         }
 
-        if (streq(field, "SetCredential")) {
+        if (STR_IN_SET(field, "SetCredential", "SetCredentialEncrypted")) {
                 r = sd_bus_message_open_container(m, 'r', "sv");
                 if (r < 0)
                         return bus_log_create_error(r);
 
-                r = sd_bus_message_append_basic(m, 's', "SetCredential");
+                r = sd_bus_message_append_basic(m, 's', field);
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1062,21 +1062,16 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "a(say)", 0);
                 else {
-                        _cleanup_free_ char *word = NULL, *unescaped = NULL;
+                        _cleanup_free_ char *word = NULL;
                         const char *p = eq;
-                        int l;
 
                         r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
                         if (r == -ENOMEM)
                                 return log_oom();
                         if (r < 0)
-                                return log_error_errno(r, "Failed to parse SetCredential= parameter: %s", eq);
+                                return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
                         if (r == 0 || !p)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to SetCredential=.");
-
-                        l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
-                        if (l < 0)
-                                return log_error_errno(l, "Failed to unescape SetCredential= value: %s", p);
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field);
 
                         r = sd_bus_message_open_container(m, 'a', "(say)");
                         if (r < 0)
@@ -1090,7 +1085,25 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                         if (r < 0)
                                 return bus_log_create_error(r);
 
-                        r = sd_bus_message_append_array(m, 'y', unescaped, l);
+                        if (streq(field, "SetCredentialEncrypted")) {
+                                _cleanup_free_ void *decoded = NULL;
+                                size_t decoded_size;
+
+                                r = unbase64mem(p, SIZE_MAX, &decoded, &decoded_size);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to base64 decode encrypted credential: %m");
+
+                                r = sd_bus_message_append_array(m, 'y', decoded, decoded_size);
+                        } else {
+                                _cleanup_free_ char *unescaped = NULL;
+                                int l;
+
+                                l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
+                                if (l < 0)
+                                        return log_error_errno(l, "Failed to unescape %s= value: %s", field, p);
+
+                                r = sd_bus_message_append_array(m, 'y', unescaped, l);
+                        }
                         if (r < 0)
                                 return bus_log_create_error(r);
 
@@ -1114,12 +1127,12 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                 return 1;
         }
 
-        if (streq(field, "LoadCredential")) {
+        if (STR_IN_SET(field, "LoadCredential", "LoadCredentialEncrypted")) {
                 r = sd_bus_message_open_container(m, 'r', "sv");
                 if (r < 0)
                         return bus_log_create_error(r);
 
-                r = sd_bus_message_append_basic(m, 's', "LoadCredential");
+                r = sd_bus_message_append_basic(m, 's', field);
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1137,9 +1150,9 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                         if (r == -ENOMEM)
                                 return log_oom();
                         if (r < 0)
-                                return log_error_errno(r, "Failed to parse LoadCredential= parameter: %s", eq);
+                                return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
                         if (r == 0 || !p)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to LoadCredential=.");
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field);
 
                         r = sd_bus_message_append(m, "a(ss)", 1, word, p);
                 }
index 451f29198844db51566baf2b1823eca1c3a2151f..d161c81ff685a021eb53537d7635d9c52ea69587 100644 (file)
@@ -83,6 +83,7 @@ LimitRTTIME=
 LimitSIGPENDING=
 LimitSTACK=
 LoadCredential=
+LoadCredentialEncrypted=
 LockPersonality=
 LogExtraFields=
 LogLevelMax=
@@ -159,6 +160,7 @@ SecureBits=
 SendSIGHUP=
 SendSIGKILL=
 SetCredential=
+SetCredentialEncrypted=
 Slice=
 SloppyOptions=
 SmackProcessLabel=
index de7d2c7dafeaa5e5887a0ae8d70fe42759f8d841..35d1f2a104f266f0604d35b6c9110161815446ec 100644 (file)
@@ -205,6 +205,7 @@ LimitRTTIME=
 LimitSIGPENDING=
 LimitSTACK=
 LoadCredential=
+LoadCredentialEncrypted=
 LockPersonality=
 LogExtraFields=
 LogLevelMax=
@@ -292,6 +293,7 @@ SecureBits=
 SendSIGHUP=
 SendSIGKILL=
 SetCredential=
+SetCredentialEncrypted=
 Slice=
 SmackProcessLabel=
 Sockets=
index 11f589e22cc7bbbd845eff7773381e5d1e9aa622..1835167cfba18d3e8154885784e9edbdd40f76dc 100644 (file)
@@ -108,6 +108,7 @@ ListenSpecial=
 ListenStream=
 ListenUSBFunction=
 LoadCredential=
+LoadCredentialEncrypted=
 LockPersonality=
 LogExtraFields=
 LogLevelMax=
@@ -199,6 +200,7 @@ SendSIGHUP=
 SendSIGKILL=
 Service=
 SetCredential=
+SetCredentialEncrypted=
 Slice=
 SmackLabel=
 SmackLabelIPIn=
index 582a136531f78a4035b8232e873c3de3bd304cdd..814d066faced786d305f80d0cd8a700483cb0e47 100644 (file)
@@ -80,6 +80,7 @@ LimitRTTIME=
 LimitSIGPENDING=
 LimitSTACK=
 LoadCredential=
+LoadCredentialEncrypted=
 LockPersonality=
 LogExtraFields=
 LogLevelMax=
@@ -156,6 +157,7 @@ SecureBits=
 SendSIGHUP=
 SendSIGKILL=
 SetCredential=
+SetCredentialEncrypted=
 Slice=
 SmackProcessLabel=
 SocketBindAllow=