]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: firewall integration with DynamicUserNFTSet=
authorTopi Miettinen <toiwoton@gmail.com>
Sun, 22 May 2022 12:17:24 +0000 (15:17 +0300)
committerTopi Miettinen <topimiettinen@users.noreply.github.com>
Wed, 8 Jun 2022 16:12:25 +0000 (16:12 +0000)
New directive `DynamicUserNFTSet=` provides a method for integrating
configuration of dynamic users into firewall rules with NFT sets.

Example:
```
table inet filter {
        set u {
                typeof meta skuid
        }

        chain service_output {
                meta skuid != @u drop
                accept
        }
}
```

```
/etc/systemd/system/dunft.service
[Service]
DynamicUser=yes
DynamicUserNFTSet=inet:filter:u
ExecStart=/bin/sleep 1000

[Install]
WantedBy=multi-user.target
```

```
$ sudo nft list set inet filter u
table inet filter {
        set u {
                typeof meta skuid
                elements = { 64864 }
        }
}
$ ps -n --format user,group,pid,command -p `pgrep sleep`
    USER    GROUP     PID COMMAND
   64864    64864   55158 /bin/sleep 1000
```

14 files changed:
man/org.freedesktop.systemd1.xml
man/systemd.exec.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/core/load-fragment.h
src/core/service.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 6625a74073e4317c44aaf7d6795f99297a24256f..b9b5768bf0892dfa1adc45dbe474aa86f1cabf26 100644 (file)
@@ -2785,6 +2785,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b DynamicUser = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(iss) DynamicUserNFTSet = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b RemoveIPC = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(say) SetCredential = [...];
@@ -3332,6 +3334,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property DynamicUser is not documented!-->
 
+    <!--property DynamicUserNFTSet is not documented!-->
+
     <!--property RemoveIPC is not documented!-->
 
     <!--property SetCredential is not documented!-->
@@ -3940,6 +3944,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="DynamicUserNFTSet"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
@@ -4679,6 +4685,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b DynamicUser = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(iss) DynamicUserNFTSet = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b RemoveIPC = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(say) SetCredential = [...];
@@ -5250,6 +5258,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--property DynamicUser is not documented!-->
 
+    <!--property DynamicUserNFTSet is not documented!-->
+
     <!--property RemoveIPC is not documented!-->
 
     <!--property SetCredential is not documented!-->
@@ -5852,6 +5862,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="DynamicUserNFTSet"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
@@ -6480,6 +6492,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b DynamicUser = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(iss) DynamicUserNFTSet = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b RemoveIPC = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(say) SetCredential = [...];
@@ -6979,6 +6993,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <!--property DynamicUser is not documented!-->
 
+    <!--property DynamicUserNFTSet is not documented!-->
+
     <!--property RemoveIPC is not documented!-->
 
     <!--property SetCredential is not documented!-->
@@ -7499,6 +7515,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="DynamicUserNFTSet"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
@@ -8254,6 +8272,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b DynamicUser = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(iss) DynamicUserNFTSet = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b RemoveIPC = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(say) SetCredential = [...];
@@ -8739,6 +8759,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <!--property DynamicUser is not documented!-->
 
+    <!--property DynamicUserNFTSet is not documented!-->
+
     <!--property RemoveIPC is not documented!-->
 
     <!--property SetCredential is not documented!-->
@@ -9245,6 +9267,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="DynamicUserNFTSet"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
index 50c5c89703bf863263d0fccbc9fead3722e81c8f..9798a8d999bec532f93a26ebcf2633f0aabf3a27 100644 (file)
@@ -3163,6 +3163,40 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>Firewall Integration</title>
+    <variablelist class='unit-directives'>
+
+      <varlistentry>
+        <term><varname>DynamicUserNFTSet=</varname><replaceable>family</replaceable>:<replaceable>table</replaceable>:<replaceable>set</replaceable></term>
+        <listitem><para>This setting provides a method for integrating <varname>DynamicUser=</varname>
+        configuration into firewall rules with NFT sets. This option expects a whitespace separated list of
+        NFT set definitions. Each definition consists of a colon-separated tuple of NFT address family (one
+        of <literal>arp</literal>, <literal>bridge</literal>, <literal>inet</literal>, <literal>ip</literal>,
+        <literal>ip6</literal>, or <literal>netdev</literal>), table name and set name. The names of tables
+        and sets must conform to lexical restrictions of NFT table names. When the unit starts, the user ID
+        will be appended to the NFT sets and it will be removed when the unit is stopped. Failures to manage
+        the sets will be ignored.</para>
+
+          <para>Example:
+          <programlisting>[Service]
+DynamicUserNFTSet=inet:filter:u</programlisting>
+          Corresponding NFT rules:
+          <programlisting>table inet filter {
+        set u {
+                typeof meta skuid
+        }
+        chain service_output {
+                meta skuid != @u drop
+                accept
+        }
+}</programlisting>
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>System V Compatibility</title>
     <variablelist class='unit-directives'>
index a4c37307d21a9346e4d6c926a96cb9dc1bb27206..a3e54e64115bb3a0601e0489b6ee456d225f6b6b 100644 (file)
@@ -22,6 +22,7 @@
 #include "execute.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "firewall-util.h"
 #include "hexdecoct.h"
 #include "io-util.h"
 #include "ioprio-util.h"
@@ -1138,6 +1139,37 @@ static int bus_property_get_exec_dir_symlink(
         return sd_bus_message_close_container(reply);
 }
 
+static int property_get_dynamic_user_nft_set(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        ExecContext *c = userdata;
+        int r;
+
+        assert(bus);
+        assert(reply);
+        assert(c);
+
+        r = sd_bus_message_open_container(reply, 'a', "(iss)");
+        if (r < 0)
+                return r;
+
+        for (size_t i = 0; i < c->n_dynamic_user_nft_set_contexts; i++) {
+                NFTSetContext *s = &c->dynamic_user_nft_set_context[i];
+
+                r = sd_bus_message_append(reply, "(iss)", s->nfproto, s->table, s->set);
+                if (r < 0)
+                        return r;
+        }
+
+        return sd_bus_message_close_container(reply);
+}
+
 const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1232,6 +1264,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DynamicUserNFTSet", "a(iss)", property_get_dynamic_user_nft_set, 0, 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),
@@ -3503,6 +3536,58 @@ int bus_exec_context_set_transient_property(
 
                 return 1;
 
+        } else if (streq(name, "DynamicUserNFTSet")) {
+                int nfproto;
+                const char *table, *set;
+                bool empty = true;
+
+                r = sd_bus_message_enter_container(message, 'a', "(iss)");
+                if (r < 0)
+                        return r;
+
+                while ((r = sd_bus_message_read(message, "(iss)", &nfproto, &table, &set)) > 0) {
+                        const char *nfproto_name;
+
+                        nfproto_name = nfproto_to_string(nfproto);
+                        if (!nfproto_name)
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid protocol %d.", nfproto);
+
+                        if (nft_identifier_bad(table))
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NFT table name %s.", table);
+
+                        if (nft_identifier_bad(set))
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NFT set name %s.", set);
+
+                        if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+                                r = nft_set_context_add(&c->dynamic_user_nft_set_context, &c->n_dynamic_user_nft_set_contexts, nfproto, table, set);
+                                if (r < 0)
+                                        return r;
+
+                                unit_write_settingf(
+                                                u, flags|UNIT_ESCAPE_SPECIFIERS, name,
+                                                "%s=%s:%s:%s",
+                                                name,
+                                                nfproto_name,
+                                                table,
+                                                set);
+                        }
+
+                        empty = false;
+                }
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_exit_container(message);
+                if (r < 0)
+                        return r;
+
+                if (empty) {
+                        c->dynamic_user_nft_set_context = nft_set_context_free_many(c->dynamic_user_nft_set_context, &c->n_dynamic_user_nft_set_contexts);
+                        unit_write_settingf(u, flags, name, "%s=", name);
+                }
+
+                return 1;
+
         } else if ((suffix = startswith(name, "Limit"))) {
                 const char *soft = NULL;
                 int ri;
index 05fc00ca1ce56184555d8308184620bab362832f..f128a45f546f98cdfd55dc398289b9e909c56e5f 100644 (file)
@@ -4083,6 +4083,43 @@ static int add_shifted_fd(int *fds, size_t fds_size, size_t *n_fds, int fd, int
         return 1;
 }
 
+static void exec_op_dynamic_user_nft_set(bool add, const ExecContext *c, uid_t uid) {
+        int r;
+
+        assert(c);
+
+        for (size_t i = 0; i < c->n_dynamic_user_nft_set_contexts; i++) {
+                NFTSetContext *s = &c->dynamic_user_nft_set_context[i];
+                if (add)
+                        r = nft_set_element_add_uint32(s, uid);
+                else
+                        r = nft_set_element_del_uint32(s, uid);
+                if (r < 0)
+                        log_warning_errno(r, "%s NFT family %s table %s set %s UID " UID_FMT " failed, ignoring: %m",
+                                          add? "Adding" : "Deleting", nfproto_to_string(s->nfproto), s->table, s->set, uid);
+        }
+}
+
+static void exec_add_dynamic_user_nft_set(const ExecContext *c, uid_t uid) {
+        exec_op_dynamic_user_nft_set(true, c, uid);
+}
+
+void exec_delete_dynamic_user_nft_set(const ExecContext *c, DynamicUser *d) {
+        int r;
+        uid_t uid;
+
+        if (!d)
+                return;
+
+        r = dynamic_user_current(d, &uid);
+        if (r < 0) {
+                log_warning_errno(r, "Can't get current dynamic user, ignoring: %m");
+                return;
+        }
+
+        exec_op_dynamic_user_nft_set(false, c, uid);
+}
+
 static int exec_child(
                 Unit *unit,
                 const ExecCommand *command,
@@ -4284,6 +4321,8 @@ static int exec_child(
                 if (dcreds->user)
                         username = dcreds->user->name;
 
+                exec_add_dynamic_user_nft_set(context, uid);
+
         } else {
                 r = get_fixed_user(context, &username, &uid, &gid, &home, &shell);
                 if (r < 0) {
@@ -5346,6 +5385,8 @@ void exec_context_done(ExecContext *c) {
         c->user = mfree(c->user);
         c->group = mfree(c->group);
 
+        c->dynamic_user_nft_set_context = nft_set_context_free_many(c->dynamic_user_nft_set_context, &c->n_dynamic_user_nft_set_contexts);
+
         c->supplementary_groups = strv_free(c->supplementary_groups);
 
         c->pam_name = mfree(c->pam_name);
@@ -6020,6 +6061,11 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                 fprintf(f, "%sGroup: %s\n", prefix, c->group);
 
         fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user));
+        for (size_t i = 0; i < c->n_dynamic_user_nft_set_contexts; i++)
+                fprintf(f, "%sDynamicUserNFTSet: %s:%s:%s\n", prefix,
+                        nfproto_to_string(c->dynamic_user_nft_set_context[i].nfproto),
+                        c->dynamic_user_nft_set_context[i].table,
+                        c->dynamic_user_nft_set_context[i].set);
 
         strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
 
index 904e7943f32e29c20ca821997c83717333d0de4a..b3516c29fc025dd3b5a4769aeda3b9b5600db3e6 100644 (file)
@@ -18,6 +18,7 @@ typedef struct Manager Manager;
 #include "cpu-set-util.h"
 #include "exec-util.h"
 #include "fdset.h"
+#include "firewall-util.h"
 #include "list.h"
 #include "missing_resource.h"
 #include "namespace.h"
@@ -313,6 +314,9 @@ struct ExecContext {
         bool mount_apivfs;
 
         bool dynamic_user;
+        size_t n_dynamic_user_nft_set_contexts;
+        NFTSetContext *dynamic_user_nft_set_context;
+
         bool remove_ipc;
 
         bool memory_deny_write_execute;
@@ -522,3 +526,5 @@ const char* exec_resource_type_to_string(ExecDirectoryType i) _const_;
 ExecDirectoryType exec_resource_type_from_string(const char *s) _pure_;
 
 bool exec_needs_mount_namespace(const ExecContext *context, const ExecParameters *params, const ExecRuntime *runtime);
+
+void exec_delete_dynamic_user_nft_set(const ExecContext *c, DynamicUser *d);
index 0db24268d19a5662d89fbf44676d9f78b301c786..facda69d0dd86466815295029f7e1f2037f622d3 100644 (file)
@@ -32,6 +32,7 @@
 {{type}}.PassEnvironment,                  config_parse_pass_environ,                   0,                                  offsetof({{type}}, exec_context.pass_environment)
 {{type}}.UnsetEnvironment,                 config_parse_unset_environ,                  0,                                  offsetof({{type}}, exec_context.unset_environment)
 {{type}}.DynamicUser,                      config_parse_bool,                           true,                               offsetof({{type}}, exec_context.dynamic_user)
+{{type}}.DynamicUserNFTSet,                config_parse_dynamic_user_nft_set,           0,                                  offsetof({{type}}, exec_context)
 {{type}}.RemoveIPC,                        config_parse_bool,                           0,                                  offsetof({{type}}, exec_context.remove_ipc)
 {{type}}.StandardInput,                    config_parse_exec_input,                     0,                                  offsetof({{type}}, exec_context)
 {{type}}.StandardOutput,                   config_parse_exec_output,                    0,                                  offsetof({{type}}, exec_context)
index 12c1af7933f73aa67b433baed290bb619f7d84cb..8c136b140271a4c0583d54532b098d453d6e8118 100644 (file)
@@ -6607,3 +6607,20 @@ int config_parse_cgroup_nft_set(
 
         return config_parse_nft_set(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &c->nft_set_context, &c->n_nft_set_contexts, u);
 }
+
+int config_parse_dynamic_user_nft_set(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+        ExecContext *c = data;
+        Unit *u = userdata;
+
+        return config_parse_nft_set(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &c->dynamic_user_nft_set_context, &c->n_dynamic_user_nft_set_contexts, u);
+}
index 3632b5b0962a531724f25a420cf1020c2b39d780..c250e4684616cfb09f0673a6b265352c5246c1aa 100644 (file)
@@ -151,6 +151,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_restrict_network_interfaces);
 CONFIG_PARSER_PROTOTYPE(config_parse_watchdog_sec);
 CONFIG_PARSER_PROTOTYPE(config_parse_tty_size);
 CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_nft_set);
+CONFIG_PARSER_PROTOTYPE(config_parse_dynamic_user_nft_set);
 
 /* gperf prototypes */
 const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
index 5f1a218bb5beba1a679f0d2340b7343ee0cfb6b1..f8d751e32fcbdb0da7c9251e7e2880e58874a39e 100644 (file)
@@ -1877,6 +1877,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
         /* Get rid of the IPC bits of the user */
         unit_unref_uid_gid(UNIT(s), true);
 
+        /* Delete DynamicUserNFTSet= */
+        exec_delete_dynamic_user_nft_set(&s->exec_context, s->dynamic_creds.user);
+
         /* Release the user, and destroy it if we are the only remaining owner */
         dynamic_creds_destroy(&s->dynamic_creds);
 
index e1584c2e8dd5a031a0c7e168231da7154414b435..1ffdcf384fce634de5a0a82ba41a72a33d0afd0d 100644 (file)
@@ -16,6 +16,7 @@
 #include "exec-util.h"
 #include "exit-status.h"
 #include "fileio.h"
+#include "firewall-util.h"
 #include "hexdecoct.h"
 #include "hostname-util.h"
 #include "in-addr-util.h"
@@ -434,6 +435,91 @@ static int bus_append_ip_address_access(sd_bus_message *m, int family, const uni
         return sd_bus_message_close_container(m);
 }
 
+static int bus_append_nft_set(sd_bus_message *m, const char *field, const char *eq) {
+        int r;
+
+        assert(m);
+
+        if (isempty(eq)) {
+                r = sd_bus_message_append(m, "(sv)", field, "a(iss)", 0);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                return 1;
+        }
+
+        r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_open_container(m, 'v', "a(iss)");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_open_container(m, 'a', "(iss)");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
+                int family;
+
+                r = extract_first_word(&eq, &word, ":", 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse %s: %m", field);
+                if (isempty(word)) {
+                        log_error("Failed to parse %s", field);
+                        return 0;
+                }
+
+                family = nfproto_from_string(word);
+                if (family < 0)
+                        return log_error_errno(family, "Failed to parse %s: %m", field);
+
+                r = extract_first_word(&eq, &word, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse %s: %m", field);
+                if (isempty(word) || isempty(eq)) {
+                        log_error("Failed to parse %s", field);
+                        return 0;
+                }
+
+                _cleanup_free_ char *unescaped = NULL;
+                ssize_t l;
+
+                l = cunescape(eq, 0, &unescaped);
+                if (l < 0)
+                        return log_error_errno(l, "Failed to unescape %s= value: %s", field, eq);
+
+                r = sd_bus_message_append(m, "(iss)", family, word, eq);
+
+                r = sd_bus_message_close_container(m);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        return 1;
+}
+
 static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) {
         int r;
 
@@ -2051,6 +2137,9 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                 return 1;
         }
 
+        if (STR_IN_SET(field, "DynamicUserNFTSet"))
+                return bus_append_nft_set(m, field, eq);
+
         return 0;
 }
 
index 2b3331a411fe87990beac3a141eec86951239875..16d2138a04cf10bd1fff84d22c2cb4a139e5d73a 100644 (file)
@@ -38,6 +38,7 @@ DevicePolicy=
 DirectoryMode=
 DisableControllers=
 DynamicUser=
+DynamicUserNFTSet=
 Environment=
 EnvironmentFile=
 ExecPaths=
index 30e6936d123dc8a9600575063c787c565d245c1e..511c2f6b4fbcb6101cdaaf005261c15d6169dd9b 100644 (file)
@@ -160,6 +160,7 @@ DeviceAllow=
 DevicePolicy=
 DisableControllers=
 DynamicUser=
+DynamicUserNFTSet=
 Environment=
 EnvironmentFile=
 ExecCondition=
index 1b1ddf8c9c7b972a86cdf5e8a7261a953033650d..b9ad5e5f84ec883fc6967db08e96cf1d6ec74ab9 100644 (file)
@@ -44,6 +44,7 @@ DevicePolicy=
 DirectoryMode=
 DisableControllers=
 DynamicUser=
+DynamicUserNFTSet=
 Environment=
 EnvironmentFile=
 ExecPaths=
index 186dedbf3e8a2e773850d463fd6205f37107345a..4721edce4be4dd81da2b2e401df300c9850234c6 100644 (file)
@@ -37,6 +37,7 @@ DeviceAllow=
 DevicePolicy=
 DisableControllers=
 DynamicUser=
+DynamicUserNFTSet=
 Environment=
 EnvironmentFile=
 ExecPaths=