]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
homectl: expose "register" verb to register a user record locally
authorLennart Poettering <lennart@poettering.net>
Thu, 20 Feb 2025 09:21:57 +0000 (10:21 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 7 Mar 2025 17:14:45 +0000 (18:14 +0100)
man/homectl.xml
shell-completion/bash/homectl
src/home/homectl.c

index 610c69ccc12c161d041be885674c9e5e002018b1..1acb338bbb2d083122e0c354925822039aad8510 100644 (file)
         <xi:include href="version-info.xml" xpointer="v258"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><command>register</command> <replaceable>FILE</replaceable> [<replaceable>FILE</replaceable>…]</term>
+
+        <listitem><para>Registers one or more users, without creating their home directories. Takes one or
+        more paths to JSON user record files. If the path is specified as <literal>-</literal> reads the
+        JSON user record from standard input.</para>
+
+        <para>Registering a user makes it accessible on the local system without creating a new home
+        directory. This is particularly useful for making a user accessible on a system it was not originally
+        created on.</para>
+
+        <para>Here's an example how to make a local user account with its home directory accessible on a
+        remote system, using SMB/CIFS file sharing. With Samba installed in its default configuration invoke
+        as <literal>root</literal>:</para>
+
+        <programlisting># smbpasswd -a lennart</programlisting>
+
+        <para>Continue as regular user <literal>lennart</literal>:</para>
+
+<programlisting>$ homectl update lennart --ssh-authorized-keys=… -N --storage=cifs --cifs-service="//$HOSTNAME/lennart"
+$ homectl get-signing-key | ssh targetsystem homectl add-signing-key --key-name="$HOSTNAME".public
+$ homectl inspect -E lennart | ssh targetsystem homectl register -
+$ ssh lennart@targetsystem</programlisting>
+
+        <para>This first ensures the user account <literal>lennart</literal> is known to and accessible by
+        Samba. It then registers a local SSH access that shall be used for accessing this user, and
+        configures CIFS as default storage for non-local systems on the account. It then adds the local
+        system's account signing key to the target system. Then it registers the local user account with the
+        target system. Finally it logs into the account on the target system. The target system will then
+        connect back via SMB/CIFS to access the home directory.</para>
+
+        <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>unregister</command> <replaceable>USER</replaceable>…</term>
+
+        <listitem><para>Unregisters one or more user accounts. This only removes the user record from the
+        local system, it does not delete the home directory. The home directory can be readded via the
+        <command>register</command> or <command>adopt</command> command later, on this or another
+        system. Note that unregistering a user whose home directory is placed in <filename>/home/</filename>
+        will not make the user disappear from the local user database, as all supported home directories
+        placed there will show up in the user database. However, the user record will become "unfixated",
+        i.e. lose its binding to the local system. When logged into it will automatically regain the binding,
+        and acquire a local UID/GID pair.</para>
+
+        <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><command>remove</command> <replaceable>USER</replaceable></term>
 
index 3bd29fc808ba435f21b94d1d0e982bd4cfffd485..1b365fbb712da5d997f3c5c7b4cc738d3c272ccd 100644 (file)
@@ -173,7 +173,7 @@ _homectl() {
     fi
 
     local -A VERBS=(
-        [STANDALONE]='list lock-all adopt'
+        [STANDALONE]='list lock-all register unregister adopt'
         [CREATE]='create'
         [NAMES]='activate deactivate inspect authenticate remove lock unlock'
         [NAME]='update passwd'
index 123fcd99985cd1e209dbde33f352a2585932ae03..12d3c403f9cb77c5dd4efb0e3be3234d5edb10aa 100644 (file)
@@ -1596,6 +1596,101 @@ static int verb_adopt_home(int argc, char *argv[], void *userdata) {
         return ret;
 }
 
+static int register_home_one(sd_bus *bus, FILE *f, const char *path) {
+        int r;
+
+        assert(bus);
+        assert(path);
+
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+        unsigned line = 0, column = 0;
+        r = sd_json_parse_file(f, path, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+        if (r < 0)
+                return log_error_errno(r, "[%s:%u:%u] Failed to parse user record: %m", path, line, column);
+
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+        r = bus_message_new_method_call(bus, &m, bus_mgr, "RegisterHome");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        _cleanup_free_ char *formatted = NULL;
+        r = sd_json_variant_format(v, /* flags= */ 0, &formatted);
+        if (r < 0)
+                return log_error_errno(r, "Failed to format JSON record: %m");
+
+        r = sd_bus_message_append(m, "s", formatted);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to register home: %s", bus_error_message(&error, r));
+
+        return 0;
+}
+
+static int verb_register_home(int argc, char *argv[], void *userdata) {
+        int r;
+
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+        if (arg_identity) {
+                if (argc > 1)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not accepting an arguments if --identity= is specified, refusing.");
+
+                return register_home_one(bus, /* f= */ NULL, arg_identity);
+        }
+
+        if (argc == 1 || (argc == 2 && streq(argv[1], "-")))
+                return register_home_one(bus, /* f= */ stdin, "<stdio>");
+
+        r = 0;
+        STRV_FOREACH(i, strv_skip(argv, 1)) {
+                if (streq(*i, "-"))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Refusing reading from standard input if multiple user records are specified.");
+
+                RET_GATHER(r, register_home_one(bus, /* f= */ NULL, *i));
+        }
+
+        return r;
+}
+
+static int verb_unregister_home(int argc, char *argv[], void *userdata) {
+        int r;
+
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+        int ret = 0;
+        STRV_FOREACH(i, strv_skip(argv, 1)) {
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+                r = bus_message_new_method_call(bus, &m, bus_mgr, "UnregisterHome");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "s", *i);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, /* ret_reply= */ NULL);
+                if (r < 0)
+                        RET_GATHER(ret, log_error_errno(r, "Failed to unregister home: %s", bus_error_message(&error, r)));
+        }
+
+        return ret;
+}
+
 static int remove_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r, ret = 0;
@@ -2841,6 +2936,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "  authenticate USER…           Authenticate a home area\n"
                "  create USER                  Create a home area\n"
                "  adopt PATH…                  Add an existing home area on this system\n"
+               "  register PATH…               Register a user record locally\n"
+               "  unregister USER…             Unregister a user record locally\n"
                "  remove USER…                 Remove a home area\n"
                "  update USER                  Update a home area\n"
                "  passwd USER                  Change password of a home area\n"
@@ -5276,6 +5373,8 @@ static int run(int argc, char *argv[]) {
                 { "authenticate",       VERB_ANY, VERB_ANY, 0,            authenticate_home        },
                 { "create",             VERB_ANY, 2,        0,            create_home              },
                 { "adopt",              VERB_ANY, VERB_ANY, 0,            verb_adopt_home          },
+                { "register",           VERB_ANY, VERB_ANY, 0,            verb_register_home       },
+                { "unregister",         2,        VERB_ANY, 0,            verb_unregister_home     },
                 { "remove",             2,        VERB_ANY, 0,            remove_home              },
                 { "update",             VERB_ANY, 2,        0,            update_home              },
                 { "passwd",             VERB_ANY, 2,        0,            passwd_home              },