1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <getopt.h>
4 
5 #include "sd-bus.h"
6 
7 #include "ask-password-api.h"
8 #include "bus-common-errors.h"
9 #include "bus-error.h"
10 #include "bus-locator.h"
11 #include "cgroup-util.h"
12 #include "dns-domain.h"
13 #include "env-util.h"
14 #include "fd-util.h"
15 #include "fileio.h"
16 #include "format-table.h"
17 #include "fs-util.h"
18 #include "glyph-util.h"
19 #include "home-util.h"
20 #include "homectl-fido2.h"
21 #include "homectl-pkcs11.h"
22 #include "homectl-recovery-key.h"
23 #include "libfido2-util.h"
24 #include "locale-util.h"
25 #include "main-func.h"
26 #include "memory-util.h"
27 #include "pager.h"
28 #include "parse-argument.h"
29 #include "parse-util.h"
30 #include "path-util.h"
31 #include "percent-util.h"
32 #include "pkcs11-util.h"
33 #include "pretty-print.h"
34 #include "process-util.h"
35 #include "pwquality-util.h"
36 #include "rlimit-util.h"
37 #include "spawn-polkit-agent.h"
38 #include "terminal-util.h"
39 #include "uid-alloc-range.h"
40 #include "user-record-pwquality.h"
41 #include "user-record-show.h"
42 #include "user-record-util.h"
43 #include "user-record.h"
44 #include "user-util.h"
45 #include "verbs.h"
46 
47 static PagerFlags arg_pager_flags = 0;
48 static bool arg_legend = true;
49 static bool arg_ask_password = true;
50 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
51 static const char *arg_host = NULL;
52 static const char *arg_identity = NULL;
53 static JsonVariant *arg_identity_extra = NULL;
54 static JsonVariant *arg_identity_extra_privileged = NULL;
55 static JsonVariant *arg_identity_extra_this_machine = NULL;
56 static JsonVariant *arg_identity_extra_rlimits = NULL;
57 static char **arg_identity_filter = NULL; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
58 static char **arg_identity_filter_rlimits = NULL;
59 static uint64_t arg_disk_size = UINT64_MAX;
60 static uint64_t arg_disk_size_relative = UINT64_MAX;
61 static char **arg_pkcs11_token_uri = NULL;
62 static char **arg_fido2_device = NULL;
63 static Fido2EnrollFlags arg_fido2_lock_with = FIDO2ENROLL_PIN | FIDO2ENROLL_UP;
64 #if HAVE_LIBFIDO2
65 static int arg_fido2_cred_alg = COSE_ES256;
66 #else
67 static int arg_fido2_cred_alg = 0;
68 #endif
69 static bool arg_recovery_key = false;
70 static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
71 static bool arg_and_resize = false;
72 static bool arg_and_change_password = false;
73 static enum {
74         EXPORT_FORMAT_FULL,          /* export the full record */
75         EXPORT_FORMAT_STRIPPED,      /* strip "state" + "binding", but leave signature in place */
76         EXPORT_FORMAT_MINIMAL,       /* also strip signature */
77 } arg_export_format = EXPORT_FORMAT_FULL;
78 
79 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, json_variant_unrefp);
80 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, json_variant_unrefp);
81 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged, json_variant_unrefp);
82 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits, json_variant_unrefp);
83 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter, strv_freep);
84 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits, strv_freep);
85 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, strv_freep);
86 STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, strv_freep);
87 
88 static const BusLocator *bus_mgr;
89 
identity_properties_specified(void)90 static bool identity_properties_specified(void) {
91         return
92                 arg_identity ||
93                 !json_variant_is_blank_object(arg_identity_extra) ||
94                 !json_variant_is_blank_object(arg_identity_extra_privileged) ||
95                 !json_variant_is_blank_object(arg_identity_extra_this_machine) ||
96                 !json_variant_is_blank_object(arg_identity_extra_rlimits) ||
97                 !strv_isempty(arg_identity_filter) ||
98                 !strv_isempty(arg_identity_filter_rlimits) ||
99                 !strv_isempty(arg_pkcs11_token_uri) ||
100                 !strv_isempty(arg_fido2_device);
101 }
102 
acquire_bus(sd_bus ** bus)103 static int acquire_bus(sd_bus **bus) {
104         int r;
105 
106         assert(bus);
107 
108         if (*bus)
109                 return 0;
110 
111         r = bus_connect_transport(arg_transport, arg_host, false, bus);
112         if (r < 0)
113                 return bus_log_connect_error(r, arg_transport);
114 
115         (void) sd_bus_set_allow_interactive_authorization(*bus, arg_ask_password);
116 
117         return 0;
118 }
119 
list_homes(int argc,char * argv[],void * userdata)120 static int list_homes(int argc, char *argv[], void *userdata) {
121         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
122         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
123         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
124         _cleanup_(table_unrefp) Table *table = NULL;
125         int r;
126 
127         r = acquire_bus(&bus);
128         if (r < 0)
129                 return r;
130 
131         r = bus_call_method(bus, bus_mgr, "ListHomes", &error, &reply, NULL);
132         if (r < 0)
133                 return log_error_errno(r, "Failed to list homes: %s", bus_error_message(&error, r));
134 
135         table = table_new("name", "uid", "gid", "state", "realname", "home", "shell");
136         if (!table)
137                 return log_oom();
138 
139         r = sd_bus_message_enter_container(reply, 'a', "(susussso)");
140         if (r < 0)
141                 return bus_log_parse_error(r);
142 
143         for (;;) {
144                 const char *name, *state, *realname, *home, *shell, *color;
145                 TableCell *cell;
146                 uint32_t uid, gid;
147 
148                 r = sd_bus_message_read(reply, "(susussso)", &name, &uid, &state, &gid, &realname, &home, &shell, NULL);
149                 if (r < 0)
150                         return bus_log_parse_error(r);
151                 if (r == 0)
152                         break;
153 
154                 r = table_add_many(table,
155                                    TABLE_STRING, name,
156                                    TABLE_UID, uid,
157                                    TABLE_GID, gid);
158                 if (r < 0)
159                         return table_log_add_error(r);
160 
161 
162                 r = table_add_cell(table, &cell, TABLE_STRING, state);
163                 if (r < 0)
164                         return table_log_add_error(r);
165 
166                 color = user_record_state_color(state);
167                 if (color)
168                         (void) table_set_color(table, cell, color);
169 
170                 r = table_add_many(table,
171                                    TABLE_STRING, strna(empty_to_null(realname)),
172                                    TABLE_STRING, home,
173                                    TABLE_STRING, strna(empty_to_null(shell)));
174                 if (r < 0)
175                         return table_log_add_error(r);
176         }
177 
178         r = sd_bus_message_exit_container(reply);
179         if (r < 0)
180                 return bus_log_parse_error(r);
181 
182         if (table_get_rows(table) > 1 || !FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
183                 r = table_set_sort(table, (size_t) 0);
184                 if (r < 0)
185                         return table_log_sort_error(r);
186 
187                 r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
188                 if (r < 0)
189                         return r;
190         }
191 
192         if (arg_legend && (arg_json_format_flags & JSON_FORMAT_OFF)) {
193                 if (table_get_rows(table) > 1)
194                         printf("\n%zu home areas listed.\n", table_get_rows(table) - 1);
195                 else
196                         printf("No home areas.\n");
197         }
198 
199         return 0;
200 }
201 
acquire_existing_password(const char * user_name,UserRecord * hr,bool emphasize_current,AskPasswordFlags flags)202 static int acquire_existing_password(
203                 const char *user_name,
204                 UserRecord *hr,
205                 bool emphasize_current,
206                 AskPasswordFlags flags) {
207 
208         _cleanup_(strv_free_erasep) char **password = NULL;
209         _cleanup_(erase_and_freep) char *envpw = NULL;
210         _cleanup_free_ char *question = NULL;
211         int r;
212 
213         assert(user_name);
214         assert(hr);
215 
216         r = getenv_steal_erase("PASSWORD", &envpw);
217         if (r < 0)
218                 return log_error_errno(r, "Failed to acquire password from environment: %m");
219         if (r > 0) {
220                 /* People really shouldn't use environment variables for passing passwords. We support this
221                  * only for testing purposes, and do not document the behaviour, so that people won't
222                  * actually use this outside of testing. */
223 
224                 r = user_record_set_password(hr, STRV_MAKE(envpw), true);
225                 if (r < 0)
226                         return log_error_errno(r, "Failed to store password: %m");
227 
228                 return 1;
229         }
230 
231         /* If this is not our own user, then don't use the password cache */
232         if (is_this_me(user_name) <= 0)
233                 SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, false);
234 
235         if (asprintf(&question, emphasize_current ?
236                      "Please enter current password for user %s:" :
237                      "Please enter password for user %s:",
238                      user_name) < 0)
239                 return log_oom();
240 
241         r = ask_password_auto(question,
242                               /* icon= */ "user-home",
243                               NULL,
244                               /* key_name= */ "home-password",
245                               /* credential_name= */ "home.password",
246                               USEC_INFINITY,
247                               flags,
248                               &password);
249         if (r == -EUNATCH) { /* EUNATCH is returned if no password was found and asking interactively was
250                               * disabled via the flags. Not an error for us. */
251                 log_debug_errno(r, "No passwords acquired.");
252                 return 0;
253         }
254         if (r < 0)
255                 return log_error_errno(r, "Failed to acquire password: %m");
256 
257         r = user_record_set_password(hr, password, true);
258         if (r < 0)
259                 return log_error_errno(r, "Failed to store password: %m");
260 
261         return 1;
262 }
263 
acquire_recovery_key(const char * user_name,UserRecord * hr,AskPasswordFlags flags)264 static int acquire_recovery_key(
265                 const char *user_name,
266                 UserRecord *hr,
267                 AskPasswordFlags flags) {
268 
269         _cleanup_(strv_free_erasep) char **recovery_key = NULL;
270         _cleanup_(erase_and_freep) char *envpw = NULL;
271         _cleanup_free_ char *question = NULL;
272         int r;
273 
274         assert(user_name);
275         assert(hr);
276 
277         r = getenv_steal_erase("PASSWORD", &envpw);
278         if (r < 0)
279                 return log_error_errno(r, "Failed to acquire password from environment: %m");
280         if (r > 0) {
281                 /* People really shouldn't use environment variables for passing secrets. We support this
282                  * only for testing purposes, and do not document the behaviour, so that people won't
283                  * actually use this outside of testing. */
284 
285                 r = user_record_set_password(hr, STRV_MAKE(envpw), true); /* recovery keys are stored in the record exactly like regular passwords! */
286                 if (r < 0)
287                         return log_error_errno(r, "Failed to store recovery key: %m");
288 
289                 return 1;
290         }
291 
292         /* If this is not our own user, then don't use the password cache */
293         if (is_this_me(user_name) <= 0)
294                 SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, false);
295 
296         if (asprintf(&question, "Please enter recovery key for user %s:", user_name) < 0)
297                 return log_oom();
298 
299         r = ask_password_auto(question,
300                               /* icon= */ "user-home",
301                               NULL,
302                               /* key_name= */ "home-recovery-key",
303                               /* credential_name= */ "home.recovery-key",
304                               USEC_INFINITY,
305                               flags,
306                               &recovery_key);
307         if (r == -EUNATCH) { /* EUNATCH is returned if no recovery key was found and asking interactively was
308                               * disabled via the flags. Not an error for us. */
309                 log_debug_errno(r, "No recovery keys acquired.");
310                 return 0;
311         }
312         if (r < 0)
313                 return log_error_errno(r, "Failed to acquire recovery keys: %m");
314 
315         r = user_record_set_password(hr, recovery_key, true);
316         if (r < 0)
317                 return log_error_errno(r, "Failed to store recovery keys: %m");
318 
319         return 1;
320 }
321 
acquire_token_pin(const char * user_name,UserRecord * hr,AskPasswordFlags flags)322 static int acquire_token_pin(
323                 const char *user_name,
324                 UserRecord *hr,
325                 AskPasswordFlags flags) {
326 
327         _cleanup_(strv_free_erasep) char **pin = NULL;
328         _cleanup_(erase_and_freep) char *envpin = NULL;
329         _cleanup_free_ char *question = NULL;
330         int r;
331 
332         assert(user_name);
333         assert(hr);
334 
335         r = getenv_steal_erase("PIN", &envpin);
336         if (r < 0)
337                 return log_error_errno(r, "Failed to acquire PIN from environment: %m");
338         if (r > 0) {
339                 r = user_record_set_token_pin(hr, STRV_MAKE(envpin), false);
340                 if (r < 0)
341                         return log_error_errno(r, "Failed to store token PIN: %m");
342 
343                 return 1;
344         }
345 
346         /* If this is not our own user, then don't use the password cache */
347         if (is_this_me(user_name) <= 0)
348                 SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, false);
349 
350         if (asprintf(&question, "Please enter security token PIN for user %s:", user_name) < 0)
351                 return log_oom();
352 
353         r = ask_password_auto(
354                         question,
355                         /* icon= */ "user-home",
356                         NULL,
357                         /* key_name= */ "token-pin",
358                         /* credential_name= */ "home.token-pin",
359                         USEC_INFINITY,
360                         flags,
361                         &pin);
362         if (r == -EUNATCH) { /* EUNATCH is returned if no PIN was found and asking interactively was disabled
363                               * via the flags. Not an error for us. */
364                 log_debug_errno(r, "No security token PINs acquired.");
365                 return 0;
366         }
367         if (r < 0)
368                 return log_error_errno(r, "Failed to acquire security token PIN: %m");
369 
370         r = user_record_set_token_pin(hr, pin, false);
371         if (r < 0)
372                 return log_error_errno(r, "Failed to store security token PIN: %m");
373 
374         return 1;
375 }
376 
handle_generic_user_record_error(const char * user_name,UserRecord * hr,const sd_bus_error * error,int ret,bool emphasize_current_password)377 static int handle_generic_user_record_error(
378                 const char *user_name,
379                 UserRecord *hr,
380                 const sd_bus_error *error,
381                 int ret,
382                 bool emphasize_current_password) {
383         int r;
384 
385         assert(user_name);
386         assert(hr);
387 
388         if (sd_bus_error_has_name(error, BUS_ERROR_HOME_ABSENT))
389                 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE),
390                                        "Home of user %s is currently absent, please plug in the necessary storage device or backing file system.", user_name);
391 
392         else if (sd_bus_error_has_name(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT))
393                 return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS),
394                                        "Too frequent login attempts for user %s, try again later.", user_name);
395 
396         else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD)) {
397 
398                 if (!strv_isempty(hr->password))
399                         log_notice("Password incorrect or not sufficient, please try again.");
400 
401                 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
402                  * let's push what we acquire here into the cache */
403                 r = acquire_existing_password(
404                                 user_name,
405                                 hr,
406                                 emphasize_current_password,
407                                 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
408                 if (r < 0)
409                         return r;
410 
411         } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_RECOVERY_KEY)) {
412 
413                 if (!strv_isempty(hr->password))
414                         log_notice("Recovery key incorrect or not sufficient, please try again.");
415 
416                 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
417                  * let's push what we acquire here into the cache */
418                 r = acquire_recovery_key(
419                                 user_name,
420                                 hr,
421                                 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
422                 if (r < 0)
423                         return r;
424 
425         } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) {
426 
427                 if (strv_isempty(hr->password))
428                         log_notice("Security token not inserted, please enter password.");
429                 else
430                         log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
431 
432                 r = acquire_existing_password(
433                                 user_name,
434                                 hr,
435                                 emphasize_current_password,
436                                 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
437                 if (r < 0)
438                         return r;
439 
440         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
441 
442                 /* First time the PIN is requested, let's accept cached data, and allow using credential store */
443                 r = acquire_token_pin(
444                                 user_name,
445                                 hr,
446                                 ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_PUSH_CACHE);
447                 if (r < 0)
448                         return r;
449 
450         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) {
451 
452                 log_notice("%s%sPlease authenticate physically on security token.",
453                            emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
454                            emoji_enabled() ? " " : "");
455 
456                 r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, true);
457                 if (r < 0)
458                         return log_error_errno(r, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
459 
460         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
461 
462                 log_notice("%s%sPlease confirm presence on security token.",
463                            emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
464                            emoji_enabled() ? " " : "");
465 
466                 r = user_record_set_fido2_user_presence_permitted(hr, true);
467                 if (r < 0)
468                         return log_error_errno(r, "Failed to set FIDO2 user presence permitted flag: %m");
469 
470         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED)) {
471 
472                 log_notice("%s%sPlease verify user on security token.",
473                            emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
474                            emoji_enabled() ? " " : "");
475 
476                 r = user_record_set_fido2_user_verification_permitted(hr, true);
477                 if (r < 0)
478                         return log_error_errno(r, "Failed to set FIDO2 user verification permitted flag: %m");
479 
480         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED))
481                 return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
482 
483         else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
484 
485                 log_notice("Security token PIN incorrect, please try again.");
486 
487                 /* If the previous PIN was wrong don't accept cached info anymore, but add to cache. Also, don't use the credential data */
488                 r = acquire_token_pin(
489                                 user_name,
490                                 hr,
491                                 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
492                 if (r < 0)
493                         return r;
494 
495         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT)) {
496 
497                 log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
498 
499                 r = acquire_token_pin(
500                                 user_name,
501                                 hr,
502                                 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
503                 if (r < 0)
504                         return r;
505 
506         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT)) {
507 
508                 log_notice("Security token PIN incorrect, please try again (only one try left!).");
509 
510                 r = acquire_token_pin(
511                                 user_name,
512                                 hr,
513                                 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
514                 if (r < 0)
515                         return r;
516         } else
517                 return log_error_errno(ret, "Operation on home %s failed: %s", user_name, bus_error_message(error, ret));
518 
519         return 0;
520 }
521 
acquire_passed_secrets(const char * user_name,UserRecord ** ret)522 static int acquire_passed_secrets(const char *user_name, UserRecord **ret) {
523         _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
524         int r;
525 
526         assert(ret);
527 
528         /* Generates an initial secret objects that contains passwords supplied via $PASSWORD, the password
529          * cache or the credentials subsystem, but excluding any interactive stuff. If nothing is passed,
530          * returns an empty secret object. */
531 
532         secret = user_record_new();
533         if (!secret)
534                 return log_oom();
535 
536         r = acquire_existing_password(
537                         user_name,
538                         secret,
539                         /* emphasize_current_password = */ false,
540                         ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_NO_TTY | ASK_PASSWORD_NO_AGENT);
541         if (r < 0)
542                 return r;
543 
544         r = acquire_token_pin(
545                         user_name,
546                         secret,
547                         ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_NO_TTY | ASK_PASSWORD_NO_AGENT);
548         if (r < 0)
549                 return r;
550 
551         r = acquire_recovery_key(
552                         user_name,
553                         secret,
554                         ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_NO_TTY | ASK_PASSWORD_NO_AGENT);
555         if (r < 0)
556                 return r;
557 
558         *ret = TAKE_PTR(secret);
559         return 0;
560 }
561 
activate_home(int argc,char * argv[],void * userdata)562 static int activate_home(int argc, char *argv[], void *userdata) {
563         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
564         int r, ret = 0;
565 
566         r = acquire_bus(&bus);
567         if (r < 0)
568                 return r;
569 
570         STRV_FOREACH(i, strv_skip(argv, 1)) {
571                 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
572 
573                 r = acquire_passed_secrets(*i, &secret);
574                 if (r < 0)
575                         return r;
576 
577                 for (;;) {
578                         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
579                         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
580 
581                         r = bus_message_new_method_call(bus, &m, bus_mgr, "ActivateHome");
582                         if (r < 0)
583                                 return bus_log_create_error(r);
584 
585                         r = sd_bus_message_append(m, "s", *i);
586                         if (r < 0)
587                                 return bus_log_create_error(r);
588 
589                         r = bus_message_append_secret(m, secret);
590                         if (r < 0)
591                                 return bus_log_create_error(r);
592 
593                         r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
594                         if (r < 0) {
595                                 r = handle_generic_user_record_error(*i, secret, &error, r, /* emphasize_current_password= */ false);
596                                 if (r < 0) {
597                                         if (ret == 0)
598                                                 ret = r;
599 
600                                         break;
601                                 }
602                         } else
603                                 break;
604                 }
605         }
606 
607         return ret;
608 }
609 
deactivate_home(int argc,char * argv[],void * userdata)610 static int deactivate_home(int argc, char *argv[], void *userdata) {
611         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
612         int r, ret = 0;
613 
614         r = acquire_bus(&bus);
615         if (r < 0)
616                 return r;
617 
618         STRV_FOREACH(i, strv_skip(argv, 1)) {
619                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
620                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
621 
622                 r = bus_message_new_method_call(bus, &m, bus_mgr, "DeactivateHome");
623                 if (r < 0)
624                         return bus_log_create_error(r);
625 
626                 r = sd_bus_message_append(m, "s", *i);
627                 if (r < 0)
628                         return bus_log_create_error(r);
629 
630                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
631                 if (r < 0) {
632                         log_error_errno(r, "Failed to deactivate user home: %s", bus_error_message(&error, r));
633                         if (ret == 0)
634                                 ret = r;
635                 }
636         }
637 
638         return ret;
639 }
640 
dump_home_record(UserRecord * hr)641 static void dump_home_record(UserRecord *hr) {
642         int r;
643 
644         assert(hr);
645 
646         if (hr->incomplete) {
647                 fflush(stdout);
648                 log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr->user_name);
649         }
650 
651         if (arg_json_format_flags & JSON_FORMAT_OFF)
652                 user_record_show(hr, true);
653         else {
654                 _cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
655 
656                 if (arg_export_format == EXPORT_FORMAT_STRIPPED)
657                         r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED|USER_RECORD_PERMISSIVE, &stripped);
658                 else if (arg_export_format == EXPORT_FORMAT_MINIMAL)
659                         r = user_record_clone(hr, USER_RECORD_EXTRACT_SIGNABLE|USER_RECORD_PERMISSIVE, &stripped);
660                 else
661                         r = 0;
662                 if (r < 0)
663                         log_warning_errno(r, "Failed to strip user record, ignoring: %m");
664                 if (stripped)
665                         hr = stripped;
666 
667                 json_variant_dump(hr->json, arg_json_format_flags, stdout, NULL);
668         }
669 }
670 
mangle_user_list(char ** list,char *** ret_allocated)671 static char **mangle_user_list(char **list, char ***ret_allocated) {
672         _cleanup_free_ char *myself = NULL;
673         char **l;
674 
675         if (!strv_isempty(list)) {
676                 *ret_allocated = NULL;
677                 return list;
678         }
679 
680         myself = getusername_malloc();
681         if (!myself)
682                 return NULL;
683 
684         l = new(char*, 2);
685         if (!l)
686                 return NULL;
687 
688         l[0] = TAKE_PTR(myself);
689         l[1] = NULL;
690 
691         *ret_allocated = l;
692         return l;
693 }
694 
inspect_home(int argc,char * argv[],void * userdata)695 static int inspect_home(int argc, char *argv[], void *userdata) {
696         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
697         _cleanup_(strv_freep) char **mangled_list = NULL;
698         int r, ret = 0;
699         char **items;
700 
701         pager_open(arg_pager_flags);
702 
703         r = acquire_bus(&bus);
704         if (r < 0)
705                 return r;
706 
707         items = mangle_user_list(strv_skip(argv, 1), &mangled_list);
708         if (!items)
709                 return log_oom();
710 
711         STRV_FOREACH(i, items) {
712                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
713                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
714                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
715                 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
716                 const char *json;
717                 int incomplete;
718                 uid_t uid;
719 
720                 r = parse_uid(*i, &uid);
721                 if (r < 0) {
722                         if (!valid_user_group_name(*i, 0)) {
723                                 log_error("Invalid user name '%s'.", *i);
724                                 if (ret == 0)
725                                         ret = -EINVAL;
726 
727                                 continue;
728                         }
729 
730                         r = bus_call_method(bus, bus_mgr, "GetUserRecordByName", &error, &reply, "s", *i);
731                 } else
732                         r = bus_call_method(bus, bus_mgr, "GetUserRecordByUID", &error, &reply, "u", (uint32_t) uid);
733 
734                 if (r < 0) {
735                         log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
736                         if (ret == 0)
737                                 ret = r;
738 
739                         continue;
740                 }
741 
742                 r = sd_bus_message_read(reply, "sbo", &json, &incomplete, NULL);
743                 if (r < 0) {
744                         bus_log_parse_error(r);
745                         if (ret == 0)
746                                 ret = r;
747 
748                         continue;
749                 }
750 
751                 r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
752                 if (r < 0) {
753                         log_error_errno(r, "Failed to parse JSON identity: %m");
754                         if (ret == 0)
755                                 ret = r;
756 
757                         continue;
758                 }
759 
760                 hr = user_record_new();
761                 if (!hr)
762                         return log_oom();
763 
764                 r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG|USER_RECORD_PERMISSIVE);
765                 if (r < 0) {
766                         if (ret == 0)
767                                 ret = r;
768 
769                         continue;
770                 }
771 
772                 hr->incomplete = incomplete;
773                 dump_home_record(hr);
774         }
775 
776         return ret;
777 }
778 
authenticate_home(int argc,char * argv[],void * userdata)779 static int authenticate_home(int argc, char *argv[], void *userdata) {
780         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
781         _cleanup_(strv_freep) char **mangled_list = NULL;
782         int r, ret = 0;
783         char **items;
784 
785         items = mangle_user_list(strv_skip(argv, 1), &mangled_list);
786         if (!items)
787                 return log_oom();
788 
789         r = acquire_bus(&bus);
790         if (r < 0)
791                 return r;
792 
793         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
794 
795         STRV_FOREACH(i, items) {
796                 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
797 
798                 r = acquire_passed_secrets(*i, &secret);
799                 if (r < 0)
800                         return r;
801 
802                 for (;;) {
803                         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
804                         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
805 
806                         r = bus_message_new_method_call(bus, &m, bus_mgr, "AuthenticateHome");
807                         if (r < 0)
808                                 return bus_log_create_error(r);
809 
810                         r = sd_bus_message_append(m, "s", *i);
811                         if (r < 0)
812                                 return bus_log_create_error(r);
813 
814                         r = bus_message_append_secret(m, secret);
815                         if (r < 0)
816                                 return bus_log_create_error(r);
817 
818                         r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
819                         if (r < 0) {
820                                 r = handle_generic_user_record_error(*i, secret, &error, r, false);
821                                 if (r < 0) {
822                                         if (ret == 0)
823                                                 ret = r;
824 
825                                         break;
826                                 }
827                         } else
828                                 break;
829                 }
830         }
831 
832         return ret;
833 }
834 
update_last_change(JsonVariant ** v,bool with_password,bool override)835 static int update_last_change(JsonVariant **v, bool with_password, bool override) {
836         JsonVariant *c;
837         usec_t n;
838         int r;
839 
840         assert(v);
841 
842         n = now(CLOCK_REALTIME);
843 
844         c = json_variant_by_key(*v, "lastChangeUSec");
845         if (c) {
846                 uint64_t u;
847 
848                 if (!override)
849                         goto update_password;
850 
851                 if (!json_variant_is_unsigned(c))
852                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastChangeUSec field is not an unsigned integer, refusing.");
853 
854                 u = json_variant_unsigned(c);
855                 if (u >= n)
856                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastChangeUSec is from the future, can't update.");
857         }
858 
859         r = json_variant_set_field_unsigned(v, "lastChangeUSec", n);
860         if (r < 0)
861                 return log_error_errno(r, "Failed to update lastChangeUSec: %m");
862 
863 update_password:
864         if (!with_password)
865                 return 0;
866 
867         c = json_variant_by_key(*v, "lastPasswordChangeUSec");
868         if (c) {
869                 uint64_t u;
870 
871                 if (!override)
872                         return 0;
873 
874                 if (!json_variant_is_unsigned(c))
875                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastPasswordChangeUSec field is not an unsigned integer, refusing.");
876 
877                 u = json_variant_unsigned(c);
878                 if (u >= n)
879                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastPasswordChangeUSec is from the future, can't update.");
880         }
881 
882         r = json_variant_set_field_unsigned(v, "lastPasswordChangeUSec", n);
883         if (r < 0)
884                 return log_error_errno(r, "Failed to update lastPasswordChangeUSec: %m");
885 
886         return 1;
887 }
888 
apply_identity_changes(JsonVariant ** _v)889 static int apply_identity_changes(JsonVariant **_v) {
890         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
891         int r;
892 
893         assert(_v);
894 
895         v = json_variant_ref(*_v);
896 
897         r = json_variant_filter(&v, arg_identity_filter);
898         if (r < 0)
899                 return log_error_errno(r, "Failed to filter identity: %m");
900 
901         r = json_variant_merge(&v, arg_identity_extra);
902         if (r < 0)
903                 return log_error_errno(r, "Failed to merge identities: %m");
904 
905         if (arg_identity_extra_this_machine || !strv_isempty(arg_identity_filter)) {
906                 _cleanup_(json_variant_unrefp) JsonVariant *per_machine = NULL, *mmid = NULL;
907                 sd_id128_t mid;
908 
909                 r = sd_id128_get_machine(&mid);
910                 if (r < 0)
911                         return log_error_errno(r, "Failed to acquire machine ID: %m");
912 
913                 r = json_variant_new_string(&mmid, SD_ID128_TO_STRING(mid));
914                 if (r < 0)
915                         return log_error_errno(r, "Failed to allocate matchMachineId object: %m");
916 
917                 per_machine = json_variant_ref(json_variant_by_key(v, "perMachine"));
918                 if (per_machine) {
919                         _cleanup_(json_variant_unrefp) JsonVariant *npm = NULL, *add = NULL;
920                         _cleanup_free_ JsonVariant **array = NULL;
921                         JsonVariant *z;
922                         size_t i = 0;
923 
924                         if (!json_variant_is_array(per_machine))
925                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "perMachine field is not an array, refusing.");
926 
927                         array = new(JsonVariant*, json_variant_elements(per_machine) + 1);
928                         if (!array)
929                                 return log_oom();
930 
931                         JSON_VARIANT_ARRAY_FOREACH(z, per_machine) {
932                                 JsonVariant *u;
933 
934                                 if (!json_variant_is_object(z))
935                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "perMachine entry is not an object, refusing.");
936 
937                                 array[i++] = z;
938 
939                                 u = json_variant_by_key(z, "matchMachineId");
940                                 if (!u)
941                                         continue;
942 
943                                 if (!json_variant_equal(u, mmid))
944                                         continue;
945 
946                                 r = json_variant_merge(&add, z);
947                                 if (r < 0)
948                                         return log_error_errno(r, "Failed to merge perMachine entry: %m");
949 
950                                 i--;
951                         }
952 
953                         r = json_variant_filter(&add, arg_identity_filter);
954                         if (r < 0)
955                                 return log_error_errno(r, "Failed to filter perMachine: %m");
956 
957                         r = json_variant_merge(&add, arg_identity_extra_this_machine);
958                         if (r < 0)
959                                 return log_error_errno(r, "Failed to merge in perMachine fields: %m");
960 
961                         if (arg_identity_filter_rlimits || arg_identity_extra_rlimits) {
962                                 _cleanup_(json_variant_unrefp) JsonVariant *rlv = NULL;
963 
964                                 rlv = json_variant_ref(json_variant_by_key(add, "resourceLimits"));
965 
966                                 r = json_variant_filter(&rlv, arg_identity_filter_rlimits);
967                                 if (r < 0)
968                                         return log_error_errno(r, "Failed to filter resource limits: %m");
969 
970                                 r = json_variant_merge(&rlv, arg_identity_extra_rlimits);
971                                 if (r < 0)
972                                         return log_error_errno(r, "Failed to set resource limits: %m");
973 
974                                 if (json_variant_is_blank_object(rlv)) {
975                                         r = json_variant_filter(&add, STRV_MAKE("resourceLimits"));
976                                         if (r < 0)
977                                                 return log_error_errno(r, "Failed to drop resource limits field from identity: %m");
978                                 } else {
979                                         r = json_variant_set_field(&add, "resourceLimits", rlv);
980                                         if (r < 0)
981                                                 return log_error_errno(r, "Failed to update resource limits of identity: %m");
982                                 }
983                         }
984 
985                         if (!json_variant_is_blank_object(add)) {
986                                 r = json_variant_set_field(&add, "matchMachineId", mmid);
987                                 if (r < 0)
988                                         return log_error_errno(r, "Failed to set matchMachineId field: %m");
989 
990                                 array[i++] = add;
991                         }
992 
993                         r = json_variant_new_array(&npm, array, i);
994                         if (r < 0)
995                                 return log_error_errno(r, "Failed to allocate new perMachine array: %m");
996 
997                         json_variant_unref(per_machine);
998                         per_machine = TAKE_PTR(npm);
999                 } else {
1000                         _cleanup_(json_variant_unrefp) JsonVariant *item = json_variant_ref(arg_identity_extra_this_machine);
1001 
1002                         if (arg_identity_extra_rlimits) {
1003                                 r = json_variant_set_field(&item, "resourceLimits", arg_identity_extra_rlimits);
1004                                 if (r < 0)
1005                                         return log_error_errno(r, "Failed to update resource limits of identity: %m");
1006                         }
1007 
1008                         r = json_variant_set_field(&item, "matchMachineId", mmid);
1009                         if (r < 0)
1010                                 return log_error_errno(r, "Failed to set matchMachineId field: %m");
1011 
1012                         r = json_variant_append_array(&per_machine, item);
1013                         if (r < 0)
1014                                 return log_error_errno(r, "Failed to append to perMachine array: %m");
1015                 }
1016 
1017                 r = json_variant_set_field(&v, "perMachine", per_machine);
1018                 if (r < 0)
1019                         return log_error_errno(r, "Failed to update per machine record: %m");
1020         }
1021 
1022         if (arg_identity_extra_privileged || arg_identity_filter) {
1023                 _cleanup_(json_variant_unrefp) JsonVariant *privileged = NULL;
1024 
1025                 privileged = json_variant_ref(json_variant_by_key(v, "privileged"));
1026 
1027                 r = json_variant_filter(&privileged, arg_identity_filter);
1028                 if (r < 0)
1029                         return log_error_errno(r, "Failed to filter identity (privileged part): %m");
1030 
1031                 r = json_variant_merge(&privileged, arg_identity_extra_privileged);
1032                 if (r < 0)
1033                         return log_error_errno(r, "Failed to merge identities (privileged part): %m");
1034 
1035                 if (json_variant_is_blank_object(privileged)) {
1036                         r = json_variant_filter(&v, STRV_MAKE("privileged"));
1037                         if (r < 0)
1038                                 return log_error_errno(r, "Failed to drop privileged part from identity: %m");
1039                 } else {
1040                         r = json_variant_set_field(&v, "privileged", privileged);
1041                         if (r < 0)
1042                                 return log_error_errno(r, "Failed to update privileged part of identity: %m");
1043                 }
1044         }
1045 
1046         if (arg_identity_filter_rlimits) {
1047                 _cleanup_(json_variant_unrefp) JsonVariant *rlv = NULL;
1048 
1049                 rlv = json_variant_ref(json_variant_by_key(v, "resourceLimits"));
1050 
1051                 r = json_variant_filter(&rlv, arg_identity_filter_rlimits);
1052                 if (r < 0)
1053                         return log_error_errno(r, "Failed to filter resource limits: %m");
1054 
1055                 /* Note that we only filter resource limits here, but don't apply them. We do that in the perMachine section */
1056 
1057                 if (json_variant_is_blank_object(rlv)) {
1058                         r = json_variant_filter(&v, STRV_MAKE("resourceLimits"));
1059                         if (r < 0)
1060                                 return log_error_errno(r, "Failed to drop resource limits field from identity: %m");
1061                 } else {
1062                         r = json_variant_set_field(&v, "resourceLimits", rlv);
1063                         if (r < 0)
1064                                 return log_error_errno(r, "Failed to update resource limits of identity: %m");
1065                 }
1066         }
1067 
1068         json_variant_unref(*_v);
1069         *_v = TAKE_PTR(v);
1070 
1071         return 0;
1072 }
1073 
add_disposition(JsonVariant ** v)1074 static int add_disposition(JsonVariant **v) {
1075         int r;
1076 
1077         assert(v);
1078 
1079         if (json_variant_by_key(*v, "disposition"))
1080                 return 0;
1081 
1082         /* Set the disposition to regular, if not configured explicitly */
1083         r = json_variant_set_field_string(v, "disposition", "regular");
1084         if (r < 0)
1085                 return log_error_errno(r, "Failed to set disposition field: %m");
1086 
1087         return 1;
1088 }
1089 
acquire_new_home_record(UserRecord ** ret)1090 static int acquire_new_home_record(UserRecord **ret) {
1091         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1092         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
1093         int r;
1094 
1095         assert(ret);
1096 
1097         if (arg_identity) {
1098                 unsigned line, column;
1099 
1100                 r = json_parse_file(
1101                                 streq(arg_identity, "-") ? stdin : NULL,
1102                                 streq(arg_identity, "-") ? "<stdin>" : arg_identity, JSON_PARSE_SENSITIVE, &v, &line, &column);
1103                 if (r < 0)
1104                         return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
1105         }
1106 
1107         r = apply_identity_changes(&v);
1108         if (r < 0)
1109                 return r;
1110 
1111         r = add_disposition(&v);
1112         if (r < 0)
1113                 return r;
1114 
1115         STRV_FOREACH(i, arg_pkcs11_token_uri) {
1116                 r = identity_add_pkcs11_key_data(&v, *i);
1117                 if (r < 0)
1118                         return r;
1119         }
1120 
1121         STRV_FOREACH(i, arg_fido2_device) {
1122                 r = identity_add_fido2_parameters(&v, *i, arg_fido2_lock_with, arg_fido2_cred_alg);
1123                 if (r < 0)
1124                         return r;
1125         }
1126 
1127         if (arg_recovery_key) {
1128                 r = identity_add_recovery_key(&v);
1129                 if (r < 0)
1130                         return r;
1131         }
1132 
1133         r = update_last_change(&v, true, false);
1134         if (r < 0)
1135                 return r;
1136 
1137         if (DEBUG_LOGGING)
1138                 json_variant_dump(v, JSON_FORMAT_PRETTY, NULL, NULL);
1139 
1140         hr = user_record_new();
1141         if (!hr)
1142                 return log_oom();
1143 
1144         r = user_record_load(hr, v, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG|USER_RECORD_PERMISSIVE);
1145         if (r < 0)
1146                 return r;
1147 
1148         *ret = TAKE_PTR(hr);
1149         return 0;
1150 }
1151 
acquire_new_password(const char * user_name,UserRecord * hr,bool suggest,char ** ret)1152 static int acquire_new_password(
1153                 const char *user_name,
1154                 UserRecord *hr,
1155                 bool suggest,
1156                 char **ret) {
1157 
1158         _cleanup_(erase_and_freep) char *envpw = NULL;
1159         unsigned i = 5;
1160         int r;
1161 
1162         assert(user_name);
1163         assert(hr);
1164 
1165         r = getenv_steal_erase("NEWPASSWORD", &envpw);
1166         if (r < 0)
1167                 return log_error_errno(r, "Failed to acquire password from environment: %m");
1168         if (r > 0) {
1169                 /* As above, this is not for use, just for testing */
1170 
1171                 r = user_record_set_password(hr, STRV_MAKE(envpw), /* prepend = */ true);
1172                 if (r < 0)
1173                         return log_error_errno(r, "Failed to store password: %m");
1174 
1175                 if (ret)
1176                         *ret = TAKE_PTR(envpw);
1177 
1178                 return 0;
1179         }
1180 
1181         if (suggest)
1182                 (void) suggest_passwords();
1183 
1184         for (;;) {
1185                 _cleanup_(strv_free_erasep) char **first = NULL, **second = NULL;
1186                 _cleanup_free_ char *question = NULL;
1187 
1188                 if (--i == 0)
1189                         return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up:");
1190 
1191                 if (asprintf(&question, "Please enter new password for user %s:", user_name) < 0)
1192                         return log_oom();
1193 
1194                 r = ask_password_auto(
1195                                 question,
1196                                 /* icon= */ "user-home",
1197                                 NULL,
1198                                 /* key_name= */ "home-password",
1199                                 /* credential_name= */ "home.new-password",
1200                                 USEC_INFINITY,
1201                                 0, /* no caching, we want to collect a new password here after all */
1202                                 &first);
1203                 if (r < 0)
1204                         return log_error_errno(r, "Failed to acquire password: %m");
1205 
1206                 question = mfree(question);
1207                 if (asprintf(&question, "Please enter new password for user %s (repeat):", user_name) < 0)
1208                         return log_oom();
1209 
1210                 r = ask_password_auto(
1211                                 question,
1212                                 /* icon= */ "user-home",
1213                                 NULL,
1214                                 /* key_name= */ "home-password",
1215                                 /* credential_name= */ "home.new-password",
1216                                 USEC_INFINITY,
1217                                 0, /* no caching */
1218                                 &second);
1219                 if (r < 0)
1220                         return log_error_errno(r, "Failed to acquire password: %m");
1221 
1222                 if (strv_equal(first, second)) {
1223                         _cleanup_(erase_and_freep) char *copy = NULL;
1224 
1225                         if (ret) {
1226                                 copy = strdup(first[0]);
1227                                 if (!copy)
1228                                         return log_oom();
1229                         }
1230 
1231                         r = user_record_set_password(hr, first, /* prepend = */ true);
1232                         if (r < 0)
1233                                 return log_error_errno(r, "Failed to store password: %m");
1234 
1235                         if (ret)
1236                                 *ret = TAKE_PTR(copy);
1237 
1238                         return 0;
1239                 }
1240 
1241                 log_error("Password didn't match, try again.");
1242         }
1243 }
1244 
create_home(int argc,char * argv[],void * userdata)1245 static int create_home(int argc, char *argv[], void *userdata) {
1246         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1247         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
1248         int r;
1249 
1250         r = acquire_bus(&bus);
1251         if (r < 0)
1252                 return r;
1253 
1254         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1255 
1256         if (argc >= 2) {
1257                 /* If a username was specified, use it */
1258 
1259                 if (valid_user_group_name(argv[1], 0))
1260                         r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]);
1261                 else {
1262                         _cleanup_free_ char *un = NULL, *rr = NULL;
1263 
1264                         /* Before we consider the user name invalid, let's check if we can split it? */
1265                         r = split_user_name_realm(argv[1], &un, &rr);
1266                         if (r < 0)
1267                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]);
1268 
1269                         if (rr) {
1270                                 r = json_variant_set_field_string(&arg_identity_extra, "realm", rr);
1271                                 if (r < 0)
1272                                         return log_error_errno(r, "Failed to set realm field: %m");
1273                         }
1274 
1275                         r = json_variant_set_field_string(&arg_identity_extra, "userName", un);
1276                 }
1277                 if (r < 0)
1278                         return log_error_errno(r, "Failed to set userName field: %m");
1279         } else {
1280                 /* If neither a username nor an identity have been specified we cannot operate. */
1281                 if (!arg_identity)
1282                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required.");
1283         }
1284 
1285         r = acquire_new_home_record(&hr);
1286         if (r < 0)
1287                 return r;
1288 
1289         /* If the JSON record carries no plain text password (besides the recovery key), then let's query it
1290          * manually. */
1291         if (strv_length(hr->password) <= arg_recovery_key) {
1292 
1293                 if (strv_isempty(hr->hashed_password)) {
1294                         _cleanup_(erase_and_freep) char *new_password = NULL;
1295 
1296                         /* No regular (i.e. non-PKCS#11) hashed passwords set in the record, let's fix that. */
1297                         r = acquire_new_password(hr->user_name, hr, /* suggest = */ true, &new_password);
1298                         if (r < 0)
1299                                 return r;
1300 
1301                         r = user_record_make_hashed_password(hr, STRV_MAKE(new_password), /* extend = */ false);
1302                         if (r < 0)
1303                                 return log_error_errno(r, "Failed to hash password: %m");
1304                 } else {
1305                         /* There's a hash password set in the record, acquire the unhashed version of it. */
1306                         r = acquire_existing_password(
1307                                         hr->user_name,
1308                                         hr,
1309                                         /* emphasize_current= */ false,
1310                                         ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_PUSH_CACHE);
1311                         if (r < 0)
1312                                 return r;
1313                 }
1314         }
1315 
1316         if (hr->enforce_password_policy == 0) {
1317                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1318 
1319                 /* If password quality enforcement is disabled, let's at least warn client side */
1320 
1321                 r = user_record_quality_check_password(hr, hr, &error);
1322                 if (r < 0)
1323                         log_warning_errno(r, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error, r));
1324         }
1325 
1326         for (;;) {
1327                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1328                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1329                 _cleanup_(erase_and_freep) char *formatted = NULL;
1330 
1331                 r = json_variant_format(hr->json, 0, &formatted);
1332                 if (r < 0)
1333                         return log_error_errno(r, "Failed to format user record: %m");
1334 
1335                 r = bus_message_new_method_call(bus, &m, bus_mgr, "CreateHome");
1336                 if (r < 0)
1337                         return bus_log_create_error(r);
1338 
1339                 (void) sd_bus_message_sensitive(m);
1340 
1341                 r = sd_bus_message_append(m, "s", formatted);
1342                 if (r < 0)
1343                         return bus_log_create_error(r);
1344 
1345                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1346                 if (r < 0) {
1347                         if (sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY)) {
1348                                 _cleanup_(erase_and_freep) char *new_password = NULL;
1349 
1350                                 log_error_errno(r, "%s", bus_error_message(&error, r));
1351                                 log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
1352 
1353                                 r = acquire_new_password(hr->user_name, hr, /* suggest = */ false, &new_password);
1354                                 if (r < 0)
1355                                         return r;
1356 
1357                                 r = user_record_make_hashed_password(hr, STRV_MAKE(new_password), /* extend = */ false);
1358                                 if (r < 0)
1359                                         return log_error_errno(r, "Failed to hash passwords: %m");
1360                         } else {
1361                                 r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
1362                                 if (r < 0)
1363                                         return r;
1364                         }
1365                 } else
1366                         break; /* done */
1367         }
1368 
1369         return 0;
1370 }
1371 
remove_home(int argc,char * argv[],void * userdata)1372 static int remove_home(int argc, char *argv[], void *userdata) {
1373         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1374         int r, ret = 0;
1375 
1376         r = acquire_bus(&bus);
1377         if (r < 0)
1378                 return r;
1379 
1380         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1381 
1382         STRV_FOREACH(i, strv_skip(argv, 1)) {
1383                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1384                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1385 
1386                 r = bus_message_new_method_call(bus, &m, bus_mgr, "RemoveHome");
1387                 if (r < 0)
1388                         return bus_log_create_error(r);
1389 
1390                 r = sd_bus_message_append(m, "s", *i);
1391                 if (r < 0)
1392                         return bus_log_create_error(r);
1393 
1394                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1395                 if (r < 0) {
1396                         log_error_errno(r, "Failed to remove home: %s", bus_error_message(&error, r));
1397                         if (ret == 0)
1398                                 ret = r;
1399                 }
1400         }
1401 
1402         return ret;
1403 }
1404 
acquire_updated_home_record(sd_bus * bus,const char * username,UserRecord ** ret)1405 static int acquire_updated_home_record(
1406                 sd_bus *bus,
1407                 const char *username,
1408                 UserRecord **ret) {
1409 
1410         _cleanup_(json_variant_unrefp) JsonVariant *json = NULL;
1411         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
1412         int r;
1413 
1414         assert(ret);
1415 
1416         if (arg_identity) {
1417                 unsigned line, column;
1418                 JsonVariant *un;
1419 
1420                 r = json_parse_file(
1421                                 streq(arg_identity, "-") ? stdin : NULL,
1422                                 streq(arg_identity, "-") ? "<stdin>" : arg_identity, JSON_PARSE_SENSITIVE, &json, &line, &column);
1423                 if (r < 0)
1424                         return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
1425 
1426                 un = json_variant_by_key(json, "userName");
1427                 if (un) {
1428                         if (!json_variant_is_string(un) || (username && !streq(json_variant_string(un), username)))
1429                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name specified on command line and in JSON record do not match.");
1430                 } else {
1431                         if (!username)
1432                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No username specified.");
1433 
1434                         r = json_variant_set_field_string(&arg_identity_extra, "userName", username);
1435                         if (r < 0)
1436                                 return log_error_errno(r, "Failed to set userName field: %m");
1437                 }
1438 
1439         } else {
1440                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1441                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1442                 int incomplete;
1443                 const char *text;
1444 
1445                 if (!identity_properties_specified())
1446                         return log_error_errno(SYNTHETIC_ERRNO(EALREADY), "No field to change specified.");
1447 
1448                 r = bus_call_method(bus, bus_mgr, "GetUserRecordByName", &error, &reply, "s", username);
1449                 if (r < 0)
1450                         return log_error_errno(r, "Failed to acquire user home record: %s", bus_error_message(&error, r));
1451 
1452                 r = sd_bus_message_read(reply, "sbo", &text, &incomplete, NULL);
1453                 if (r < 0)
1454                         return bus_log_parse_error(r);
1455 
1456                 if (incomplete)
1457                         return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Lacking rights to acquire user record including privileged metadata, can't update record.");
1458 
1459                 r = json_parse(text, JSON_PARSE_SENSITIVE, &json, NULL, NULL);
1460                 if (r < 0)
1461                         return log_error_errno(r, "Failed to parse JSON identity: %m");
1462 
1463                 reply = sd_bus_message_unref(reply);
1464 
1465                 r = json_variant_filter(&json, STRV_MAKE("binding", "status", "signature"));
1466                 if (r < 0)
1467                         return log_error_errno(r, "Failed to strip binding and status from record to update: %m");
1468         }
1469 
1470         r = apply_identity_changes(&json);
1471         if (r < 0)
1472                 return r;
1473 
1474         STRV_FOREACH(i, arg_pkcs11_token_uri) {
1475                 r = identity_add_pkcs11_key_data(&json, *i);
1476                 if (r < 0)
1477                         return r;
1478         }
1479 
1480         STRV_FOREACH(i, arg_fido2_device) {
1481                 r = identity_add_fido2_parameters(&json, *i, arg_fido2_lock_with, arg_fido2_cred_alg);
1482                 if (r < 0)
1483                         return r;
1484         }
1485 
1486         /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
1487          * override. */
1488         r = update_last_change(&json, arg_pkcs11_token_uri || arg_fido2_device, !arg_identity);
1489         if (r < 0)
1490                 return r;
1491 
1492         if (DEBUG_LOGGING)
1493                 json_variant_dump(json, JSON_FORMAT_PRETTY, NULL, NULL);
1494 
1495         hr = user_record_new();
1496         if (!hr)
1497                 return log_oom();
1498 
1499         r = user_record_load(hr, json, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG|USER_RECORD_PERMISSIVE);
1500         if (r < 0)
1501                 return r;
1502 
1503         *ret = TAKE_PTR(hr);
1504         return 0;
1505 }
1506 
home_record_reset_human_interaction_permission(UserRecord * hr)1507 static int home_record_reset_human_interaction_permission(UserRecord *hr) {
1508         int r;
1509 
1510         assert(hr);
1511 
1512         /* When we execute multiple operations one after the other, let's reset the permission to ask the
1513          * user each time, so that if interaction is necessary we will be told so again and thus can print a
1514          * nice message to the user, telling the user so. */
1515 
1516         r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, -1);
1517         if (r < 0)
1518                 return log_error_errno(r, "Failed to reset PKCS#11 protected authentication path permission flag: %m");
1519 
1520         r = user_record_set_fido2_user_presence_permitted(hr, -1);
1521         if (r < 0)
1522                 return log_error_errno(r, "Failed to reset FIDO2 user presence permission flag: %m");
1523 
1524         r = user_record_set_fido2_user_verification_permitted(hr, -1);
1525         if (r < 0)
1526                 return log_error_errno(r, "Failed to reset FIDO2 user verification permission flag: %m");
1527 
1528         return 0;
1529 }
1530 
update_home(int argc,char * argv[],void * userdata)1531 static int update_home(int argc, char *argv[], void *userdata) {
1532         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1533         _cleanup_(user_record_unrefp) UserRecord *hr = NULL, *secret = NULL;
1534         _cleanup_free_ char *buffer = NULL;
1535         const char *username;
1536         int r;
1537 
1538         if (argc >= 2)
1539                 username = argv[1];
1540         else if (!arg_identity) {
1541                 buffer = getusername_malloc();
1542                 if (!buffer)
1543                         return log_oom();
1544 
1545                 username = buffer;
1546         } else
1547                 username = NULL;
1548 
1549         r = acquire_bus(&bus);
1550         if (r < 0)
1551                 return r;
1552 
1553         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1554 
1555         r = acquire_updated_home_record(bus, username, &hr);
1556         if (r < 0)
1557                 return r;
1558 
1559         /* Add in all secrets we can acquire cheaply */
1560         r = acquire_passed_secrets(username, &secret);
1561         if (r < 0)
1562                 return r;
1563 
1564         r = user_record_merge_secret(hr, secret);
1565         if (r < 0)
1566                 return r;
1567 
1568         /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
1569          * authentication might be confusing. */
1570 
1571         if (arg_and_resize || arg_and_change_password)
1572                 log_info("Updating home directory.");
1573 
1574         for (;;) {
1575                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1576                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1577                 _cleanup_free_ char *formatted = NULL;
1578 
1579                 r = bus_message_new_method_call(bus, &m, bus_mgr, "UpdateHome");
1580                 if (r < 0)
1581                         return bus_log_create_error(r);
1582 
1583                 r = json_variant_format(hr->json, 0, &formatted);
1584                 if (r < 0)
1585                         return log_error_errno(r, "Failed to format user record: %m");
1586 
1587                 (void) sd_bus_message_sensitive(m);
1588 
1589                 r = sd_bus_message_append(m, "s", formatted);
1590                 if (r < 0)
1591                         return bus_log_create_error(r);
1592 
1593                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1594                 if (r < 0) {
1595                         if (arg_and_change_password &&
1596                             sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
1597                                 /* In the generic handler we'd ask for a password in this case, but when
1598                                  * changing passwords that's not sufficient, as we need to acquire all keys
1599                                  * first. */
1600                                 return log_error_errno(r, "Security token not inserted, refusing.");
1601 
1602                         r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
1603                         if (r < 0)
1604                                 return r;
1605                 } else
1606                         break;
1607         }
1608 
1609         if (arg_and_resize)
1610                 log_info("Resizing home.");
1611 
1612         (void) home_record_reset_human_interaction_permission(hr);
1613 
1614         /* Also sync down disk size to underlying LUKS/fscrypt/quota */
1615         while (arg_and_resize) {
1616                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1617                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1618 
1619                 r = bus_message_new_method_call(bus, &m, bus_mgr, "ResizeHome");
1620                 if (r < 0)
1621                         return bus_log_create_error(r);
1622 
1623                 /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
1624                 r = sd_bus_message_append(m, "st", hr->user_name, UINT64_MAX);
1625                 if (r < 0)
1626                         return bus_log_create_error(r);
1627 
1628                 r = bus_message_append_secret(m, hr);
1629                 if (r < 0)
1630                         return bus_log_create_error(r);
1631 
1632                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1633                 if (r < 0) {
1634                         if (arg_and_change_password &&
1635                             sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
1636                                 return log_error_errno(r, "Security token not inserted, refusing.");
1637 
1638                         r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
1639                         if (r < 0)
1640                                 return r;
1641                 } else
1642                         break;
1643         }
1644 
1645         if (arg_and_change_password)
1646                 log_info("Synchronizing passwords and encryption keys.");
1647 
1648         (void) home_record_reset_human_interaction_permission(hr);
1649 
1650         /* Also sync down passwords to underlying LUKS/fscrypt */
1651         while (arg_and_change_password) {
1652                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1653                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1654 
1655                 r = bus_message_new_method_call(bus, &m, bus_mgr, "ChangePasswordHome");
1656                 if (r < 0)
1657                         return bus_log_create_error(r);
1658 
1659                 /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
1660                 r = sd_bus_message_append(m, "ss", hr->user_name, "{}");
1661                 if (r < 0)
1662                         return bus_log_create_error(r);
1663 
1664                 r = bus_message_append_secret(m, hr);
1665                 if (r < 0)
1666                         return bus_log_create_error(r);
1667 
1668                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1669                 if (r < 0) {
1670                         if (sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
1671                                 return log_error_errno(r, "Security token not inserted, refusing.");
1672 
1673                         r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
1674                         if (r < 0)
1675                                 return r;
1676                 } else
1677                         break;
1678         }
1679 
1680         return 0;
1681 }
1682 
passwd_home(int argc,char * argv[],void * userdata)1683 static int passwd_home(int argc, char *argv[], void *userdata) {
1684         _cleanup_(user_record_unrefp) UserRecord *old_secret = NULL, *new_secret = NULL;
1685         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1686         _cleanup_free_ char *buffer = NULL;
1687         const char *username;
1688         int r;
1689 
1690         if (arg_pkcs11_token_uri)
1691                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=…'.");
1692         if (arg_fido2_device)
1693                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "To change the FIDO2 security token use 'homectl update --fido2-device=…'.");
1694         if (identity_properties_specified())
1695                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The 'passwd' verb does not permit changing other record properties at the same time.");
1696 
1697         if (argc >= 2)
1698                 username = argv[1];
1699         else {
1700                 buffer = getusername_malloc();
1701                 if (!buffer)
1702                         return log_oom();
1703 
1704                 username = buffer;
1705         }
1706 
1707         r = acquire_bus(&bus);
1708         if (r < 0)
1709                 return r;
1710 
1711         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1712 
1713         r = acquire_passed_secrets(username, &old_secret);
1714         if (r < 0)
1715                 return r;
1716 
1717         new_secret = user_record_new();
1718         if (!new_secret)
1719                 return log_oom();
1720 
1721         r = acquire_new_password(username, new_secret, /* suggest = */ true, NULL);
1722         if (r < 0)
1723                 return r;
1724 
1725         for (;;) {
1726                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1727                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1728 
1729                 r = bus_message_new_method_call(bus, &m, bus_mgr, "ChangePasswordHome");
1730                 if (r < 0)
1731                         return bus_log_create_error(r);
1732 
1733                 r = sd_bus_message_append(m, "s", username);
1734                 if (r < 0)
1735                         return bus_log_create_error(r);
1736 
1737                 r = bus_message_append_secret(m, new_secret);
1738                 if (r < 0)
1739                         return bus_log_create_error(r);
1740 
1741                 r = bus_message_append_secret(m, old_secret);
1742                 if (r < 0)
1743                         return bus_log_create_error(r);
1744 
1745                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1746                 if (r < 0) {
1747                         if (sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY)) {
1748 
1749                                 log_error_errno(r, "%s", bus_error_message(&error, r));
1750 
1751                                 r = acquire_new_password(username, new_secret, /* suggest = */ false, NULL);
1752 
1753                         } else if (sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
1754 
1755                                 /* In the generic handler we'd ask for a password in this case, but when
1756                                  * changing passwords that's not sufficeint, as we need to acquire all keys
1757                                  * first. */
1758                                 return log_error_errno(r, "Security token not inserted, refusing.");
1759                         else
1760                                 r = handle_generic_user_record_error(username, old_secret, &error, r, true);
1761                         if (r < 0)
1762                                 return r;
1763                 } else
1764                         break;
1765         }
1766 
1767         return 0;
1768 }
1769 
parse_disk_size(const char * t,uint64_t * ret)1770 static int parse_disk_size(const char *t, uint64_t *ret) {
1771         int r;
1772 
1773         assert(t);
1774         assert(ret);
1775 
1776         if (streq(t, "min"))
1777                 *ret = 0;
1778         else if (streq(t, "max"))
1779                 *ret = UINT64_MAX-1;  /* Largest size that isn't UINT64_MAX special marker */
1780         else {
1781                 uint64_t ds;
1782 
1783                 r = parse_size(t, 1024, &ds);
1784                 if (r < 0)
1785                         return log_error_errno(r, "Failed to parse disk size parameter: %s", t);
1786 
1787                 if (ds >= UINT64_MAX) /* UINT64_MAX has special meaning for us ("dont change"), refuse */
1788                         return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Disk size out of range: %s", t);
1789 
1790                 *ret = ds;
1791         }
1792 
1793         return 0;
1794 }
1795 
resize_home(int argc,char * argv[],void * userdata)1796 static int resize_home(int argc, char *argv[], void *userdata) {
1797         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1798         _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
1799         uint64_t ds = UINT64_MAX;
1800         int r;
1801 
1802         r = acquire_bus(&bus);
1803         if (r < 0)
1804                 return r;
1805 
1806         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1807 
1808         if (arg_disk_size_relative != UINT64_MAX ||
1809             (argc > 2 && parse_permyriad(argv[2]) >= 0))
1810                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1811                                                "Relative disk size specification currently not supported when resizing.");
1812 
1813         if (argc > 2) {
1814                 r = parse_disk_size(argv[2], &ds);
1815                 if (r < 0)
1816                         return r;
1817         }
1818 
1819         if (arg_disk_size != UINT64_MAX) {
1820                 if (ds != UINT64_MAX && ds != arg_disk_size)
1821                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Disk size specified twice and doesn't match, refusing.");
1822 
1823                 ds = arg_disk_size;
1824         }
1825 
1826         r = acquire_passed_secrets(argv[1], &secret);
1827         if (r < 0)
1828                 return r;
1829 
1830         for (;;) {
1831                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1832                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1833 
1834                 r = bus_message_new_method_call(bus, &m, bus_mgr, "ResizeHome");
1835                 if (r < 0)
1836                         return bus_log_create_error(r);
1837 
1838                 r = sd_bus_message_append(m, "st", argv[1], ds);
1839                 if (r < 0)
1840                         return bus_log_create_error(r);
1841 
1842                 r = bus_message_append_secret(m, secret);
1843                 if (r < 0)
1844                         return bus_log_create_error(r);
1845 
1846                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1847                 if (r < 0) {
1848                         r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
1849                         if (r < 0)
1850                                 return r;
1851                 } else
1852                         break;
1853         }
1854 
1855         return 0;
1856 }
1857 
lock_home(int argc,char * argv[],void * userdata)1858 static int lock_home(int argc, char *argv[], void *userdata) {
1859         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1860         int r, ret = 0;
1861 
1862         r = acquire_bus(&bus);
1863         if (r < 0)
1864                 return r;
1865 
1866         STRV_FOREACH(i, strv_skip(argv, 1)) {
1867                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1868                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1869 
1870                 r = bus_message_new_method_call(bus, &m, bus_mgr, "LockHome");
1871                 if (r < 0)
1872                         return bus_log_create_error(r);
1873 
1874                 r = sd_bus_message_append(m, "s", *i);
1875                 if (r < 0)
1876                         return bus_log_create_error(r);
1877 
1878                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1879                 if (r < 0) {
1880                         log_error_errno(r, "Failed to lock home: %s", bus_error_message(&error, r));
1881                         if (ret == 0)
1882                                 ret = r;
1883                 }
1884         }
1885 
1886         return ret;
1887 }
1888 
unlock_home(int argc,char * argv[],void * userdata)1889 static int unlock_home(int argc, char *argv[], void *userdata) {
1890         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1891         int r, ret = 0;
1892 
1893         r = acquire_bus(&bus);
1894         if (r < 0)
1895                 return r;
1896 
1897         STRV_FOREACH(i, strv_skip(argv, 1)) {
1898                 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
1899 
1900                 r = acquire_passed_secrets(*i, &secret);
1901                 if (r < 0)
1902                         return r;
1903 
1904                 for (;;) {
1905                         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1906                         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1907 
1908                         r = bus_message_new_method_call(bus, &m, bus_mgr, "UnlockHome");
1909                         if (r < 0)
1910                                 return bus_log_create_error(r);
1911 
1912                         r = sd_bus_message_append(m, "s", *i);
1913                         if (r < 0)
1914                                 return bus_log_create_error(r);
1915 
1916                         r = bus_message_append_secret(m, secret);
1917                         if (r < 0)
1918                                 return bus_log_create_error(r);
1919 
1920                         r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1921                         if (r < 0) {
1922                                 r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
1923                                 if (r < 0) {
1924                                         if (ret == 0)
1925                                                 ret = r;
1926 
1927                                         break;
1928                                 }
1929                         } else
1930                                 break;
1931                 }
1932         }
1933 
1934         return ret;
1935 }
1936 
with_home(int argc,char * argv[],void * userdata)1937 static int with_home(int argc, char *argv[], void *userdata) {
1938         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1939         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
1940         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1941         _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
1942         _cleanup_close_ int acquired_fd = -1;
1943         _cleanup_strv_free_ char **cmdline  = NULL;
1944         const char *home;
1945         int r, ret;
1946         pid_t pid;
1947 
1948         r = acquire_bus(&bus);
1949         if (r < 0)
1950                 return r;
1951 
1952         if (argc < 3) {
1953                 _cleanup_free_ char *shell = NULL;
1954 
1955                 /* If no command is specified, spawn a shell */
1956                 r = get_shell(&shell);
1957                 if (r < 0)
1958                         return log_error_errno(r, "Failed to acquire shell: %m");
1959 
1960                 cmdline = strv_new(shell);
1961         } else
1962                 cmdline = strv_copy(argv + 2);
1963         if (!cmdline)
1964                 return log_oom();
1965 
1966         r = acquire_passed_secrets(argv[1], &secret);
1967         if (r < 0)
1968                 return r;
1969 
1970         for (;;) {
1971                 r = bus_message_new_method_call(bus, &m, bus_mgr, "AcquireHome");
1972                 if (r < 0)
1973                         return bus_log_create_error(r);
1974 
1975                 r = sd_bus_message_append(m, "s", argv[1]);
1976                 if (r < 0)
1977                         return bus_log_create_error(r);
1978 
1979                 r = bus_message_append_secret(m, secret);
1980                 if (r < 0)
1981                         return bus_log_create_error(r);
1982 
1983                 r = sd_bus_message_append(m, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
1984                 if (r < 0)
1985                         return bus_log_create_error(r);
1986 
1987                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
1988                 m = sd_bus_message_unref(m);
1989                 if (r < 0) {
1990                         r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
1991                         if (r < 0)
1992                                 return r;
1993 
1994                         sd_bus_error_free(&error);
1995                 } else {
1996                         int fd;
1997 
1998                         r = sd_bus_message_read(reply, "h", &fd);
1999                         if (r < 0)
2000                                 return bus_log_parse_error(r);
2001 
2002                         acquired_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
2003                         if (acquired_fd < 0)
2004                                 return log_error_errno(errno, "Failed to duplicate acquired fd: %m");
2005 
2006                         reply = sd_bus_message_unref(reply);
2007                         break;
2008                 }
2009         }
2010 
2011         r = bus_call_method(bus, bus_mgr, "GetHomeByName", &error, &reply, "s", argv[1]);
2012         if (r < 0)
2013                 return log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
2014 
2015         r = sd_bus_message_read(reply, "usussso", NULL, NULL, NULL, NULL, &home, NULL, NULL);
2016         if (r < 0)
2017                 return bus_log_parse_error(r);
2018 
2019         r = safe_fork("(with)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REOPEN_LOG, &pid);
2020         if (r < 0)
2021                 return r;
2022         if (r == 0) {
2023                 if (chdir(home) < 0) {
2024                         log_error_errno(errno, "Failed to change to directory %s: %m", home);
2025                         _exit(255);
2026                 }
2027 
2028                 execvp(cmdline[0], cmdline);
2029                 log_error_errno(errno, "Failed to execute %s: %m", cmdline[0]);
2030                 _exit(255);
2031         }
2032 
2033         ret = wait_for_terminate_and_check(cmdline[0], pid, WAIT_LOG_ABNORMAL);
2034 
2035         /* Close the fd that pings the home now. */
2036         acquired_fd = safe_close(acquired_fd);
2037 
2038         r = bus_message_new_method_call(bus, &m, bus_mgr, "ReleaseHome");
2039         if (r < 0)
2040                 return bus_log_create_error(r);
2041 
2042         r = sd_bus_message_append(m, "s", argv[1]);
2043         if (r < 0)
2044                 return bus_log_create_error(r);
2045 
2046         r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2047         if (r < 0) {
2048                 if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_BUSY))
2049                         log_notice("Not deactivating home directory of %s, as it is still used.", argv[1]);
2050                 else
2051                         return log_error_errno(r, "Failed to release user home: %s", bus_error_message(&error, r));
2052         }
2053 
2054         return ret;
2055 }
2056 
lock_all_homes(int argc,char * argv[],void * userdata)2057 static int lock_all_homes(int argc, char *argv[], void *userdata) {
2058         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2059         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2060         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2061         int r;
2062 
2063         r = acquire_bus(&bus);
2064         if (r < 0)
2065                 return r;
2066 
2067         r = bus_message_new_method_call(bus, &m, bus_mgr, "LockAllHomes");
2068         if (r < 0)
2069                 return bus_log_create_error(r);
2070 
2071         r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2072         if (r < 0)
2073                 return log_error_errno(r, "Failed to lock all homes: %s", bus_error_message(&error, r));
2074 
2075         return 0;
2076 }
2077 
deactivate_all_homes(int argc,char * argv[],void * userdata)2078 static int deactivate_all_homes(int argc, char *argv[], void *userdata) {
2079         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2080         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2081         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2082         int r;
2083 
2084         r = acquire_bus(&bus);
2085         if (r < 0)
2086                 return r;
2087 
2088         r = bus_message_new_method_call(bus, &m, bus_mgr, "DeactivateAllHomes");
2089         if (r < 0)
2090                 return bus_log_create_error(r);
2091 
2092         r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2093         if (r < 0)
2094                 return log_error_errno(r, "Failed to deactivate all homes: %s", bus_error_message(&error, r));
2095 
2096         return 0;
2097 }
2098 
rebalance(int argc,char * argv[],void * userdata)2099 static int rebalance(int argc, char *argv[], void *userdata) {
2100         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2101         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2102         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2103         int r;
2104 
2105         r = acquire_bus(&bus);
2106         if (r < 0)
2107                 return r;
2108 
2109         r = bus_message_new_method_call(bus, &m, bus_mgr, "Rebalance");
2110         if (r < 0)
2111                 return bus_log_create_error(r);
2112 
2113         r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2114         if (r < 0) {
2115                 if (sd_bus_error_has_name(&error, BUS_ERROR_REBALANCE_NOT_NEEDED))
2116                         log_info("No homes needed rebalancing.");
2117                 else
2118                         return log_error_errno(r, "Failed to rebalance: %s", bus_error_message(&error, r));
2119         } else
2120                 log_info("Completed rebalancing.");
2121 
2122         return 0;
2123 }
2124 
drop_from_identity(const char * field)2125 static int drop_from_identity(const char *field) {
2126         int r;
2127 
2128         assert(field);
2129 
2130         /* If we are called to update an identity record and drop some field, let's keep track of what to
2131          * remove from the old record */
2132         r = strv_extend(&arg_identity_filter, field);
2133         if (r < 0)
2134                 return log_oom();
2135 
2136         /* Let's also drop the field if it was previously set to a new value on the same command line */
2137         r = json_variant_filter(&arg_identity_extra, STRV_MAKE(field));
2138         if (r < 0)
2139                 return log_error_errno(r, "Failed to filter JSON identity data: %m");
2140 
2141         r = json_variant_filter(&arg_identity_extra_this_machine, STRV_MAKE(field));
2142         if (r < 0)
2143                 return log_error_errno(r, "Failed to filter JSON identity data: %m");
2144 
2145         r = json_variant_filter(&arg_identity_extra_privileged, STRV_MAKE(field));
2146         if (r < 0)
2147                 return log_error_errno(r, "Failed to filter JSON identity data: %m");
2148 
2149         return 0;
2150 }
2151 
help(int argc,char * argv[],void * userdata)2152 static int help(int argc, char *argv[], void *userdata) {
2153         _cleanup_free_ char *link = NULL;
2154         int r;
2155 
2156         pager_open(arg_pager_flags);
2157 
2158         r = terminal_urlify_man("homectl", "1", &link);
2159         if (r < 0)
2160                 return log_oom();
2161 
2162         printf("%1$s [OPTIONS...] COMMAND ...\n\n"
2163                "%2$sCreate, manipulate or inspect home directories.%3$s\n"
2164                "\n%4$sCommands:%5$s\n"
2165                "  list                         List home areas\n"
2166                "  activate USER…               Activate a home area\n"
2167                "  deactivate USER…             Deactivate a home area\n"
2168                "  inspect USER…                Inspect a home area\n"
2169                "  authenticate USER…           Authenticate a home area\n"
2170                "  create USER                  Create a home area\n"
2171                "  remove USER…                 Remove a home area\n"
2172                "  update USER                  Update a home area\n"
2173                "  passwd USER                  Change password of a home area\n"
2174                "  resize USER SIZE             Resize a home area\n"
2175                "  lock USER…                   Temporarily lock an active home area\n"
2176                "  unlock USER…                 Unlock a temporarily locked home area\n"
2177                "  lock-all                     Lock all suitable home areas\n"
2178                "  deactivate-all               Deactivate all active home areas\n"
2179                "  rebalance                    Rebalance free space between home areas\n"
2180                "  with USER [COMMAND…]         Run shell or command with access to a home area\n"
2181                "\n%4$sOptions:%5$s\n"
2182                "  -h --help                    Show this help\n"
2183                "     --version                 Show package version\n"
2184                "     --no-pager                Do not pipe output into a pager\n"
2185                "     --no-legend               Do not show the headers and footers\n"
2186                "     --no-ask-password         Do not ask for system passwords\n"
2187                "  -H --host=[USER@]HOST        Operate on remote host\n"
2188                "  -M --machine=CONTAINER       Operate on local container\n"
2189                "     --identity=PATH           Read JSON identity from file\n"
2190                "     --json=FORMAT             Output inspection data in JSON (takes one of\n"
2191                "                               pretty, short, off)\n"
2192                "  -j                           Equivalent to --json=pretty (on TTY) or\n"
2193                "                               --json=short (otherwise)\n"
2194                "     --export-format=          Strip JSON inspection data (full, stripped,\n"
2195                "                               minimal)\n"
2196                "  -E                           When specified once equals -j --export-format=\n"
2197                "                               stripped, when specified twice equals\n"
2198                "                               -j --export-format=minimal\n"
2199                "\n%4$sGeneral User Record Properties:%5$s\n"
2200                "  -c --real-name=REALNAME      Real name for user\n"
2201                "     --realm=REALM             Realm to create user in\n"
2202                "     --email-address=EMAIL     Email address for user\n"
2203                "     --location=LOCATION       Set location of user on earth\n"
2204                "     --icon-name=NAME          Icon name for user\n"
2205                "  -d --home-dir=PATH           Home directory\n"
2206                "  -u --uid=UID                 Numeric UID for user\n"
2207                "  -G --member-of=GROUP         Add user to group\n"
2208                "     --skel=PATH               Skeleton directory to use\n"
2209                "     --shell=PATH              Shell for account\n"
2210                "     --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n"
2211                "     --timezone=TIMEZONE       Set a time-zone\n"
2212                "     --language=LOCALE         Set preferred language\n"
2213                "     --ssh-authorized-keys=KEYS\n"
2214                "                               Specify SSH public keys\n"
2215                "     --pkcs11-token-uri=URI    URI to PKCS#11 security token containing\n"
2216                "                               private key and matching X.509 certificate\n"
2217                "     --fido2-device=PATH       Path to FIDO2 hidraw device with hmac-secret\n"
2218                "                               extension\n"
2219                "     --fido2-with-client-pin=BOOL\n"
2220                "                               Whether to require entering a PIN to unlock the\n"
2221                "                               account\n"
2222                "     --fido2-with-user-presence=BOOL\n"
2223                "                               Whether to require user presence to unlock the\n"
2224                "                               account\n"
2225                "     --fido2-with-user-verification=BOOL\n"
2226                "                               Whether to require user verification to unlock\n"
2227                "                               the account\n"
2228                "     --recovery-key=BOOL       Add a recovery key\n"
2229                "\n%4$sAccount Management User  Record Properties:%5$s\n"
2230                "     --locked=BOOL             Set locked account state\n"
2231                "     --not-before=TIMESTAMP    Do not allow logins before\n"
2232                "     --not-after=TIMESTAMP     Do not allow logins after\n"
2233                "     --rate-limit-interval=SECS\n"
2234                "                               Login rate-limit interval in seconds\n"
2235                "     --rate-limit-burst=NUMBER\n"
2236                "                               Login rate-limit attempts per interval\n"
2237                "\n%4$sPassword Policy User Record Properties:%5$s\n"
2238                "     --password-hint=HINT      Set Password hint\n"
2239                "     --enforce-password-policy=BOOL\n"
2240                "                               Control whether to enforce system's password\n"
2241                "                               policy for this user\n"
2242                "  -P                           Same as --enforce-password-password=no\n"
2243                "     --password-change-now=BOOL\n"
2244                "                               Require the password to be changed on next login\n"
2245                "     --password-change-min=TIME\n"
2246                "                               Require minimum time between password changes\n"
2247                "     --password-change-max=TIME\n"
2248                "                               Require maximum time between password changes\n"
2249                "     --password-change-warn=TIME\n"
2250                "                               How much time to warn before password expiry\n"
2251                "     --password-change-inactive=TIME\n"
2252                "                               How much time to block password after expiry\n"
2253                "\n%4$sResource Management User Record Properties:%5$s\n"
2254                "     --disk-size=BYTES         Size to assign the user on disk\n"
2255                "     --access-mode=MODE        User home directory access mode\n"
2256                "     --umask=MODE              Umask for user when logging in\n"
2257                "     --nice=NICE               Nice level for user\n"
2258                "     --rlimit=LIMIT=VALUE[:VALUE]\n"
2259                "                               Set resource limits\n"
2260                "     --tasks-max=MAX           Set maximum number of per-user tasks\n"
2261                "     --memory-high=BYTES       Set high memory threshold in bytes\n"
2262                "     --memory-max=BYTES        Set maximum memory limit\n"
2263                "     --cpu-weight=WEIGHT       Set CPU weight\n"
2264                "     --io-weight=WEIGHT        Set IO weight\n"
2265                "\n%4$sStorage User Record Properties:%5$s\n"
2266                "     --storage=STORAGE         Storage type to use (luks, fscrypt, directory,\n"
2267                "                               subvolume, cifs)\n"
2268                "     --image-path=PATH         Path to image file/directory\n"
2269                "     --drop-caches=BOOL        Whether to automatically drop caches on logout\n"
2270                "\n%4$sLUKS Storage User Record Properties:%5$s\n"
2271                "     --fs-type=TYPE            File system type to use in case of luks\n"
2272                "                               storage (btrfs, ext4, xfs)\n"
2273                "     --luks-discard=BOOL       Whether to use 'discard' feature of file system\n"
2274                "                               when activated (mounted)\n"
2275                "     --luks-offline-discard=BOOL\n"
2276                "                               Whether to trim file on logout\n"
2277                "     --luks-cipher=CIPHER      Cipher to use for LUKS encryption\n"
2278                "     --luks-cipher-mode=MODE   Cipher mode to use for LUKS encryption\n"
2279                "     --luks-volume-key-size=BITS\n"
2280                "                               Volume key size to use for LUKS encryption\n"
2281                "     --luks-pbkdf-type=TYPE    Password-based Key Derivation Function to use\n"
2282                "     --luks-pbkdf-hash-algorithm=ALGORITHM\n"
2283                "                               PBKDF hash algorithm to use\n"
2284                "     --luks-pbkdf-time-cost=SECS\n"
2285                "                               Time cost for PBKDF in seconds\n"
2286                "     --luks-pbkdf-memory-cost=BYTES\n"
2287                "                               Memory cost for PBKDF in bytes\n"
2288                "     --luks-pbkdf-parallel-threads=NUMBER\n"
2289                "                               Number of parallel threads for PKBDF\n"
2290                "     --luks-extra-mount-options=OPTIONS\n"
2291                "                               LUKS extra mount options\n"
2292                "     --auto-resize-mode=MODE   Automatically grow/shrink home on login/logout\n"
2293                "     --rebalance-weight=WEIGHT Weight while rebalancing\n"
2294                "\n%4$sMounting User Record Properties:%5$s\n"
2295                "     --nosuid=BOOL             Control the 'nosuid' flag of the home mount\n"
2296                "     --nodev=BOOL              Control the 'nodev' flag of the home mount\n"
2297                "     --noexec=BOOL             Control the 'noexec' flag of the home mount\n"
2298                "\n%4$sCIFS User Record Properties:%5$s\n"
2299                "     --cifs-domain=DOMAIN      CIFS (Windows) domain\n"
2300                "     --cifs-user-name=USER     CIFS (Windows) user name\n"
2301                "     --cifs-service=SERVICE    CIFS (Windows) service to mount as home area\n"
2302                "     --cifs-extra-mount-options=OPTIONS\n"
2303                "                               CIFS (Windows) extra mount options\n"
2304                "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
2305                "     --stop-delay=SECS         How long to leave user services running after\n"
2306                "                               logout\n"
2307                "     --kill-processes=BOOL     Whether to kill user processes when sessions\n"
2308                "                               terminate\n"
2309                "     --auto-login=BOOL         Try to log this user in automatically\n"
2310                "\nSee the %6$s for details.\n",
2311                program_invocation_short_name,
2312                ansi_highlight(),
2313                ansi_normal(),
2314                ansi_underline(),
2315                ansi_normal(),
2316                link);
2317 
2318         return 0;
2319 }
2320 
parse_argv(int argc,char * argv[])2321 static int parse_argv(int argc, char *argv[]) {
2322 
2323         enum {
2324                 ARG_VERSION = 0x100,
2325                 ARG_NO_PAGER,
2326                 ARG_NO_LEGEND,
2327                 ARG_NO_ASK_PASSWORD,
2328                 ARG_REALM,
2329                 ARG_EMAIL_ADDRESS,
2330                 ARG_DISK_SIZE,
2331                 ARG_ACCESS_MODE,
2332                 ARG_STORAGE,
2333                 ARG_FS_TYPE,
2334                 ARG_IMAGE_PATH,
2335                 ARG_UMASK,
2336                 ARG_LUKS_DISCARD,
2337                 ARG_LUKS_OFFLINE_DISCARD,
2338                 ARG_JSON,
2339                 ARG_SETENV,
2340                 ARG_TIMEZONE,
2341                 ARG_LANGUAGE,
2342                 ARG_LOCKED,
2343                 ARG_SSH_AUTHORIZED_KEYS,
2344                 ARG_LOCATION,
2345                 ARG_ICON_NAME,
2346                 ARG_PASSWORD_HINT,
2347                 ARG_NICE,
2348                 ARG_RLIMIT,
2349                 ARG_NOT_BEFORE,
2350                 ARG_NOT_AFTER,
2351                 ARG_LUKS_CIPHER,
2352                 ARG_LUKS_CIPHER_MODE,
2353                 ARG_LUKS_VOLUME_KEY_SIZE,
2354                 ARG_NOSUID,
2355                 ARG_NODEV,
2356                 ARG_NOEXEC,
2357                 ARG_CIFS_DOMAIN,
2358                 ARG_CIFS_USER_NAME,
2359                 ARG_CIFS_SERVICE,
2360                 ARG_CIFS_EXTRA_MOUNT_OPTIONS,
2361                 ARG_TASKS_MAX,
2362                 ARG_MEMORY_HIGH,
2363                 ARG_MEMORY_MAX,
2364                 ARG_CPU_WEIGHT,
2365                 ARG_IO_WEIGHT,
2366                 ARG_LUKS_PBKDF_TYPE,
2367                 ARG_LUKS_PBKDF_HASH_ALGORITHM,
2368                 ARG_LUKS_PBKDF_TIME_COST,
2369                 ARG_LUKS_PBKDF_MEMORY_COST,
2370                 ARG_LUKS_PBKDF_PARALLEL_THREADS,
2371                 ARG_RATE_LIMIT_INTERVAL,
2372                 ARG_RATE_LIMIT_BURST,
2373                 ARG_STOP_DELAY,
2374                 ARG_KILL_PROCESSES,
2375                 ARG_ENFORCE_PASSWORD_POLICY,
2376                 ARG_PASSWORD_CHANGE_NOW,
2377                 ARG_PASSWORD_CHANGE_MIN,
2378                 ARG_PASSWORD_CHANGE_MAX,
2379                 ARG_PASSWORD_CHANGE_WARN,
2380                 ARG_PASSWORD_CHANGE_INACTIVE,
2381                 ARG_EXPORT_FORMAT,
2382                 ARG_AUTO_LOGIN,
2383                 ARG_PKCS11_TOKEN_URI,
2384                 ARG_FIDO2_DEVICE,
2385                 ARG_FIDO2_WITH_PIN,
2386                 ARG_FIDO2_WITH_UP,
2387                 ARG_FIDO2_WITH_UV,
2388                 ARG_RECOVERY_KEY,
2389                 ARG_AND_RESIZE,
2390                 ARG_AND_CHANGE_PASSWORD,
2391                 ARG_DROP_CACHES,
2392                 ARG_LUKS_EXTRA_MOUNT_OPTIONS,
2393                 ARG_AUTO_RESIZE_MODE,
2394                 ARG_REBALANCE_WEIGHT,
2395                 ARG_FIDO2_CRED_ALG,
2396         };
2397 
2398         static const struct option options[] = {
2399                 { "help",                        no_argument,       NULL, 'h'                             },
2400                 { "version",                     no_argument,       NULL, ARG_VERSION                     },
2401                 { "no-pager",                    no_argument,       NULL, ARG_NO_PAGER                    },
2402                 { "no-legend",                   no_argument,       NULL, ARG_NO_LEGEND                   },
2403                 { "no-ask-password",             no_argument,       NULL, ARG_NO_ASK_PASSWORD             },
2404                 { "host",                        required_argument, NULL, 'H'                             },
2405                 { "machine",                     required_argument, NULL, 'M'                             },
2406                 { "identity",                    required_argument, NULL, 'I'                             },
2407                 { "real-name",                   required_argument, NULL, 'c'                             },
2408                 { "comment",                     required_argument, NULL, 'c'                             }, /* Compat alias to keep thing in sync with useradd(8) */
2409                 { "realm",                       required_argument, NULL, ARG_REALM                       },
2410                 { "email-address",               required_argument, NULL, ARG_EMAIL_ADDRESS               },
2411                 { "location",                    required_argument, NULL, ARG_LOCATION                    },
2412                 { "password-hint",               required_argument, NULL, ARG_PASSWORD_HINT               },
2413                 { "icon-name",                   required_argument, NULL, ARG_ICON_NAME                   },
2414                 { "home-dir",                    required_argument, NULL, 'd'                             }, /* Compatible with useradd(8) */
2415                 { "uid",                         required_argument, NULL, 'u'                             }, /* Compatible with useradd(8) */
2416                 { "member-of",                   required_argument, NULL, 'G'                             },
2417                 { "groups",                      required_argument, NULL, 'G'                             }, /* Compat alias to keep thing in sync with useradd(8) */
2418                 { "skel",                        required_argument, NULL, 'k'                             }, /* Compatible with useradd(8) */
2419                 { "shell",                       required_argument, NULL, 's'                             }, /* Compatible with useradd(8) */
2420                 { "setenv",                      required_argument, NULL, ARG_SETENV                      },
2421                 { "timezone",                    required_argument, NULL, ARG_TIMEZONE                    },
2422                 { "language",                    required_argument, NULL, ARG_LANGUAGE                    },
2423                 { "locked",                      required_argument, NULL, ARG_LOCKED                      },
2424                 { "not-before",                  required_argument, NULL, ARG_NOT_BEFORE                  },
2425                 { "not-after",                   required_argument, NULL, ARG_NOT_AFTER                   },
2426                 { "expiredate",                  required_argument, NULL, 'e'                             }, /* Compat alias to keep thing in sync with useradd(8) */
2427                 { "ssh-authorized-keys",         required_argument, NULL, ARG_SSH_AUTHORIZED_KEYS         },
2428                 { "disk-size",                   required_argument, NULL, ARG_DISK_SIZE                   },
2429                 { "access-mode",                 required_argument, NULL, ARG_ACCESS_MODE                 },
2430                 { "umask",                       required_argument, NULL, ARG_UMASK                       },
2431                 { "nice",                        required_argument, NULL, ARG_NICE                        },
2432                 { "rlimit",                      required_argument, NULL, ARG_RLIMIT                      },
2433                 { "tasks-max",                   required_argument, NULL, ARG_TASKS_MAX                   },
2434                 { "memory-high",                 required_argument, NULL, ARG_MEMORY_HIGH                 },
2435                 { "memory-max",                  required_argument, NULL, ARG_MEMORY_MAX                  },
2436                 { "cpu-weight",                  required_argument, NULL, ARG_CPU_WEIGHT                  },
2437                 { "io-weight",                   required_argument, NULL, ARG_IO_WEIGHT                   },
2438                 { "storage",                     required_argument, NULL, ARG_STORAGE                     },
2439                 { "image-path",                  required_argument, NULL, ARG_IMAGE_PATH                  },
2440                 { "fs-type",                     required_argument, NULL, ARG_FS_TYPE                     },
2441                 { "luks-discard",                required_argument, NULL, ARG_LUKS_DISCARD                },
2442                 { "luks-offline-discard",        required_argument, NULL, ARG_LUKS_OFFLINE_DISCARD        },
2443                 { "luks-cipher",                 required_argument, NULL, ARG_LUKS_CIPHER                 },
2444                 { "luks-cipher-mode",            required_argument, NULL, ARG_LUKS_CIPHER_MODE            },
2445                 { "luks-volume-key-size",        required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE        },
2446                 { "luks-pbkdf-type",             required_argument, NULL, ARG_LUKS_PBKDF_TYPE             },
2447                 { "luks-pbkdf-hash-algorithm",   required_argument, NULL, ARG_LUKS_PBKDF_HASH_ALGORITHM   },
2448                 { "luks-pbkdf-time-cost",        required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST        },
2449                 { "luks-pbkdf-memory-cost",      required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST      },
2450                 { "luks-pbkdf-parallel-threads", required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS },
2451                 { "nosuid",                      required_argument, NULL, ARG_NOSUID                      },
2452                 { "nodev",                       required_argument, NULL, ARG_NODEV                       },
2453                 { "noexec",                      required_argument, NULL, ARG_NOEXEC                      },
2454                 { "cifs-user-name",              required_argument, NULL, ARG_CIFS_USER_NAME              },
2455                 { "cifs-domain",                 required_argument, NULL, ARG_CIFS_DOMAIN                 },
2456                 { "cifs-service",                required_argument, NULL, ARG_CIFS_SERVICE                },
2457                 { "cifs-extra-mount-options",    required_argument, NULL, ARG_CIFS_EXTRA_MOUNT_OPTIONS    },
2458                 { "rate-limit-interval",         required_argument, NULL, ARG_RATE_LIMIT_INTERVAL         },
2459                 { "rate-limit-burst",            required_argument, NULL, ARG_RATE_LIMIT_BURST            },
2460                 { "stop-delay",                  required_argument, NULL, ARG_STOP_DELAY                  },
2461                 { "kill-processes",              required_argument, NULL, ARG_KILL_PROCESSES              },
2462                 { "enforce-password-policy",     required_argument, NULL, ARG_ENFORCE_PASSWORD_POLICY     },
2463                 { "password-change-now",         required_argument, NULL, ARG_PASSWORD_CHANGE_NOW         },
2464                 { "password-change-min",         required_argument, NULL, ARG_PASSWORD_CHANGE_MIN         },
2465                 { "password-change-max",         required_argument, NULL, ARG_PASSWORD_CHANGE_MAX         },
2466                 { "password-change-warn",        required_argument, NULL, ARG_PASSWORD_CHANGE_WARN        },
2467                 { "password-change-inactive",    required_argument, NULL, ARG_PASSWORD_CHANGE_INACTIVE    },
2468                 { "auto-login",                  required_argument, NULL, ARG_AUTO_LOGIN                  },
2469                 { "json",                        required_argument, NULL, ARG_JSON                        },
2470                 { "export-format",               required_argument, NULL, ARG_EXPORT_FORMAT               },
2471                 { "pkcs11-token-uri",            required_argument, NULL, ARG_PKCS11_TOKEN_URI            },
2472                 { "fido2-credential-algorithm",  required_argument, NULL, ARG_FIDO2_CRED_ALG              },
2473                 { "fido2-device",                required_argument, NULL, ARG_FIDO2_DEVICE                },
2474                 { "fido2-with-client-pin",       required_argument, NULL, ARG_FIDO2_WITH_PIN              },
2475                 { "fido2-with-user-presence",    required_argument, NULL, ARG_FIDO2_WITH_UP               },
2476                 { "fido2-with-user-verification",required_argument, NULL, ARG_FIDO2_WITH_UV               },
2477                 { "recovery-key",                required_argument, NULL, ARG_RECOVERY_KEY                },
2478                 { "and-resize",                  required_argument, NULL, ARG_AND_RESIZE                  },
2479                 { "and-change-password",         required_argument, NULL, ARG_AND_CHANGE_PASSWORD         },
2480                 { "drop-caches",                 required_argument, NULL, ARG_DROP_CACHES                 },
2481                 { "luks-extra-mount-options",    required_argument, NULL, ARG_LUKS_EXTRA_MOUNT_OPTIONS    },
2482                 { "auto-resize-mode",            required_argument, NULL, ARG_AUTO_RESIZE_MODE            },
2483                 { "rebalance-weight",            required_argument, NULL, ARG_REBALANCE_WEIGHT            },
2484                 {}
2485         };
2486 
2487         int r;
2488 
2489         assert(argc >= 0);
2490         assert(argv);
2491 
2492         for (;;) {
2493                 int c;
2494 
2495                 c = getopt_long(argc, argv, "hH:M:I:c:d:u:k:s:e:G:jPE", options, NULL);
2496                 if (c < 0)
2497                         break;
2498 
2499                 switch (c) {
2500 
2501                 case 'h':
2502                         return help(0, NULL, NULL);
2503 
2504                 case ARG_VERSION:
2505                         return version();
2506 
2507                 case ARG_NO_PAGER:
2508                         arg_pager_flags |= PAGER_DISABLE;
2509                         break;
2510 
2511                 case ARG_NO_LEGEND:
2512                         arg_legend = false;
2513                         break;
2514 
2515                 case ARG_NO_ASK_PASSWORD:
2516                         arg_ask_password = false;
2517                         break;
2518 
2519                 case 'H':
2520                         arg_transport = BUS_TRANSPORT_REMOTE;
2521                         arg_host = optarg;
2522                         break;
2523 
2524                 case 'M':
2525                         arg_transport = BUS_TRANSPORT_MACHINE;
2526                         arg_host = optarg;
2527                         break;
2528 
2529                 case 'I':
2530                         arg_identity = optarg;
2531                         break;
2532 
2533                 case 'c':
2534                         if (isempty(optarg)) {
2535                                 r = drop_from_identity("realName");
2536                                 if (r < 0)
2537                                         return r;
2538 
2539                                 break;
2540                         }
2541 
2542                         if (!valid_gecos(optarg))
2543                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Real name '%s' not a valid GECOS field.", optarg);
2544 
2545                         r = json_variant_set_field_string(&arg_identity_extra, "realName", optarg);
2546                         if (r < 0)
2547                                 return log_error_errno(r, "Failed to set realName field: %m");
2548 
2549                         break;
2550 
2551                 case 'd': {
2552                         _cleanup_free_ char *hd = NULL;
2553 
2554                         if (isempty(optarg)) {
2555                                 r = drop_from_identity("homeDirectory");
2556                                 if (r < 0)
2557                                         return r;
2558 
2559                                 break;
2560                         }
2561 
2562                         r = parse_path_argument(optarg, false, &hd);
2563                         if (r < 0)
2564                                 return r;
2565 
2566                         if (!valid_home(hd))
2567                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Home directory '%s' not valid.", hd);
2568 
2569                         r = json_variant_set_field_string(&arg_identity_extra, "homeDirectory", hd);
2570                         if (r < 0)
2571                                 return log_error_errno(r, "Failed to set homeDirectory field: %m");
2572 
2573                         break;
2574                 }
2575 
2576                 case ARG_REALM:
2577                         if (isempty(optarg)) {
2578                                 r = drop_from_identity("realm");
2579                                 if (r < 0)
2580                                         return r;
2581 
2582                                 break;
2583                         }
2584 
2585                         r = dns_name_is_valid(optarg);
2586                         if (r < 0)
2587                                 return log_error_errno(r, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg);
2588                         if (r == 0)
2589                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Realm '%s' is not a valid DNS domain: %m", optarg);
2590 
2591                         r = json_variant_set_field_string(&arg_identity_extra, "realm", optarg);
2592                         if (r < 0)
2593                                 return log_error_errno(r, "Failed to set realm field: %m");
2594                         break;
2595 
2596                 case ARG_EMAIL_ADDRESS:
2597                 case ARG_LOCATION:
2598                 case ARG_ICON_NAME:
2599                 case ARG_CIFS_USER_NAME:
2600                 case ARG_CIFS_DOMAIN:
2601                 case ARG_CIFS_EXTRA_MOUNT_OPTIONS:
2602                 case ARG_LUKS_EXTRA_MOUNT_OPTIONS: {
2603 
2604                         const char *field =
2605                                            c == ARG_EMAIL_ADDRESS ? "emailAddress" :
2606                                                 c == ARG_LOCATION ? "location" :
2607                                                c == ARG_ICON_NAME ? "iconName" :
2608                                           c == ARG_CIFS_USER_NAME ? "cifsUserName" :
2609                                              c == ARG_CIFS_DOMAIN ? "cifsDomain" :
2610                                 c == ARG_CIFS_EXTRA_MOUNT_OPTIONS ? "cifsExtraMountOptions" :
2611                                 c == ARG_LUKS_EXTRA_MOUNT_OPTIONS ? "luksExtraMountOptions" :
2612                                                                     NULL;
2613 
2614                         assert(field);
2615 
2616                         if (isempty(optarg)) {
2617                                 r = drop_from_identity(field);
2618                                 if (r < 0)
2619                                         return r;
2620 
2621                                 break;
2622                         }
2623 
2624                         r = json_variant_set_field_string(&arg_identity_extra, field, optarg);
2625                         if (r < 0)
2626                                 return log_error_errno(r, "Failed to set %s field: %m", field);
2627 
2628                         break;
2629                 }
2630 
2631                 case ARG_CIFS_SERVICE:
2632                         if (isempty(optarg)) {
2633                                 r = drop_from_identity("cifsService");
2634                                 if (r < 0)
2635                                         return r;
2636 
2637                                 break;
2638                         }
2639 
2640                         r = parse_cifs_service(optarg, NULL, NULL, NULL);
2641                         if (r < 0)
2642                                 return log_error_errno(r, "Failed to validate CIFS service name: %s", optarg);
2643 
2644                         r = json_variant_set_field_string(&arg_identity_extra, "cifsService", optarg);
2645                         if (r < 0)
2646                                 return log_error_errno(r, "Failed to set cifsService field: %m");
2647 
2648                         break;
2649 
2650                 case ARG_PASSWORD_HINT:
2651                         if (isempty(optarg)) {
2652                                 r = drop_from_identity("passwordHint");
2653                                 if (r < 0)
2654                                         return r;
2655 
2656                                 break;
2657                         }
2658 
2659                         r = json_variant_set_field_string(&arg_identity_extra_privileged, "passwordHint", optarg);
2660                         if (r < 0)
2661                                 return log_error_errno(r, "Failed to set passwordHint field: %m");
2662 
2663                         string_erase(optarg);
2664                         break;
2665 
2666                 case ARG_NICE: {
2667                         int nc;
2668 
2669                         if (isempty(optarg)) {
2670                                 r = drop_from_identity("niceLevel");
2671                                 if (r < 0)
2672                                         return r;
2673                                 break;
2674                         }
2675 
2676                         r = parse_nice(optarg, &nc);
2677                         if (r < 0)
2678                                 return log_error_errno(r, "Failed to parse nice level: %s", optarg);
2679 
2680                         r = json_variant_set_field_integer(&arg_identity_extra, "niceLevel", nc);
2681                         if (r < 0)
2682                                 return log_error_errno(r, "Failed to set niceLevel field: %m");
2683 
2684                         break;
2685                 }
2686 
2687                 case ARG_RLIMIT: {
2688                         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *jcur = NULL, *jmax = NULL;
2689                         _cleanup_free_ char *field = NULL, *t = NULL;
2690                         const char *eq;
2691                         struct rlimit rl;
2692                         int l;
2693 
2694                         if (isempty(optarg)) {
2695                                 /* Remove all resource limits */
2696 
2697                                 r = drop_from_identity("resourceLimits");
2698                                 if (r < 0)
2699                                         return r;
2700 
2701                                 arg_identity_filter_rlimits = strv_free(arg_identity_filter_rlimits);
2702                                 arg_identity_extra_rlimits = json_variant_unref(arg_identity_extra_rlimits);
2703                                 break;
2704                         }
2705 
2706                         eq = strchr(optarg, '=');
2707                         if (!eq)
2708                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't parse resource limit assignment: %s", optarg);
2709 
2710                         field = strndup(optarg, eq - optarg);
2711                         if (!field)
2712                                 return log_oom();
2713 
2714                         l = rlimit_from_string_harder(field);
2715                         if (l < 0)
2716                                 return log_error_errno(l, "Unknown resource limit type: %s", field);
2717 
2718                         if (isempty(eq + 1)) {
2719                                 /* Remove only the specific rlimit */
2720 
2721                                 r = strv_extend(&arg_identity_filter_rlimits, rlimit_to_string(l));
2722                                 if (r < 0)
2723                                         return r;
2724 
2725                                 r = json_variant_filter(&arg_identity_extra_rlimits, STRV_MAKE(field));
2726                                 if (r < 0)
2727                                         return log_error_errno(r, "Failed to filter JSON identity data: %m");
2728 
2729                                 break;
2730                         }
2731 
2732                         r = rlimit_parse(l, eq + 1, &rl);
2733                         if (r < 0)
2734                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse resource limit value: %s", eq + 1);
2735 
2736                         r = rl.rlim_cur == RLIM_INFINITY ? json_variant_new_null(&jcur) : json_variant_new_unsigned(&jcur, rl.rlim_cur);
2737                         if (r < 0)
2738                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to allocate current integer: %m");
2739 
2740                         r = rl.rlim_max == RLIM_INFINITY ? json_variant_new_null(&jmax) : json_variant_new_unsigned(&jmax, rl.rlim_max);
2741                         if (r < 0)
2742                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to allocate maximum integer: %m");
2743 
2744                         r = json_build(&v,
2745                                        JSON_BUILD_OBJECT(
2746                                                        JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur)),
2747                                                        JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax))));
2748                         if (r < 0)
2749                                 return log_error_errno(r, "Failed to build resource limit: %m");
2750 
2751                         t = strjoin("RLIMIT_", rlimit_to_string(l));
2752                         if (!t)
2753                                 return log_oom();
2754 
2755                         r = json_variant_set_field(&arg_identity_extra_rlimits, t, v);
2756                         if (r < 0)
2757                                 return log_error_errno(r, "Failed to set %s field: %m", rlimit_to_string(l));
2758 
2759                         break;
2760                 }
2761 
2762                 case 'u': {
2763                         uid_t uid;
2764 
2765                         if (isempty(optarg)) {
2766                                 r = drop_from_identity("uid");
2767                                 if (r < 0)
2768                                         return r;
2769 
2770                                 break;
2771                         }
2772 
2773                         r = parse_uid(optarg, &uid);
2774                         if (r < 0)
2775                                 return log_error_errno(r, "Failed to parse UID '%s'.", optarg);
2776 
2777                         if (uid_is_system(uid))
2778                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in system range, refusing.", uid);
2779                         if (uid_is_dynamic(uid))
2780                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in dynamic range, refusing.", uid);
2781                         if (uid == UID_NOBODY)
2782                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is nobody UID, refusing.", uid);
2783 
2784                         r = json_variant_set_field_unsigned(&arg_identity_extra, "uid", uid);
2785                         if (r < 0)
2786                                 return log_error_errno(r, "Failed to set realm field: %m");
2787 
2788                         break;
2789                 }
2790 
2791                 case 'k':
2792                 case ARG_IMAGE_PATH: {
2793                         const char *field = c == 'k' ? "skeletonDirectory" : "imagePath";
2794                         _cleanup_free_ char *v = NULL;
2795 
2796                         if (isempty(optarg)) {
2797                                 r = drop_from_identity(field);
2798                                 if (r < 0)
2799                                         return r;
2800 
2801                                 break;
2802                         }
2803 
2804                         r = parse_path_argument(optarg, false, &v);
2805                         if (r < 0)
2806                                 return r;
2807 
2808                         r = json_variant_set_field_string(&arg_identity_extra_this_machine, field, v);
2809                         if (r < 0)
2810                                 return log_error_errno(r, "Failed to set %s field: %m", v);
2811 
2812                         break;
2813                 }
2814 
2815                 case 's':
2816                         if (isempty(optarg)) {
2817                                 r = drop_from_identity("shell");
2818                                 if (r < 0)
2819                                         return r;
2820 
2821                                 break;
2822                         }
2823 
2824                         if (!valid_shell(optarg))
2825                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Shell '%s' not valid.", optarg);
2826 
2827                         r = json_variant_set_field_string(&arg_identity_extra, "shell", optarg);
2828                         if (r < 0)
2829                                 return log_error_errno(r, "Failed to set shell field: %m");
2830 
2831                         break;
2832 
2833                 case ARG_SETENV: {
2834                         _cleanup_free_ char **l = NULL;
2835                         _cleanup_(json_variant_unrefp) JsonVariant *ne = NULL;
2836                         JsonVariant *e;
2837 
2838                         if (isempty(optarg)) {
2839                                 r = drop_from_identity("environment");
2840                                 if (r < 0)
2841                                         return r;
2842 
2843                                 break;
2844                         }
2845 
2846                         e = json_variant_by_key(arg_identity_extra, "environment");
2847                         if (e) {
2848                                 r = json_variant_strv(e, &l);
2849                                 if (r < 0)
2850                                         return log_error_errno(r, "Failed to parse JSON environment field: %m");
2851                         }
2852 
2853                         r = strv_env_replace_strdup_passthrough(&l, optarg);
2854                         if (r < 0)
2855                                 return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
2856 
2857                         strv_sort(l);
2858 
2859                         r = json_variant_new_array_strv(&ne, l);
2860                         if (r < 0)
2861                                 return log_error_errno(r, "Failed to allocate environment list JSON: %m");
2862 
2863                         r = json_variant_set_field(&arg_identity_extra, "environment", ne);
2864                         if (r < 0)
2865                                 return log_error_errno(r, "Failed to set environment list: %m");
2866 
2867                         break;
2868                 }
2869 
2870                 case ARG_TIMEZONE:
2871 
2872                         if (isempty(optarg)) {
2873                                 r = drop_from_identity("timeZone");
2874                                 if (r < 0)
2875                                         return r;
2876 
2877                                 break;
2878                         }
2879 
2880                         if (!timezone_is_valid(optarg, LOG_DEBUG))
2881                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Timezone '%s' is not valid.", optarg);
2882 
2883                         r = json_variant_set_field_string(&arg_identity_extra, "timeZone", optarg);
2884                         if (r < 0)
2885                                 return log_error_errno(r, "Failed to set timezone field: %m");
2886 
2887                         break;
2888 
2889                 case ARG_LANGUAGE:
2890                         if (isempty(optarg)) {
2891                                 r = drop_from_identity("language");
2892                                 if (r < 0)
2893                                         return r;
2894 
2895                                 break;
2896                         }
2897 
2898                         if (!locale_is_valid(optarg))
2899                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale '%s' is not valid.", optarg);
2900 
2901                         if (locale_is_installed(optarg) <= 0)
2902                                 log_warning("Locale '%s' is not installed, accepting anyway.", optarg);
2903 
2904                         r = json_variant_set_field_string(&arg_identity_extra, "preferredLanguage", optarg);
2905                         if (r < 0)
2906                                 return log_error_errno(r, "Failed to set preferredLanguage field: %m");
2907 
2908                         break;
2909 
2910                 case ARG_NOSUID:
2911                 case ARG_NODEV:
2912                 case ARG_NOEXEC:
2913                 case ARG_LOCKED:
2914                 case ARG_KILL_PROCESSES:
2915                 case ARG_ENFORCE_PASSWORD_POLICY:
2916                 case ARG_AUTO_LOGIN:
2917                 case ARG_PASSWORD_CHANGE_NOW: {
2918                         const char *field =
2919                                                  c == ARG_LOCKED ? "locked" :
2920                                                  c == ARG_NOSUID ? "mountNoSuid" :
2921                                                   c == ARG_NODEV ? "mountNoDevices" :
2922                                                  c == ARG_NOEXEC ? "mountNoExecute" :
2923                                          c == ARG_KILL_PROCESSES ? "killProcesses" :
2924                                 c == ARG_ENFORCE_PASSWORD_POLICY ? "enforcePasswordPolicy" :
2925                                              c == ARG_AUTO_LOGIN ? "autoLogin" :
2926                                     c == ARG_PASSWORD_CHANGE_NOW ? "passwordChangeNow" :
2927                                                                    NULL;
2928 
2929                         assert(field);
2930 
2931                         if (isempty(optarg)) {
2932                                 r = drop_from_identity(field);
2933                                 if (r < 0)
2934                                         return r;
2935 
2936                                 break;
2937                         }
2938 
2939                         r = parse_boolean(optarg);
2940                         if (r < 0)
2941                                 return log_error_errno(r, "Failed to parse %s boolean: %m", field);
2942 
2943                         r = json_variant_set_field_boolean(&arg_identity_extra, field, r > 0);
2944                         if (r < 0)
2945                                 return log_error_errno(r, "Failed to set %s field: %m", field);
2946 
2947                         break;
2948                 }
2949 
2950                 case 'P':
2951                         r = json_variant_set_field_boolean(&arg_identity_extra, "enforcePasswordPolicy", false);
2952                         if (r < 0)
2953                                 return log_error_errno(r, "Failed to set enforcePasswordPolicy field: %m");
2954 
2955                         break;
2956 
2957                 case ARG_DISK_SIZE:
2958                         if (isempty(optarg)) {
2959                                 FOREACH_STRING(prop, "diskSize", "diskSizeRelative", "rebalanceWeight") {
2960                                         r = drop_from_identity(prop);
2961                                         if (r < 0)
2962                                                 return r;
2963                                 }
2964 
2965                                 arg_disk_size = arg_disk_size_relative = UINT64_MAX;
2966                                 break;
2967                         }
2968 
2969                         r = parse_permyriad(optarg);
2970                         if (r < 0) {
2971                                 r = parse_disk_size(optarg, &arg_disk_size);
2972                                 if (r < 0)
2973                                         return r;
2974 
2975                                 r = drop_from_identity("diskSizeRelative");
2976                                 if (r < 0)
2977                                         return r;
2978 
2979                                 r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "diskSize", arg_disk_size);
2980                                 if (r < 0)
2981                                         return log_error_errno(r, "Failed to set diskSize field: %m");
2982 
2983                                 arg_disk_size_relative = UINT64_MAX;
2984                         } else {
2985                                 /* Normalize to UINT32_MAX == 100% */
2986                                 arg_disk_size_relative = UINT32_SCALE_FROM_PERMYRIAD(r);
2987 
2988                                 r = drop_from_identity("diskSize");
2989                                 if (r < 0)
2990                                         return r;
2991 
2992                                 r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "diskSizeRelative", arg_disk_size_relative);
2993                                 if (r < 0)
2994                                         return log_error_errno(r, "Failed to set diskSizeRelative field: %m");
2995 
2996                                 arg_disk_size = UINT64_MAX;
2997                         }
2998 
2999                         /* Automatically turn off the rebalance logic if user configured a size explicitly */
3000                         r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "rebalanceWeight", REBALANCE_WEIGHT_OFF);
3001                         if (r < 0)
3002                                 return log_error_errno(r, "Failed to set rebalanceWeight field: %m");
3003 
3004                         break;
3005 
3006                 case ARG_ACCESS_MODE: {
3007                         mode_t mode;
3008 
3009                         if (isempty(optarg)) {
3010                                 r = drop_from_identity("accessMode");
3011                                 if (r < 0)
3012                                         return r;
3013 
3014                                 break;
3015                         }
3016 
3017                         r = parse_mode(optarg, &mode);
3018                         if (r < 0)
3019                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Access mode '%s' not valid.", optarg);
3020 
3021                         r = json_variant_set_field_unsigned(&arg_identity_extra, "accessMode", mode);
3022                         if (r < 0)
3023                                 return log_error_errno(r, "Failed to set access mode field: %m");
3024 
3025                         break;
3026                 }
3027 
3028                 case ARG_LUKS_DISCARD:
3029                         if (isempty(optarg)) {
3030                                 r = drop_from_identity("luksDiscard");
3031                                 if (r < 0)
3032                                         return r;
3033 
3034                                 break;
3035                         }
3036 
3037                         r = parse_boolean(optarg);
3038                         if (r < 0)
3039                                 return log_error_errno(r, "Failed to parse --luks-discard= parameter: %s", optarg);
3040 
3041                         r = json_variant_set_field_boolean(&arg_identity_extra, "luksDiscard", r);
3042                         if (r < 0)
3043                                 return log_error_errno(r, "Failed to set discard field: %m");
3044 
3045                         break;
3046 
3047                 case ARG_LUKS_OFFLINE_DISCARD:
3048                         if (isempty(optarg)) {
3049                                 r = drop_from_identity("luksOfflineDiscard");
3050                                 if (r < 0)
3051                                         return r;
3052 
3053                                 break;
3054                         }
3055 
3056                         r = parse_boolean(optarg);
3057                         if (r < 0)
3058                                 return log_error_errno(r, "Failed to parse --luks-offline-discard= parameter: %s", optarg);
3059 
3060                         r = json_variant_set_field_boolean(&arg_identity_extra, "luksOfflineDiscard", r);
3061                         if (r < 0)
3062                                 return log_error_errno(r, "Failed to set offline discard field: %m");
3063 
3064                         break;
3065 
3066                 case ARG_LUKS_VOLUME_KEY_SIZE:
3067                 case ARG_LUKS_PBKDF_PARALLEL_THREADS:
3068                 case ARG_RATE_LIMIT_BURST: {
3069                         const char *field =
3070                                        c == ARG_LUKS_VOLUME_KEY_SIZE ? "luksVolumeKeySize" :
3071                                 c == ARG_LUKS_PBKDF_PARALLEL_THREADS ? "luksPbkdfParallelThreads" :
3072                                            c == ARG_RATE_LIMIT_BURST ? "rateLimitBurst" : NULL;
3073                         unsigned n;
3074 
3075                         assert(field);
3076 
3077                         if (isempty(optarg)) {
3078                                 r = drop_from_identity(field);
3079                                 if (r < 0)
3080                                         return r;
3081                         }
3082 
3083                         r = safe_atou(optarg, &n);
3084                         if (r < 0)
3085                                 return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
3086 
3087                         r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
3088                         if (r < 0)
3089                                 return log_error_errno(r, "Failed to set %s field: %m", field);
3090 
3091                         break;
3092                 }
3093 
3094                 case ARG_UMASK: {
3095                         mode_t m;
3096 
3097                         if (isempty(optarg)) {
3098                                 r = drop_from_identity("umask");
3099                                 if (r < 0)
3100                                         return r;
3101 
3102                                 break;
3103                         }
3104 
3105                         r = parse_mode(optarg, &m);
3106                         if (r < 0)
3107                                 return log_error_errno(r, "Failed to parse umask: %m");
3108 
3109                         r = json_variant_set_field_integer(&arg_identity_extra, "umask", m);
3110                         if (r < 0)
3111                                 return log_error_errno(r, "Failed to set umask field: %m");
3112 
3113                         break;
3114                 }
3115 
3116                 case ARG_SSH_AUTHORIZED_KEYS: {
3117                         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
3118                         _cleanup_(strv_freep) char **l = NULL, **add = NULL;
3119 
3120                         if (isempty(optarg)) {
3121                                 r = drop_from_identity("sshAuthorizedKeys");
3122                                 if (r < 0)
3123                                         return r;
3124 
3125                                 break;
3126                         }
3127 
3128                         if (optarg[0] == '@') {
3129                                 _cleanup_fclose_ FILE *f = NULL;
3130 
3131                                 /* If prefixed with '@' read from a file */
3132 
3133                                 f = fopen(optarg+1, "re");
3134                                 if (!f)
3135                                         return log_error_errno(errno, "Failed to open '%s': %m", optarg+1);
3136 
3137                                 for (;;) {
3138                                         _cleanup_free_ char *line = NULL;
3139 
3140                                         r = read_line(f, LONG_LINE_MAX, &line);
3141                                         if (r < 0)
3142                                                 return log_error_errno(r, "Failed to read from '%s': %m", optarg+1);
3143                                         if (r == 0)
3144                                                 break;
3145 
3146                                         if (isempty(line))
3147                                                 continue;
3148 
3149                                         if (line[0] == '#')
3150                                                 continue;
3151 
3152                                         r = strv_consume(&add, TAKE_PTR(line));
3153                                         if (r < 0)
3154                                                 return log_oom();
3155                                 }
3156                         } else {
3157                                 /* Otherwise, assume it's a literal key. Let's do some superficial checks
3158                                  * before accept it though. */
3159 
3160                                 if (string_has_cc(optarg, NULL))
3161                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Authorized key contains control characters, refusing.");
3162                                 if (optarg[0] == '#')
3163                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified key is a comment?");
3164 
3165                                 add = strv_new(optarg);
3166                                 if (!add)
3167                                         return log_oom();
3168                         }
3169 
3170                         v = json_variant_ref(json_variant_by_key(arg_identity_extra_privileged, "sshAuthorizedKeys"));
3171                         if (v) {
3172                                 r = json_variant_strv(v, &l);
3173                                 if (r < 0)
3174                                         return log_error_errno(r, "Failed to parse SSH authorized keys list: %m");
3175                         }
3176 
3177                         r = strv_extend_strv(&l, add, true);
3178                         if (r < 0)
3179                                 return log_oom();
3180 
3181                         v = json_variant_unref(v);
3182 
3183                         r = json_variant_new_array_strv(&v, l);
3184                         if (r < 0)
3185                                 return log_oom();
3186 
3187                         r = json_variant_set_field(&arg_identity_extra_privileged, "sshAuthorizedKeys", v);
3188                         if (r < 0)
3189                                 return log_error_errno(r, "Failed to set authorized keys: %m");
3190 
3191                         break;
3192                 }
3193 
3194                 case ARG_NOT_BEFORE:
3195                 case ARG_NOT_AFTER:
3196                 case 'e': {
3197                         const char *field;
3198                         usec_t n;
3199 
3200                         field =           c == ARG_NOT_BEFORE ? "notBeforeUSec" :
3201                                 IN_SET(c, ARG_NOT_AFTER, 'e') ? "notAfterUSec" : NULL;
3202 
3203                         assert(field);
3204 
3205                         if (isempty(optarg)) {
3206                                 r = drop_from_identity(field);
3207                                 if (r < 0)
3208                                         return r;
3209 
3210                                 break;
3211                         }
3212 
3213                         /* Note the minor discrepancy regarding -e parsing here: we support that for compat
3214                          * reasons, and in the original useradd(8) implementation it accepts dates in the
3215                          * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
3216                          * with greater precision. */
3217                         r = parse_timestamp(optarg, &n);
3218                         if (r < 0)
3219                                 return log_error_errno(r, "Failed to parse %s parameter: %m", field);
3220 
3221                         r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
3222                         if (r < 0)
3223                                 return log_error_errno(r, "Failed to set %s field: %m", field);
3224                         break;
3225                 }
3226 
3227                 case ARG_PASSWORD_CHANGE_MIN:
3228                 case ARG_PASSWORD_CHANGE_MAX:
3229                 case ARG_PASSWORD_CHANGE_WARN:
3230                 case ARG_PASSWORD_CHANGE_INACTIVE: {
3231                         const char *field;
3232                         usec_t n;
3233 
3234                         field =      c == ARG_PASSWORD_CHANGE_MIN ? "passwordChangeMinUSec" :
3235                                      c == ARG_PASSWORD_CHANGE_MAX ? "passwordChangeMaxUSec" :
3236                                     c == ARG_PASSWORD_CHANGE_WARN ? "passwordChangeWarnUSec" :
3237                                 c == ARG_PASSWORD_CHANGE_INACTIVE ? "passwordChangeInactiveUSec" :
3238                                                                     NULL;
3239 
3240                         assert(field);
3241 
3242                         if (isempty(optarg)) {
3243                                 r = drop_from_identity(field);
3244                                 if (r < 0)
3245                                         return r;
3246 
3247                                 break;
3248                         }
3249 
3250                         r = parse_sec(optarg, &n);
3251                         if (r < 0)
3252                                 return log_error_errno(r, "Failed to parse %s parameter: %m", field);
3253 
3254                         r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
3255                         if (r < 0)
3256                                 return log_error_errno(r, "Failed to set %s field: %m", field);
3257                         break;
3258                 }
3259 
3260                 case ARG_STORAGE:
3261                 case ARG_FS_TYPE:
3262                 case ARG_LUKS_CIPHER:
3263                 case ARG_LUKS_CIPHER_MODE:
3264                 case ARG_LUKS_PBKDF_TYPE:
3265                 case ARG_LUKS_PBKDF_HASH_ALGORITHM: {
3266 
3267                         const char *field =
3268                                                   c == ARG_STORAGE ? "storage" :
3269                                                   c == ARG_FS_TYPE ? "fileSystemType" :
3270                                               c == ARG_LUKS_CIPHER ? "luksCipher" :
3271                                          c == ARG_LUKS_CIPHER_MODE ? "luksCipherMode" :
3272                                           c == ARG_LUKS_PBKDF_TYPE ? "luksPbkdfType" :
3273                                 c == ARG_LUKS_PBKDF_HASH_ALGORITHM ? "luksPbkdfHashAlgorithm" : NULL;
3274 
3275                         assert(field);
3276 
3277                         if (isempty(optarg)) {
3278                                 r = drop_from_identity(field);
3279                                 if (r < 0)
3280                                         return r;
3281 
3282                                 break;
3283                         }
3284 
3285                         if (!string_is_safe(optarg))
3286                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parameter for %s field not valid: %s", field, optarg);
3287 
3288                         r = json_variant_set_field_string(
3289                                         IN_SET(c, ARG_STORAGE, ARG_FS_TYPE) ?
3290                                         &arg_identity_extra_this_machine :
3291                                         &arg_identity_extra, field, optarg);
3292                         if (r < 0)
3293                                 return log_error_errno(r, "Failed to set %s field: %m", field);
3294 
3295                         break;
3296                 }
3297 
3298                 case ARG_LUKS_PBKDF_TIME_COST:
3299                 case ARG_RATE_LIMIT_INTERVAL:
3300                 case ARG_STOP_DELAY: {
3301                         const char *field =
3302                                 c == ARG_LUKS_PBKDF_TIME_COST ? "luksPbkdfTimeCostUSec" :
3303                                  c == ARG_RATE_LIMIT_INTERVAL ? "rateLimitIntervalUSec" :
3304                                           c == ARG_STOP_DELAY ? "stopDelayUSec" :
3305                                                                 NULL;
3306                         usec_t t;
3307 
3308                         assert(field);
3309 
3310                         if (isempty(optarg)) {
3311                                 r = drop_from_identity(field);
3312                                 if (r < 0)
3313                                         return r;
3314 
3315                                 break;
3316                         }
3317 
3318                         r = parse_sec(optarg, &t);
3319                         if (r < 0)
3320                                 return log_error_errno(r, "Failed to parse %s field: %s", field, optarg);
3321 
3322                         r = json_variant_set_field_unsigned(&arg_identity_extra, field, t);
3323                         if (r < 0)
3324                                 return log_error_errno(r, "Failed to set %s field: %m", field);
3325 
3326                         break;
3327                 }
3328 
3329                 case 'G': {
3330                         const char *p = optarg;
3331 
3332                         if (isempty(p)) {
3333                                 r = drop_from_identity("memberOf");
3334                                 if (r < 0)
3335                                         return r;
3336 
3337                                 break;
3338                         }
3339 
3340                         for (;;) {
3341                                 _cleanup_(json_variant_unrefp) JsonVariant *mo = NULL;
3342                                 _cleanup_strv_free_ char **list = NULL;
3343                                 _cleanup_free_ char *word = NULL;
3344 
3345                                 r = extract_first_word(&p, &word, ",", 0);
3346                                 if (r < 0)
3347                                         return log_error_errno(r, "Failed to parse group list: %m");
3348                                 if (r == 0)
3349                                         break;
3350 
3351                                 if (!valid_user_group_name(word, 0))
3352                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid group name %s.", word);
3353 
3354                                 mo = json_variant_ref(json_variant_by_key(arg_identity_extra, "memberOf"));
3355 
3356                                 r = json_variant_strv(mo, &list);
3357                                 if (r < 0)
3358                                         return log_error_errno(r, "Failed to parse group list: %m");
3359 
3360                                 r = strv_extend(&list, word);
3361                                 if (r < 0)
3362                                         return log_oom();
3363 
3364                                 strv_sort(list);
3365                                 strv_uniq(list);
3366 
3367                                 mo = json_variant_unref(mo);
3368                                 r = json_variant_new_array_strv(&mo, list);
3369                                 if (r < 0)
3370                                         return log_error_errno(r, "Failed to create group list JSON: %m");
3371 
3372                                 r = json_variant_set_field(&arg_identity_extra, "memberOf", mo);
3373                                 if (r < 0)
3374                                         return log_error_errno(r, "Failed to update group list: %m");
3375                         }
3376 
3377                         break;
3378                 }
3379 
3380                 case ARG_TASKS_MAX: {
3381                         uint64_t u;
3382 
3383                         if (isempty(optarg)) {
3384                                 r = drop_from_identity("tasksMax");
3385                                 if (r < 0)
3386                                         return r;
3387                                 break;
3388                         }
3389 
3390                         r = safe_atou64(optarg, &u);
3391                         if (r < 0)
3392                                 return log_error_errno(r, "Failed to parse --tasks-max= parameter: %s", optarg);
3393 
3394                         r = json_variant_set_field_unsigned(&arg_identity_extra, "tasksMax", u);
3395                         if (r < 0)
3396                                 return log_error_errno(r, "Failed to set tasksMax field: %m");
3397 
3398                         break;
3399                 }
3400 
3401                 case ARG_MEMORY_MAX:
3402                 case ARG_MEMORY_HIGH:
3403                 case ARG_LUKS_PBKDF_MEMORY_COST: {
3404                         const char *field =
3405                                             c == ARG_MEMORY_MAX ? "memoryMax" :
3406                                            c == ARG_MEMORY_HIGH ? "memoryHigh" :
3407                                 c == ARG_LUKS_PBKDF_MEMORY_COST ? "luksPbkdfMemoryCost" : NULL;
3408 
3409                         uint64_t u;
3410 
3411                         assert(field);
3412 
3413                         if (isempty(optarg)) {
3414                                 r = drop_from_identity(field);
3415                                 if (r < 0)
3416                                         return r;
3417                                 break;
3418                         }
3419 
3420                         r = parse_size(optarg, 1024, &u);
3421                         if (r < 0)
3422                                 return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
3423 
3424                         r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, field, u);
3425                         if (r < 0)
3426                                 return log_error_errno(r, "Failed to set %s field: %m", field);
3427 
3428                         break;
3429                 }
3430 
3431                 case ARG_CPU_WEIGHT:
3432                 case ARG_IO_WEIGHT: {
3433                         const char *field = c == ARG_CPU_WEIGHT ? "cpuWeight" :
3434                                             c == ARG_IO_WEIGHT ? "ioWeight" : NULL;
3435                         uint64_t u;
3436 
3437                         assert(field);
3438 
3439                         if (isempty(optarg)) {
3440                                 r = drop_from_identity(field);
3441                                 if (r < 0)
3442                                         return r;
3443                                 break;
3444                         }
3445 
3446                         r = safe_atou64(optarg, &u);
3447                         if (r < 0)
3448                                 return log_error_errno(r, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg);
3449 
3450                         if (!CGROUP_WEIGHT_IS_OK(u))
3451                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Weight %" PRIu64 " is out of valid weight range.", u);
3452 
3453                         r = json_variant_set_field_unsigned(&arg_identity_extra, field, u);
3454                         if (r < 0)
3455                                 return log_error_errno(r, "Failed to set %s field: %m", field);
3456 
3457                         break;
3458                 }
3459 
3460                 case ARG_PKCS11_TOKEN_URI:
3461                         if (streq(optarg, "list"))
3462                                 return pkcs11_list_tokens();
3463 
3464                         /* If --pkcs11-token-uri= is specified we always drop everything old */
3465                         FOREACH_STRING(p, "pkcs11TokenUri", "pkcs11EncryptedKey") {
3466                                 r = drop_from_identity(p);
3467                                 if (r < 0)
3468                                         return r;
3469                         }
3470 
3471                         if (isempty(optarg)) {
3472                                 arg_pkcs11_token_uri = strv_free(arg_pkcs11_token_uri);
3473                                 break;
3474                         }
3475 
3476                         if (streq(optarg, "auto")) {
3477                                 _cleanup_free_ char *found = NULL;
3478 
3479                                 r = pkcs11_find_token_auto(&found);
3480                                 if (r < 0)
3481                                         return r;
3482                                 r = strv_consume(&arg_pkcs11_token_uri, TAKE_PTR(found));
3483                         } else {
3484                                 if (!pkcs11_uri_valid(optarg))
3485                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg);
3486 
3487                                 r = strv_extend(&arg_pkcs11_token_uri, optarg);
3488                         }
3489                         if (r < 0)
3490                                 return r;
3491 
3492                         strv_uniq(arg_pkcs11_token_uri);
3493                         break;
3494 
3495                 case ARG_FIDO2_CRED_ALG:
3496                         r = parse_fido2_algorithm(optarg, &arg_fido2_cred_alg);
3497                         if (r < 0)
3498                                 return log_error_errno(r, "Failed to parse COSE algorithm: %s", optarg);
3499                         break;
3500 
3501                 case ARG_FIDO2_DEVICE:
3502                         if (streq(optarg, "list"))
3503                                 return fido2_list_devices();
3504 
3505                         FOREACH_STRING(p, "fido2HmacCredential", "fido2HmacSalt") {
3506                                 r = drop_from_identity(p);
3507                                 if (r < 0)
3508                                         return r;
3509                         }
3510 
3511                         if (isempty(optarg)) {
3512                                 arg_fido2_device = strv_free(arg_fido2_device);
3513                                 break;
3514                         }
3515 
3516                         if (streq(optarg, "auto")) {
3517                                 _cleanup_free_ char *found = NULL;
3518 
3519                                 r = fido2_find_device_auto(&found);
3520                                 if (r < 0)
3521                                         return r;
3522 
3523                                 r = strv_consume(&arg_fido2_device, TAKE_PTR(found));
3524                         } else
3525                                 r = strv_extend(&arg_fido2_device, optarg);
3526                         if (r < 0)
3527                                 return r;
3528 
3529                         strv_uniq(arg_fido2_device);
3530                         break;
3531 
3532                 case ARG_FIDO2_WITH_PIN: {
3533                         bool lock_with_pin;
3534 
3535                         r = parse_boolean_argument("--fido2-with-client-pin=", optarg, &lock_with_pin);
3536                         if (r < 0)
3537                                 return r;
3538 
3539                         SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_PIN, lock_with_pin);
3540                         break;
3541                 }
3542 
3543                 case ARG_FIDO2_WITH_UP: {
3544                         bool lock_with_up;
3545 
3546                         r = parse_boolean_argument("--fido2-with-user-presence=", optarg, &lock_with_up);
3547                         if (r < 0)
3548                                 return r;
3549 
3550                         SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UP, lock_with_up);
3551                         break;
3552                 }
3553 
3554                 case ARG_FIDO2_WITH_UV: {
3555                         bool lock_with_uv;
3556 
3557                         r = parse_boolean_argument("--fido2-with-user-verification=", optarg, &lock_with_uv);
3558                         if (r < 0)
3559                                 return r;
3560 
3561                         SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UV, lock_with_uv);
3562                         break;
3563                 }
3564 
3565                 case ARG_RECOVERY_KEY:
3566                         r = parse_boolean(optarg);
3567                         if (r < 0)
3568                                 return log_error_errno(r, "Failed to parse --recovery-key= argument: %s", optarg);
3569 
3570                         arg_recovery_key = r;
3571 
3572                         FOREACH_STRING(p, "recoveryKey", "recoveryKeyType") {
3573                                 r = drop_from_identity(p);
3574                                 if (r < 0)
3575                                         return r;
3576                         }
3577 
3578                         break;
3579 
3580                 case ARG_AUTO_RESIZE_MODE:
3581                         if (isempty(optarg)) {
3582                                 r = drop_from_identity("autoResizeMode");
3583                                 if (r < 0)
3584                                         return r;
3585 
3586                                 break;
3587                         }
3588 
3589                         r = auto_resize_mode_from_string(optarg);
3590                         if (r < 0)
3591                                 return log_error_errno(r, "Failed to parse --auto-resize-mode= argument: %s", optarg);
3592 
3593                         r = json_variant_set_field_string(&arg_identity_extra, "autoResizeMode", auto_resize_mode_to_string(r));
3594                         if (r < 0)
3595                                 return log_error_errno(r, "Failed to set autoResizeMode field: %m");
3596 
3597                         break;
3598 
3599                 case ARG_REBALANCE_WEIGHT: {
3600                         uint64_t u;
3601 
3602                         if (isempty(optarg)) {
3603                                 r = drop_from_identity("rebalanceWeight");
3604                                 if (r < 0)
3605                                         return r;
3606                         }
3607 
3608                         if (streq(optarg, "off"))
3609                                 u = REBALANCE_WEIGHT_OFF;
3610                         else {
3611                                 r = safe_atou64(optarg, &u);
3612                                 if (r < 0)
3613                                         return log_error_errno(r, "Failed to parse --rebalance-weight= argument: %s", optarg);
3614 
3615                                 if (u < REBALANCE_WEIGHT_MIN || u > REBALANCE_WEIGHT_MAX)
3616                                         return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Rebalancing weight out of valid range %" PRIu64 "…%" PRIu64 ": %s",
3617                                                                REBALANCE_WEIGHT_MIN, REBALANCE_WEIGHT_MAX, optarg);
3618                         }
3619 
3620                         /* Drop from per machine stuff and everywhere */
3621                         r = drop_from_identity("rebalanceWeight");
3622                         if (r < 0)
3623                                 return r;
3624 
3625                         /* Add to main identity */
3626                         r = json_variant_set_field_unsigned(&arg_identity_extra, "rebalanceWeight", u);
3627                         if (r < 0)
3628                                 return log_error_errno(r, "Failed to set rebalanceWeight field: %m");
3629 
3630                         break;
3631                 }
3632 
3633                 case 'j':
3634                         arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
3635                         break;
3636 
3637                 case ARG_JSON:
3638                         r = parse_json_argument(optarg, &arg_json_format_flags);
3639                         if (r <= 0)
3640                                 return r;
3641 
3642                         break;
3643 
3644                 case 'E':
3645                         if (arg_export_format == EXPORT_FORMAT_FULL)
3646                                 arg_export_format = EXPORT_FORMAT_STRIPPED;
3647                         else if (arg_export_format == EXPORT_FORMAT_STRIPPED)
3648                                 arg_export_format = EXPORT_FORMAT_MINIMAL;
3649                         else
3650                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specifying -E more than twice is not supported.");
3651 
3652                         arg_json_format_flags &= ~JSON_FORMAT_OFF;
3653                         if (arg_json_format_flags == 0)
3654                                 arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
3655                         break;
3656 
3657                 case ARG_EXPORT_FORMAT:
3658                         if (streq(optarg, "full"))
3659                                 arg_export_format = EXPORT_FORMAT_FULL;
3660                         else if (streq(optarg, "stripped"))
3661                                 arg_export_format = EXPORT_FORMAT_STRIPPED;
3662                         else if (streq(optarg, "minimal"))
3663                                 arg_export_format = EXPORT_FORMAT_MINIMAL;
3664                         else if (streq(optarg, "help")) {
3665                                 puts("full\n"
3666                                      "stripped\n"
3667                                      "minimal");
3668                                 return 0;
3669                         }
3670 
3671                         break;
3672 
3673                 case ARG_AND_RESIZE:
3674                         arg_and_resize = true;
3675                         break;
3676 
3677                 case ARG_AND_CHANGE_PASSWORD:
3678                         arg_and_change_password = true;
3679                         break;
3680 
3681                 case ARG_DROP_CACHES: {
3682                         bool drop_caches;
3683 
3684                         if (isempty(optarg)) {
3685                                 r = drop_from_identity("dropCaches");
3686                                 if (r < 0)
3687                                         return r;
3688                         }
3689 
3690                         r = parse_boolean_argument("--drop-caches=", optarg, &drop_caches);
3691                         if (r < 0)
3692                                 return r;
3693 
3694                         r = json_variant_set_field_boolean(&arg_identity_extra, "dropCaches", r);
3695                         if (r < 0)
3696                                 return log_error_errno(r, "Failed to set drop caches field: %m");
3697 
3698                         break;
3699                 }
3700 
3701                 case '?':
3702                         return -EINVAL;
3703 
3704                 default:
3705                         assert_not_reached();
3706                 }
3707         }
3708 
3709         if (!strv_isempty(arg_pkcs11_token_uri) || !strv_isempty(arg_fido2_device))
3710                 arg_and_change_password = true;
3711 
3712         if (arg_disk_size != UINT64_MAX || arg_disk_size_relative != UINT64_MAX)
3713                 arg_and_resize = true;
3714 
3715         return 1;
3716 }
3717 
redirect_bus_mgr(void)3718 static int redirect_bus_mgr(void) {
3719         const char *suffix;
3720 
3721         /* Talk to a different service if that's requested. (The same env var is also understood by homed, so
3722          * that it is relatively easily possible to invoke a second instance of homed for debug purposes and
3723          * have homectl talk to it, without colliding with the host version. This is handy when operating
3724          * from a homed-managed account.) */
3725 
3726         suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
3727         if (suffix) {
3728                 static BusLocator locator = {
3729                         .path = "/org/freedesktop/home1",
3730                         .interface = "org.freedesktop.home1.Manager",
3731                 };
3732 
3733                 /* Yes, we leak this memory, but there's little point to collect this, given that we only do
3734                  * this in a debug environment, do it only once, and the string shall live for out entire
3735                  * process runtime. */
3736 
3737                 locator.destination = strjoin("org.freedesktop.home1.", suffix);
3738                 if (!locator.destination)
3739                         return log_oom();
3740 
3741                 bus_mgr = &locator;
3742         } else
3743                 bus_mgr = bus_home_mgr;
3744 
3745         return 0;
3746 }
3747 
run(int argc,char * argv[])3748 static int run(int argc, char *argv[]) {
3749         static const Verb verbs[] = {
3750                 { "help",           VERB_ANY, VERB_ANY, 0,            help                 },
3751                 { "list",           VERB_ANY, 1,        VERB_DEFAULT, list_homes           },
3752                 { "activate",       2,        VERB_ANY, 0,            activate_home        },
3753                 { "deactivate",     2,        VERB_ANY, 0,            deactivate_home      },
3754                 { "inspect",        VERB_ANY, VERB_ANY, 0,            inspect_home         },
3755                 { "authenticate",   VERB_ANY, VERB_ANY, 0,            authenticate_home    },
3756                 { "create",         VERB_ANY, 2,        0,            create_home          },
3757                 { "remove",         2,        VERB_ANY, 0,            remove_home          },
3758                 { "update",         VERB_ANY, 2,        0,            update_home          },
3759                 { "passwd",         VERB_ANY, 2,        0,            passwd_home          },
3760                 { "resize",         2,        3,        0,            resize_home          },
3761                 { "lock",           2,        VERB_ANY, 0,            lock_home            },
3762                 { "unlock",         2,        VERB_ANY, 0,            unlock_home          },
3763                 { "with",           2,        VERB_ANY, 0,            with_home            },
3764                 { "lock-all",       VERB_ANY, 1,        0,            lock_all_homes       },
3765                 { "deactivate-all", VERB_ANY, 1,        0,            deactivate_all_homes },
3766                 { "rebalance",      VERB_ANY, 1,        0,            rebalance            },
3767                 {}
3768         };
3769 
3770         int r;
3771 
3772         log_setup();
3773 
3774         r = redirect_bus_mgr();
3775         if (r < 0)
3776                 return r;
3777 
3778         r = parse_argv(argc, argv);
3779         if (r <= 0)
3780                 return r;
3781 
3782         return dispatch_verb(argc, argv, verbs, NULL);
3783 }
3784 
3785 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
3786