]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/homectl.c
f27c7f7875448dbde0cad4a19235cfaac2b37f12
[thirdparty/systemd.git] / src / home / homectl.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <getopt.h>
4 #include <unistd.h>
5
6 #include "sd-bus.h"
7
8 #include "ask-password-api.h"
9 #include "bitfield.h"
10 #include "build.h"
11 #include "bus-common-errors.h"
12 #include "bus-error.h"
13 #include "bus-locator.h"
14 #include "bus-util.h"
15 #include "cap-list.h"
16 #include "capability-util.h"
17 #include "cgroup-util.h"
18 #include "creds-util.h"
19 #include "dirent-util.h"
20 #include "dns-domain.h"
21 #include "env-util.h"
22 #include "errno-util.h"
23 #include "extract-word.h"
24 #include "fd-util.h"
25 #include "fileio.h"
26 #include "format-table.h"
27 #include "format-util.h"
28 #include "fs-util.h"
29 #include "glyph-util.h"
30 #include "hashmap.h"
31 #include "hexdecoct.h"
32 #include "home-util.h"
33 #include "homectl-fido2.h"
34 #include "homectl-pkcs11.h"
35 #include "homectl-recovery-key.h"
36 #include "json-util.h"
37 #include "libfido2-util.h"
38 #include "locale-util.h"
39 #include "main-func.h"
40 #include "openssl-util.h"
41 #include "pager.h"
42 #include "parse-argument.h"
43 #include "parse-util.h"
44 #include "password-quality-util.h"
45 #include "path-util.h"
46 #include "percent-util.h"
47 #include "pkcs11-util.h"
48 #include "polkit-agent.h"
49 #include "pretty-print.h"
50 #include "proc-cmdline.h"
51 #include "process-util.h"
52 #include "recurse-dir.h"
53 #include "rlimit-util.h"
54 #include "runtime-scope.h"
55 #include "stat-util.h"
56 #include "string-util.h"
57 #include "strv.h"
58 #include "terminal-util.h"
59 #include "time-util.h"
60 #include "uid-classification.h"
61 #include "user-record.h"
62 #include "user-record-password-quality.h"
63 #include "user-record-show.h"
64 #include "user-record-util.h"
65 #include "user-util.h"
66 #include "userdb.h"
67 #include "verbs.h"
68
69 static PagerFlags arg_pager_flags = 0;
70 static bool arg_legend = true;
71 static bool arg_ask_password = true;
72 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
73 static const char *arg_host = NULL;
74 static bool arg_offline = false;
75 static const char *arg_identity = NULL;
76 static sd_json_variant *arg_identity_extra = NULL;
77 static sd_json_variant *arg_identity_extra_privileged = NULL;
78 static sd_json_variant *arg_identity_extra_this_machine = NULL;
79 static sd_json_variant *arg_identity_extra_other_machines = NULL;
80 static sd_json_variant *arg_identity_extra_rlimits = NULL;
81 static char **arg_identity_filter = NULL; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
82 static char **arg_identity_filter_rlimits = NULL;
83 static uint64_t arg_disk_size = UINT64_MAX;
84 static uint64_t arg_disk_size_relative = UINT64_MAX;
85 static char **arg_pkcs11_token_uri = NULL;
86 static char **arg_fido2_device = NULL;
87 static Fido2EnrollFlags arg_fido2_lock_with = FIDO2ENROLL_PIN | FIDO2ENROLL_UP;
88 #if HAVE_LIBFIDO2
89 static int arg_fido2_cred_alg = COSE_ES256;
90 #else
91 static int arg_fido2_cred_alg = 0;
92 #endif
93 static bool arg_recovery_key = false;
94 static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
95 static bool arg_and_resize = false;
96 static bool arg_and_change_password = false;
97 static enum {
98 EXPORT_FORMAT_FULL, /* export the full record */
99 EXPORT_FORMAT_STRIPPED, /* strip "state" + "binding", but leave signature in place */
100 EXPORT_FORMAT_MINIMAL, /* also strip signature */
101 } arg_export_format = EXPORT_FORMAT_FULL;
102 static uint64_t arg_capability_bounding_set = UINT64_MAX;
103 static uint64_t arg_capability_ambient_set = UINT64_MAX;
104 static bool arg_prompt_new_user = false;
105 static char *arg_blob_dir = NULL;
106 static bool arg_blob_clear = false;
107 static Hashmap *arg_blob_files = NULL;
108 static char *arg_key_name = NULL;
109 static bool arg_dry_run = false;
110 static bool arg_seize = true;
111
112 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, sd_json_variant_unrefp);
113 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, sd_json_variant_unrefp);
114 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_other_machines, sd_json_variant_unrefp);
115 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged, sd_json_variant_unrefp);
116 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits, sd_json_variant_unrefp);
117 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter, strv_freep);
118 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits, strv_freep);
119 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, strv_freep);
120 STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, strv_freep);
121 STATIC_DESTRUCTOR_REGISTER(arg_blob_dir, freep);
122 STATIC_DESTRUCTOR_REGISTER(arg_blob_files, hashmap_freep);
123 STATIC_DESTRUCTOR_REGISTER(arg_key_name, freep);
124
125 static const BusLocator *bus_mgr;
126
127 static bool identity_properties_specified(void) {
128 return
129 arg_identity ||
130 !sd_json_variant_is_blank_object(arg_identity_extra) ||
131 !sd_json_variant_is_blank_object(arg_identity_extra_privileged) ||
132 !sd_json_variant_is_blank_object(arg_identity_extra_this_machine) ||
133 !sd_json_variant_is_blank_object(arg_identity_extra_other_machines) ||
134 !sd_json_variant_is_blank_object(arg_identity_extra_rlimits) ||
135 !strv_isempty(arg_identity_filter) ||
136 !strv_isempty(arg_identity_filter_rlimits) ||
137 !strv_isempty(arg_pkcs11_token_uri) ||
138 !strv_isempty(arg_fido2_device) ||
139 arg_blob_dir ||
140 arg_blob_clear ||
141 !hashmap_isempty(arg_blob_files);
142 }
143
144 static int acquire_bus(sd_bus **bus) {
145 int r;
146
147 assert(bus);
148
149 if (*bus)
150 return 0;
151
152 r = bus_connect_transport(arg_transport, arg_host, RUNTIME_SCOPE_SYSTEM, bus);
153 if (r < 0)
154 return bus_log_connect_error(r, arg_transport, RUNTIME_SCOPE_SYSTEM);
155
156 (void) sd_bus_set_allow_interactive_authorization(*bus, arg_ask_password);
157
158 return 0;
159 }
160
161 static int list_homes(int argc, char *argv[], void *userdata) {
162 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
163 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
164 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
165 _cleanup_(table_unrefp) Table *table = NULL;
166 int r;
167
168 r = acquire_bus(&bus);
169 if (r < 0)
170 return r;
171
172 r = bus_call_method(bus, bus_mgr, "ListHomes", &error, &reply, NULL);
173 if (r < 0)
174 return log_error_errno(r, "Failed to list homes: %s", bus_error_message(&error, r));
175
176 table = table_new("name", "uid", "gid", "state", "realname", "home", "shell");
177 if (!table)
178 return log_oom();
179
180 r = sd_bus_message_enter_container(reply, 'a', "(susussso)");
181 if (r < 0)
182 return bus_log_parse_error(r);
183
184 for (;;) {
185 const char *name, *state, *realname, *home, *shell, *color;
186 TableCell *cell;
187 uint32_t uid, gid;
188
189 r = sd_bus_message_read(reply, "(susussso)", &name, &uid, &state, &gid, &realname, &home, &shell, NULL);
190 if (r < 0)
191 return bus_log_parse_error(r);
192 if (r == 0)
193 break;
194
195 r = table_add_many(table,
196 TABLE_STRING, name,
197 TABLE_UID, uid,
198 TABLE_GID, gid);
199 if (r < 0)
200 return table_log_add_error(r);
201
202 r = table_add_cell(table, &cell, TABLE_STRING, state);
203 if (r < 0)
204 return table_log_add_error(r);
205
206 color = user_record_state_color(state);
207 if (color)
208 (void) table_set_color(table, cell, color);
209
210 r = table_add_many(table,
211 TABLE_STRING, strna(empty_to_null(realname)),
212 TABLE_STRING, home,
213 TABLE_STRING, strna(empty_to_null(shell)));
214 if (r < 0)
215 return table_log_add_error(r);
216 }
217
218 r = sd_bus_message_exit_container(reply);
219 if (r < 0)
220 return bus_log_parse_error(r);
221
222 if (!table_isempty(table) || sd_json_format_enabled(arg_json_format_flags)) {
223 r = table_set_sort(table, (size_t) 0);
224 if (r < 0)
225 return table_log_sort_error(r);
226
227 r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
228 if (r < 0)
229 return r;
230 }
231
232 if (arg_legend && !sd_json_format_enabled(arg_json_format_flags)) {
233 if (table_isempty(table))
234 printf("No home areas.\n");
235 else
236 printf("\n%zu home areas listed.\n", table_get_rows(table) - 1);
237 }
238
239 return 0;
240 }
241
242 static int acquire_existing_password(
243 const char *user_name,
244 UserRecord *hr,
245 bool emphasize_current_password,
246 AskPasswordFlags flags) {
247
248 _cleanup_strv_free_erase_ char **password = NULL;
249 _cleanup_(erase_and_freep) char *envpw = NULL;
250 _cleanup_free_ char *question = NULL;
251 int r;
252
253 assert(user_name);
254 assert(hr);
255
256 r = getenv_steal_erase("PASSWORD", &envpw);
257 if (r < 0)
258 return log_error_errno(r, "Failed to acquire password from environment: %m");
259 if (r > 0) {
260 /* People really shouldn't use environment variables for passing passwords. We support this
261 * only for testing purposes, and do not document the behaviour, so that people won't
262 * actually use this outside of testing. */
263
264 r = user_record_set_password(hr, STRV_MAKE(envpw), true);
265 if (r < 0)
266 return log_error_errno(r, "Failed to store password: %m");
267
268 return 1;
269 }
270
271 /* If this is not our own user, then don't use the password cache */
272 if (is_this_me(user_name) <= 0)
273 SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, false);
274
275 if (asprintf(&question, emphasize_current_password ?
276 "Please enter current password for user %s:" :
277 "Please enter password for user %s:",
278 user_name) < 0)
279 return log_oom();
280
281 AskPasswordRequest req = {
282 .tty_fd = -EBADF,
283 .message = question,
284 .icon = "user-home",
285 .keyring = "home-password",
286 .credential = "home.password",
287 .until = USEC_INFINITY,
288 .hup_fd = -EBADF,
289 };
290
291 r = ask_password_auto(&req, flags, &password);
292 if (r == -EUNATCH) { /* EUNATCH is returned if no password was found and asking interactively was
293 * disabled via the flags. Not an error for us. */
294 log_debug_errno(r, "No passwords acquired.");
295 return 0;
296 }
297 if (r < 0)
298 return log_error_errno(r, "Failed to acquire password: %m");
299
300 r = user_record_set_password(hr, password, true);
301 if (r < 0)
302 return log_error_errno(r, "Failed to store password: %m");
303
304 return 1;
305 }
306
307 static int acquire_recovery_key(
308 const char *user_name,
309 UserRecord *hr,
310 AskPasswordFlags flags) {
311
312 _cleanup_strv_free_erase_ char **recovery_key = NULL;
313 _cleanup_(erase_and_freep) char *envpw = NULL;
314 _cleanup_free_ char *question = NULL;
315 int r;
316
317 assert(user_name);
318 assert(hr);
319
320 r = getenv_steal_erase("PASSWORD", &envpw);
321 if (r < 0)
322 return log_error_errno(r, "Failed to acquire password from environment: %m");
323 if (r > 0) {
324 /* People really shouldn't use environment variables for passing secrets. We support this
325 * only for testing purposes, and do not document the behaviour, so that people won't
326 * actually use this outside of testing. */
327
328 r = user_record_set_password(hr, STRV_MAKE(envpw), true); /* recovery keys are stored in the record exactly like regular passwords! */
329 if (r < 0)
330 return log_error_errno(r, "Failed to store recovery key: %m");
331
332 return 1;
333 }
334
335 /* If this is not our own user, then don't use the password cache */
336 if (is_this_me(user_name) <= 0)
337 SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, false);
338
339 if (asprintf(&question, "Please enter recovery key for user %s:", user_name) < 0)
340 return log_oom();
341
342 AskPasswordRequest req = {
343 .tty_fd = -EBADF,
344 .message = question,
345 .icon = "user-home",
346 .keyring = "home-recovery-key",
347 .credential = "home.recovery-key",
348 .until = USEC_INFINITY,
349 .hup_fd = -EBADF,
350 };
351
352 r = ask_password_auto(&req, flags, &recovery_key);
353 if (r == -EUNATCH) { /* EUNATCH is returned if no recovery key was found and asking interactively was
354 * disabled via the flags. Not an error for us. */
355 log_debug_errno(r, "No recovery keys acquired.");
356 return 0;
357 }
358 if (r < 0)
359 return log_error_errno(r, "Failed to acquire recovery keys: %m");
360
361 r = user_record_set_password(hr, recovery_key, true);
362 if (r < 0)
363 return log_error_errno(r, "Failed to store recovery keys: %m");
364
365 return 1;
366 }
367
368 static int acquire_token_pin(
369 const char *user_name,
370 UserRecord *hr,
371 AskPasswordFlags flags) {
372
373 _cleanup_strv_free_erase_ char **pin = NULL;
374 _cleanup_(erase_and_freep) char *envpin = NULL;
375 _cleanup_free_ char *question = NULL;
376 int r;
377
378 assert(user_name);
379 assert(hr);
380
381 r = getenv_steal_erase("PIN", &envpin);
382 if (r < 0)
383 return log_error_errno(r, "Failed to acquire PIN from environment: %m");
384 if (r > 0) {
385 r = user_record_set_token_pin(hr, STRV_MAKE(envpin), false);
386 if (r < 0)
387 return log_error_errno(r, "Failed to store token PIN: %m");
388
389 return 1;
390 }
391
392 /* If this is not our own user, then don't use the password cache */
393 if (is_this_me(user_name) <= 0)
394 SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, false);
395
396 if (asprintf(&question, "Please enter security token PIN for user %s:", user_name) < 0)
397 return log_oom();
398
399 AskPasswordRequest req = {
400 .tty_fd = -EBADF,
401 .message = question,
402 .icon = "user-home",
403 .keyring = "token-pin",
404 .credential = "home.token-pin",
405 .until = USEC_INFINITY,
406 .hup_fd = -EBADF,
407 };
408
409 r = ask_password_auto(&req, flags, &pin);
410 if (r == -EUNATCH) { /* EUNATCH is returned if no PIN was found and asking interactively was disabled
411 * via the flags. Not an error for us. */
412 log_debug_errno(r, "No security token PINs acquired.");
413 return 0;
414 }
415 if (r < 0)
416 return log_error_errno(r, "Failed to acquire security token PIN: %m");
417
418 r = user_record_set_token_pin(hr, pin, false);
419 if (r < 0)
420 return log_error_errno(r, "Failed to store security token PIN: %m");
421
422 return 1;
423 }
424
425 static int handle_generic_user_record_error(
426 const char *user_name,
427 UserRecord *hr,
428 const sd_bus_error *error,
429 int ret,
430 bool emphasize_current_password) {
431 int r;
432
433 assert(user_name);
434 assert(hr);
435
436 if (sd_bus_error_has_name(error, BUS_ERROR_HOME_ABSENT))
437 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE),
438 "Home of user %s is currently absent, please plug in the necessary storage device or backing file system.", user_name);
439
440 else if (sd_bus_error_has_name(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT))
441 return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS),
442 "Too frequent login attempts for user %s, try again later.", user_name);
443
444 else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD)) {
445
446 if (!strv_isempty(hr->password))
447 log_notice("Password incorrect or not sufficient, please try again.");
448
449 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
450 * let's push what we acquire here into the cache */
451 r = acquire_existing_password(
452 user_name,
453 hr,
454 emphasize_current_password,
455 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
456 if (r < 0)
457 return r;
458
459 } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_RECOVERY_KEY)) {
460
461 if (!strv_isempty(hr->password))
462 log_notice("Recovery key incorrect or not sufficient, please try again.");
463
464 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
465 * let's push what we acquire here into the cache */
466 r = acquire_recovery_key(
467 user_name,
468 hr,
469 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
470 if (r < 0)
471 return r;
472
473 } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) {
474
475 if (strv_isempty(hr->password))
476 log_notice("Security token not inserted, please enter password.");
477 else
478 log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
479
480 r = acquire_existing_password(
481 user_name,
482 hr,
483 emphasize_current_password,
484 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
485 if (r < 0)
486 return r;
487
488 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
489
490 /* First time the PIN is requested, let's accept cached data, and allow using credential store */
491 r = acquire_token_pin(
492 user_name,
493 hr,
494 ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_PUSH_CACHE);
495 if (r < 0)
496 return r;
497
498 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) {
499
500 log_notice("%s%sPlease authenticate physically on security token.",
501 emoji_enabled() ? glyph(GLYPH_TOUCH) : "",
502 emoji_enabled() ? " " : "");
503
504 r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, true);
505 if (r < 0)
506 return log_error_errno(r, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
507
508 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
509
510 log_notice("%s%sPlease confirm presence on security token.",
511 emoji_enabled() ? glyph(GLYPH_TOUCH) : "",
512 emoji_enabled() ? " " : "");
513
514 r = user_record_set_fido2_user_presence_permitted(hr, true);
515 if (r < 0)
516 return log_error_errno(r, "Failed to set FIDO2 user presence permitted flag: %m");
517
518 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED)) {
519
520 log_notice("%s%sPlease verify user on security token.",
521 emoji_enabled() ? glyph(GLYPH_TOUCH) : "",
522 emoji_enabled() ? " " : "");
523
524 r = user_record_set_fido2_user_verification_permitted(hr, true);
525 if (r < 0)
526 return log_error_errno(r, "Failed to set FIDO2 user verification permitted flag: %m");
527
528 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED))
529 return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
530
531 else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
532
533 log_notice("Security token PIN incorrect, please try again.");
534
535 /* If the previous PIN was wrong don't accept cached info anymore, but add to cache. Also, don't use the credential data */
536 r = acquire_token_pin(
537 user_name,
538 hr,
539 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
540 if (r < 0)
541 return r;
542
543 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT)) {
544
545 log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
546
547 r = acquire_token_pin(
548 user_name,
549 hr,
550 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
551 if (r < 0)
552 return r;
553
554 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT)) {
555
556 log_notice("Security token PIN incorrect, please try again (only one try left!).");
557
558 r = acquire_token_pin(
559 user_name,
560 hr,
561 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
562 if (r < 0)
563 return r;
564 } else
565 return log_error_errno(ret, "Operation on home %s failed: %s", user_name, bus_error_message(error, ret));
566
567 return 0;
568 }
569
570 static int acquire_passed_secrets(const char *user_name, UserRecord **ret) {
571 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
572 int r;
573
574 assert(ret);
575
576 /* Generates an initial secret objects that contains passwords supplied via $PASSWORD, the password
577 * cache or the credentials subsystem, but excluding any interactive stuff. If nothing is passed,
578 * returns an empty secret object. */
579
580 secret = user_record_new();
581 if (!secret)
582 return log_oom();
583
584 r = acquire_existing_password(
585 user_name,
586 secret,
587 /* emphasize_current_password = */ false,
588 ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_NO_TTY | ASK_PASSWORD_NO_AGENT);
589 if (r < 0)
590 return r;
591
592 r = acquire_token_pin(
593 user_name,
594 secret,
595 ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_NO_TTY | ASK_PASSWORD_NO_AGENT);
596 if (r < 0)
597 return r;
598
599 r = acquire_recovery_key(
600 user_name,
601 secret,
602 ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_NO_TTY | ASK_PASSWORD_NO_AGENT);
603 if (r < 0)
604 return r;
605
606 *ret = TAKE_PTR(secret);
607 return 0;
608 }
609
610 static int activate_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_(user_record_unrefp) UserRecord *secret = NULL;
620
621 r = acquire_passed_secrets(*i, &secret);
622 if (r < 0)
623 return r;
624
625 for (;;) {
626 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
627 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
628
629 r = bus_message_new_method_call(bus, &m, bus_mgr, "ActivateHome");
630 if (r < 0)
631 return bus_log_create_error(r);
632
633 r = sd_bus_message_append(m, "s", *i);
634 if (r < 0)
635 return bus_log_create_error(r);
636
637 r = bus_message_append_secret(m, secret);
638 if (r < 0)
639 return bus_log_create_error(r);
640
641 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
642 if (r < 0) {
643 r = handle_generic_user_record_error(*i, secret, &error, r, /* emphasize_current_password= */ false);
644 if (r < 0) {
645 if (ret == 0)
646 ret = r;
647
648 break;
649 }
650 } else
651 break;
652 }
653 }
654
655 return ret;
656 }
657
658 static int deactivate_home(int argc, char *argv[], void *userdata) {
659 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
660 int r, ret = 0;
661
662 r = acquire_bus(&bus);
663 if (r < 0)
664 return r;
665
666 STRV_FOREACH(i, strv_skip(argv, 1)) {
667 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
668 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
669
670 r = bus_message_new_method_call(bus, &m, bus_mgr, "DeactivateHome");
671 if (r < 0)
672 return bus_log_create_error(r);
673
674 r = sd_bus_message_append(m, "s", *i);
675 if (r < 0)
676 return bus_log_create_error(r);
677
678 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
679 if (r < 0) {
680 log_error_errno(r, "Failed to deactivate user home: %s", bus_error_message(&error, r));
681 if (ret == 0)
682 ret = r;
683 }
684 }
685
686 return ret;
687 }
688
689 static void dump_home_record(UserRecord *hr) {
690 int r;
691
692 assert(hr);
693
694 if (hr->incomplete) {
695 fflush(stdout);
696 log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr->user_name);
697 }
698
699 if (!sd_json_format_enabled(arg_json_format_flags))
700 user_record_show(hr, true);
701 else {
702 _cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
703
704 if (arg_export_format == EXPORT_FORMAT_STRIPPED)
705 r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED|USER_RECORD_PERMISSIVE, &stripped);
706 else if (arg_export_format == EXPORT_FORMAT_MINIMAL)
707 r = user_record_clone(hr, USER_RECORD_EXTRACT_SIGNABLE|USER_RECORD_PERMISSIVE, &stripped);
708 else
709 r = 0;
710 if (r < 0)
711 log_warning_errno(r, "Failed to strip user record, ignoring: %m");
712 if (stripped)
713 hr = stripped;
714
715 sd_json_variant_dump(hr->json, arg_json_format_flags, stdout, NULL);
716 }
717 }
718
719 static int inspect_home(sd_bus *bus, const char *name) {
720 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
721 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
722 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
723 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
724 const char *json;
725 int incomplete;
726 uid_t uid;
727 int r;
728
729 r = parse_uid(name, &uid);
730 if (r < 0)
731 r = bus_call_method(bus, bus_mgr, "GetUserRecordByName", &error, &reply, "s", name);
732 else
733 r = bus_call_method(bus, bus_mgr, "GetUserRecordByUID", &error, &reply, "u", (uint32_t) uid);
734 if (r < 0)
735 return log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
736
737 r = sd_bus_message_read(reply, "sbo", &json, &incomplete, NULL);
738 if (r < 0)
739 return bus_log_parse_error(r);
740
741 r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, NULL, NULL);
742 if (r < 0)
743 return log_error_errno(r, "Failed to parse JSON identity: %m");
744
745 hr = user_record_new();
746 if (!hr)
747 return log_oom();
748
749 r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG|USER_RECORD_PERMISSIVE);
750 if (r < 0)
751 return r;
752
753 hr->incomplete = incomplete;
754 dump_home_record(hr);
755 return 0;
756 }
757
758 static int inspect_homes(int argc, char *argv[], void *userdata) {
759 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
760 int r;
761
762 r = acquire_bus(&bus);
763 if (r < 0)
764 return r;
765
766 pager_open(arg_pager_flags);
767
768 char **args = strv_skip(argv, 1);
769 if (args) {
770 STRV_FOREACH(arg, args)
771 RET_GATHER(r, inspect_home(bus, *arg));
772 return r;
773 } else {
774 _cleanup_free_ char *myself = getusername_malloc();
775 if (!myself)
776 return log_oom();
777
778 return inspect_home(bus, myself);
779 }
780 }
781
782 static int authenticate_home(sd_bus *bus, const char *name) {
783 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
784 int r;
785
786 r = acquire_passed_secrets(name, &secret);
787 if (r < 0)
788 return r;
789
790 for (;;) {
791 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
792 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
793
794 r = bus_message_new_method_call(bus, &m, bus_mgr, "AuthenticateHome");
795 if (r < 0)
796 return bus_log_create_error(r);
797
798 r = sd_bus_message_append(m, "s", name);
799 if (r < 0)
800 return bus_log_create_error(r);
801
802 r = bus_message_append_secret(m, secret);
803 if (r < 0)
804 return bus_log_create_error(r);
805
806 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
807 if (r < 0) {
808 r = handle_generic_user_record_error(name, secret, &error, r, false);
809 if (r >= 0)
810 continue;
811 }
812 return r;
813 }
814 }
815
816 static int authenticate_homes(int argc, char *argv[], void *userdata) {
817 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
818 int r;
819
820 r = acquire_bus(&bus);
821 if (r < 0)
822 return r;
823
824 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
825
826 char **args = strv_skip(argv, 1);
827 if (args) {
828 STRV_FOREACH(arg, args)
829 RET_GATHER(r, authenticate_home(bus, *arg));
830
831 return r;
832 } else {
833 _cleanup_free_ char *myself = getusername_malloc();
834 if (!myself)
835 return log_oom();
836
837 return authenticate_home(bus, myself);
838 }
839 }
840
841 static int update_last_change(sd_json_variant **v, bool with_password, bool override) {
842 sd_json_variant *c;
843 usec_t n;
844 int r;
845
846 assert(v);
847
848 n = now(CLOCK_REALTIME);
849
850 c = sd_json_variant_by_key(*v, "lastChangeUSec");
851 if (c) {
852 uint64_t u;
853
854 if (!override)
855 goto update_password;
856
857 if (!sd_json_variant_is_unsigned(c))
858 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastChangeUSec field is not an unsigned integer, refusing.");
859
860 u = sd_json_variant_unsigned(c);
861 if (u >= n)
862 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastChangeUSec is from the future, can't update.");
863 }
864
865 r = sd_json_variant_set_field_unsigned(v, "lastChangeUSec", n);
866 if (r < 0)
867 return log_error_errno(r, "Failed to update lastChangeUSec: %m");
868
869 update_password:
870 if (!with_password)
871 return 0;
872
873 c = sd_json_variant_by_key(*v, "lastPasswordChangeUSec");
874 if (c) {
875 uint64_t u;
876
877 if (!override)
878 return 0;
879
880 if (!sd_json_variant_is_unsigned(c))
881 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastPasswordChangeUSec field is not an unsigned integer, refusing.");
882
883 u = sd_json_variant_unsigned(c);
884 if (u >= n)
885 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastPasswordChangeUSec is from the future, can't update.");
886 }
887
888 r = sd_json_variant_set_field_unsigned(v, "lastPasswordChangeUSec", n);
889 if (r < 0)
890 return log_error_errno(r, "Failed to update lastPasswordChangeUSec: %m");
891
892 return 1;
893 }
894
895 static int apply_identity_changes(sd_json_variant **_v) {
896 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
897 int r;
898
899 assert(_v);
900
901 v = sd_json_variant_ref(*_v);
902
903 r = sd_json_variant_filter(&v, arg_identity_filter);
904 if (r < 0)
905 return log_error_errno(r, "Failed to filter identity: %m");
906
907 r = sd_json_variant_merge_object(&v, arg_identity_extra);
908 if (r < 0)
909 return log_error_errno(r, "Failed to merge identities: %m");
910
911 if (arg_identity_extra_this_machine || arg_identity_extra_other_machines || !strv_isempty(arg_identity_filter)) {
912 _cleanup_(sd_json_variant_unrefp) sd_json_variant *per_machine = NULL, *mmid = NULL;
913 sd_id128_t mid;
914
915 r = sd_id128_get_machine(&mid);
916 if (r < 0)
917 return log_error_errno(r, "Failed to acquire machine ID: %m");
918
919 r = sd_json_variant_new_string(&mmid, SD_ID128_TO_STRING(mid));
920 if (r < 0)
921 return log_error_errno(r, "Failed to allocate matchMachineId object: %m");
922
923 per_machine = sd_json_variant_ref(sd_json_variant_by_key(v, "perMachine"));
924 if (per_machine) {
925 _cleanup_(sd_json_variant_unrefp) sd_json_variant *npm = NULL, *positive = NULL, *negative = NULL;
926 _cleanup_free_ sd_json_variant **array = NULL;
927 sd_json_variant *z;
928 size_t i = 0;
929
930 if (!sd_json_variant_is_array(per_machine))
931 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "perMachine field is not an array, refusing.");
932
933 array = new(sd_json_variant*, sd_json_variant_elements(per_machine) + 2);
934 if (!array)
935 return log_oom();
936
937 JSON_VARIANT_ARRAY_FOREACH(z, per_machine) {
938 sd_json_variant *u;
939
940 if (!sd_json_variant_is_object(z))
941 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "perMachine entry is not an object, refusing.");
942
943 array[i++] = z;
944
945 u = sd_json_variant_by_key(z, "matchMachineId");
946 if (u && sd_json_variant_equal(u, mmid))
947 r = sd_json_variant_merge_object(&positive, z);
948 else {
949 u = sd_json_variant_by_key(z, "matchNotMachineId");
950 if (!u || !sd_json_variant_equal(u, mmid))
951 continue;
952
953 r = sd_json_variant_merge_object(&negative, z);
954 }
955 if (r < 0)
956 return log_error_errno(r, "Failed to merge perMachine entry: %m");
957
958 i--;
959 }
960
961 r = sd_json_variant_filter(&positive, arg_identity_filter);
962 if (r < 0)
963 return log_error_errno(r, "Failed to filter perMachine: %m");
964
965 r = sd_json_variant_filter(&negative, arg_identity_filter);
966 if (r < 0)
967 return log_error_errno(r, "Failed to filter perMachine: %m");
968
969 r = sd_json_variant_merge_object(&positive, arg_identity_extra_this_machine);
970 if (r < 0)
971 return log_error_errno(r, "Failed to merge in perMachine fields: %m");
972
973 r = sd_json_variant_merge_object(&negative, arg_identity_extra_other_machines);
974 if (r < 0)
975 return log_error_errno(r, "Failed to merge in perMachine fields: %m");
976
977 if (arg_identity_filter_rlimits || arg_identity_extra_rlimits) {
978 _cleanup_(sd_json_variant_unrefp) sd_json_variant *rlv = NULL;
979
980 rlv = sd_json_variant_ref(sd_json_variant_by_key(positive, "resourceLimits"));
981
982 r = sd_json_variant_filter(&rlv, arg_identity_filter_rlimits);
983 if (r < 0)
984 return log_error_errno(r, "Failed to filter resource limits: %m");
985
986 r = sd_json_variant_merge_object(&rlv, arg_identity_extra_rlimits);
987 if (r < 0)
988 return log_error_errno(r, "Failed to set resource limits: %m");
989
990 if (sd_json_variant_is_blank_object(rlv)) {
991 r = sd_json_variant_filter(&positive, STRV_MAKE("resourceLimits"));
992 if (r < 0)
993 return log_error_errno(r, "Failed to drop resource limits field from identity: %m");
994 } else {
995 r = sd_json_variant_set_field(&positive, "resourceLimits", rlv);
996 if (r < 0)
997 return log_error_errno(r, "Failed to update resource limits of identity: %m");
998 }
999 }
1000
1001 if (!sd_json_variant_is_blank_object(positive)) {
1002 r = sd_json_variant_set_field(&positive, "matchMachineId", mmid);
1003 if (r < 0)
1004 return log_error_errno(r, "Failed to set matchMachineId field: %m");
1005
1006 array[i++] = positive;
1007 }
1008
1009 if (!sd_json_variant_is_blank_object(negative)) {
1010 r = sd_json_variant_set_field(&negative, "matchNotMachineId", mmid);
1011 if (r < 0)
1012 return log_error_errno(r, "Failed to set matchNotMachineId field: %m");
1013
1014 array[i++] = negative;
1015 }
1016
1017 r = sd_json_variant_new_array(&npm, array, i);
1018 if (r < 0)
1019 return log_error_errno(r, "Failed to allocate new perMachine array: %m");
1020
1021 sd_json_variant_unref(per_machine);
1022 per_machine = TAKE_PTR(npm);
1023 } else {
1024 _cleanup_(sd_json_variant_unrefp) sd_json_variant *positive = sd_json_variant_ref(arg_identity_extra_this_machine),
1025 *negative = sd_json_variant_ref(arg_identity_extra_other_machines);
1026
1027 if (arg_identity_extra_rlimits) {
1028 r = sd_json_variant_set_field(&positive, "resourceLimits", arg_identity_extra_rlimits);
1029 if (r < 0)
1030 return log_error_errno(r, "Failed to update resource limits of identity: %m");
1031 }
1032
1033 if (positive) {
1034 r = sd_json_variant_set_field(&positive, "matchMachineId", mmid);
1035 if (r < 0)
1036 return log_error_errno(r, "Failed to set matchMachineId field: %m");
1037
1038 r = sd_json_variant_append_array(&per_machine, positive);
1039 if (r < 0)
1040 return log_error_errno(r, "Failed to append to perMachine array: %m");
1041 }
1042
1043 if (negative) {
1044 r = sd_json_variant_set_field(&negative, "matchNotMachineId", mmid);
1045 if (r < 0)
1046 return log_error_errno(r, "Failed to set matchNotMachineId field: %m");
1047
1048 r = sd_json_variant_append_array(&per_machine, negative);
1049 if (r < 0)
1050 return log_error_errno(r, "Failed to append to perMachine array: %m");
1051 }
1052 }
1053
1054 r = sd_json_variant_set_field(&v, "perMachine", per_machine);
1055 if (r < 0)
1056 return log_error_errno(r, "Failed to update per machine record: %m");
1057 }
1058
1059 if (arg_identity_extra_privileged || arg_identity_filter) {
1060 _cleanup_(sd_json_variant_unrefp) sd_json_variant *privileged = NULL;
1061
1062 privileged = sd_json_variant_ref(sd_json_variant_by_key(v, "privileged"));
1063
1064 r = sd_json_variant_filter(&privileged, arg_identity_filter);
1065 if (r < 0)
1066 return log_error_errno(r, "Failed to filter identity (privileged part): %m");
1067
1068 r = sd_json_variant_merge_object(&privileged, arg_identity_extra_privileged);
1069 if (r < 0)
1070 return log_error_errno(r, "Failed to merge identities (privileged part): %m");
1071
1072 if (sd_json_variant_is_blank_object(privileged)) {
1073 r = sd_json_variant_filter(&v, STRV_MAKE("privileged"));
1074 if (r < 0)
1075 return log_error_errno(r, "Failed to drop privileged part from identity: %m");
1076 } else {
1077 r = sd_json_variant_set_field(&v, "privileged", privileged);
1078 if (r < 0)
1079 return log_error_errno(r, "Failed to update privileged part of identity: %m");
1080 }
1081 }
1082
1083 if (arg_identity_filter_rlimits) {
1084 _cleanup_(sd_json_variant_unrefp) sd_json_variant *rlv = NULL;
1085
1086 rlv = sd_json_variant_ref(sd_json_variant_by_key(v, "resourceLimits"));
1087
1088 r = sd_json_variant_filter(&rlv, arg_identity_filter_rlimits);
1089 if (r < 0)
1090 return log_error_errno(r, "Failed to filter resource limits: %m");
1091
1092 /* Note that we only filter resource limits here, but don't apply them. We do that in the perMachine section */
1093
1094 if (sd_json_variant_is_blank_object(rlv)) {
1095 r = sd_json_variant_filter(&v, STRV_MAKE("resourceLimits"));
1096 if (r < 0)
1097 return log_error_errno(r, "Failed to drop resource limits field from identity: %m");
1098 } else {
1099 r = sd_json_variant_set_field(&v, "resourceLimits", rlv);
1100 if (r < 0)
1101 return log_error_errno(r, "Failed to update resource limits of identity: %m");
1102 }
1103 }
1104
1105 sd_json_variant_unref(*_v);
1106 *_v = TAKE_PTR(v);
1107
1108 return 0;
1109 }
1110
1111 static int add_disposition(sd_json_variant **v) {
1112 int r;
1113
1114 assert(v);
1115
1116 if (sd_json_variant_by_key(*v, "disposition"))
1117 return 0;
1118
1119 /* Set the disposition to regular, if not configured explicitly */
1120 r = sd_json_variant_set_field_string(v, "disposition", "regular");
1121 if (r < 0)
1122 return log_error_errno(r, "Failed to set disposition field: %m");
1123
1124 return 1;
1125 }
1126
1127 static int acquire_new_home_record(sd_json_variant *input, UserRecord **ret) {
1128 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
1129 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
1130 int r;
1131
1132 assert(ret);
1133
1134 if (arg_identity) {
1135 unsigned line = 0, column = 0;
1136
1137 if (input)
1138 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Two identity records specified, refusing.");
1139
1140 r = sd_json_parse_file(
1141 streq(arg_identity, "-") ? stdin : NULL,
1142 streq(arg_identity, "-") ? "<stdin>" : arg_identity, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
1143 if (r < 0)
1144 return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
1145 } else
1146 v = sd_json_variant_ref(input);
1147
1148 r = apply_identity_changes(&v);
1149 if (r < 0)
1150 return r;
1151
1152 r = add_disposition(&v);
1153 if (r < 0)
1154 return r;
1155
1156 STRV_FOREACH(i, arg_pkcs11_token_uri) {
1157 r = identity_add_pkcs11_key_data(&v, *i);
1158 if (r < 0)
1159 return r;
1160 }
1161
1162 STRV_FOREACH(i, arg_fido2_device) {
1163 r = identity_add_fido2_parameters(&v, *i, arg_fido2_lock_with, arg_fido2_cred_alg);
1164 if (r < 0)
1165 return r;
1166 }
1167
1168 if (arg_recovery_key) {
1169 r = identity_add_recovery_key(&v);
1170 if (r < 0)
1171 return r;
1172 }
1173
1174 r = update_last_change(&v, true, false);
1175 if (r < 0)
1176 return r;
1177
1178 if (DEBUG_LOGGING)
1179 sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY, NULL, NULL);
1180
1181 hr = user_record_new();
1182 if (!hr)
1183 return log_oom();
1184
1185 r = user_record_load(
1186 hr,
1187 v,
1188 USER_RECORD_REQUIRE_REGULAR|
1189 USER_RECORD_ALLOW_SECRET|
1190 USER_RECORD_ALLOW_PRIVILEGED|
1191 USER_RECORD_ALLOW_PER_MACHINE|
1192 USER_RECORD_STRIP_BINDING|
1193 USER_RECORD_STRIP_STATUS|
1194 (arg_seize ? USER_RECORD_STRIP_SIGNATURE : USER_RECORD_ALLOW_SIGNATURE) |
1195 USER_RECORD_LOG|
1196 USER_RECORD_PERMISSIVE);
1197 if (r < 0)
1198 return r;
1199
1200 *ret = TAKE_PTR(hr);
1201 return 0;
1202 }
1203
1204 static int acquire_new_password(
1205 const char *user_name,
1206 UserRecord *hr,
1207 bool suggest,
1208 char **ret) {
1209
1210 _cleanup_(erase_and_freep) char *envpw = NULL;
1211 unsigned i = 5;
1212 int r;
1213
1214 assert(user_name);
1215 assert(hr);
1216
1217 r = getenv_steal_erase("NEWPASSWORD", &envpw);
1218 if (r < 0)
1219 return log_error_errno(r, "Failed to acquire password from environment: %m");
1220 if (r > 0) {
1221 /* As above, this is not for use, just for testing */
1222
1223 r = user_record_set_password(hr, STRV_MAKE(envpw), /* prepend = */ true);
1224 if (r < 0)
1225 return log_error_errno(r, "Failed to store password: %m");
1226
1227 if (ret)
1228 *ret = TAKE_PTR(envpw);
1229
1230 return 0;
1231 }
1232
1233 if (suggest)
1234 (void) suggest_passwords();
1235
1236 for (;;) {
1237 _cleanup_strv_free_erase_ char **first = NULL, **second = NULL;
1238 _cleanup_free_ char *question = NULL;
1239
1240 if (--i == 0)
1241 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up.");
1242
1243 if (asprintf(&question, "Please enter new password for user %s:", user_name) < 0)
1244 return log_oom();
1245
1246 AskPasswordRequest req = {
1247 .tty_fd = -EBADF,
1248 .message = question,
1249 .icon = "user-home",
1250 .keyring = "home-password",
1251 .credential = "home.new-password",
1252 .until = USEC_INFINITY,
1253 .hup_fd = -EBADF,
1254 };
1255
1256 r = ask_password_auto(
1257 &req,
1258 /* flags= */ 0, /* no caching, we want to collect a new password here after all */
1259 &first);
1260 if (r < 0)
1261 return log_error_errno(r, "Failed to acquire password: %m");
1262
1263 assert(!strv_isempty(first));
1264
1265 question = mfree(question);
1266 if (asprintf(&question, "Please enter new password for user %s (repeat):", user_name) < 0)
1267 return log_oom();
1268
1269 req.message = question;
1270
1271 r = ask_password_auto(
1272 &req,
1273 /* flags= */ 0, /* no caching */
1274 &second);
1275 if (r < 0)
1276 return log_error_errno(r, "Failed to acquire password: %m");
1277
1278 if (strv_equal(first, second)) {
1279 _cleanup_(erase_and_freep) char *copy = NULL;
1280
1281 if (ret) {
1282 copy = strdup(first[0]);
1283 if (!copy)
1284 return log_oom();
1285 }
1286
1287 r = user_record_set_password(hr, first, /* prepend = */ true);
1288 if (r < 0)
1289 return log_error_errno(r, "Failed to store password: %m");
1290
1291 if (ret)
1292 *ret = TAKE_PTR(copy);
1293
1294 return 0;
1295 }
1296
1297 log_error("Password didn't match, try again.");
1298 }
1299 }
1300
1301 static int acquire_merged_blob_dir(UserRecord *hr, bool existing, Hashmap **ret) {
1302 _cleanup_free_ char *sys_blob_path = NULL;
1303 _cleanup_hashmap_free_ Hashmap *blobs = NULL;
1304 _cleanup_closedir_ DIR *d = NULL;
1305 const char *src_blob_path, *filename;
1306 void *fd_ptr;
1307 int r;
1308
1309 assert(ret);
1310
1311 HASHMAP_FOREACH_KEY(fd_ptr, filename, arg_blob_files) {
1312 _cleanup_free_ char *filename_dup = NULL;
1313 _cleanup_close_ int fd_dup = -EBADF;
1314
1315 filename_dup = strdup(filename);
1316 if (!filename_dup)
1317 return log_oom();
1318
1319 if (PTR_TO_FD(fd_ptr) != -EBADF) {
1320 fd_dup = fcntl(PTR_TO_FD(fd_ptr), F_DUPFD_CLOEXEC, 3);
1321 if (fd_dup < 0)
1322 return log_error_errno(errno, "Failed to duplicate fd of %s: %m", filename);
1323 }
1324
1325 r = hashmap_ensure_put(&blobs, &blob_fd_hash_ops, filename_dup, FD_TO_PTR(fd_dup));
1326 if (r < 0)
1327 return r;
1328 TAKE_PTR(filename_dup); /* Ownership transferred to hashmap */
1329 TAKE_FD(fd_dup);
1330 }
1331
1332 if (arg_blob_dir)
1333 src_blob_path = arg_blob_dir;
1334 else if (existing && !arg_blob_clear) {
1335 if (hr->blob_directory)
1336 src_blob_path = hr->blob_directory;
1337 else {
1338 /* This isn't technically a correct thing to do for generic user records,
1339 * so anyone looking at this code for reference shouldn't replicate it.
1340 * However, since homectl is tied to homed, this is OK. This adds robustness
1341 * for situations where the user record is coming directly from the CLI and
1342 * thus doesn't have a blobDirectory set */
1343
1344 sys_blob_path = path_join(home_system_blob_dir(), hr->user_name);
1345 if (!sys_blob_path)
1346 return log_oom();
1347
1348 src_blob_path = sys_blob_path;
1349 }
1350 } else
1351 goto nodir; /* Shortcut: no dir to merge with, so just return copy of arg_blob_files */
1352
1353 d = opendir(src_blob_path);
1354 if (!d)
1355 return log_error_errno(errno, "Failed to open %s: %m", src_blob_path);
1356
1357 FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read %s: %m", src_blob_path)) {
1358 _cleanup_free_ char *name = NULL;
1359 _cleanup_close_ int fd = -EBADF;
1360
1361 if (dot_or_dot_dot(de->d_name))
1362 continue;
1363
1364 if (hashmap_contains(blobs, de->d_name))
1365 continue; /* arg_blob_files should override the base dir */
1366
1367 if (!suitable_blob_filename(de->d_name)) {
1368 log_warning("File %s in blob directory %s has an invalid filename. Skipping.", de->d_name, src_blob_path);
1369 continue;
1370 }
1371
1372 name = strdup(de->d_name);
1373 if (!name)
1374 return log_oom();
1375
1376 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1377 if (fd < 0)
1378 return log_error_errno(errno, "Failed to open %s in %s: %m", de->d_name, src_blob_path);
1379
1380 r = fd_verify_regular(fd);
1381 if (r < 0) {
1382 log_warning_errno(r, "Entry %s in blob directory %s is not a regular file. Skipping.", de->d_name, src_blob_path);
1383 continue;
1384 }
1385
1386 r = hashmap_ensure_put(&blobs, &blob_fd_hash_ops, name, FD_TO_PTR(fd));
1387 if (r < 0)
1388 return r;
1389 TAKE_PTR(name); /* Ownership transferred to hashmap */
1390 TAKE_FD(fd);
1391 }
1392
1393 nodir:
1394 *ret = TAKE_PTR(blobs);
1395 return 0;
1396 }
1397
1398 static int bus_message_append_blobs(sd_bus_message *m, Hashmap *blobs) {
1399 const char *filename;
1400 void *fd_ptr;
1401 int r;
1402
1403 assert(m);
1404
1405 r = sd_bus_message_open_container(m, 'a', "{sh}");
1406 if (r < 0)
1407 return r;
1408
1409 HASHMAP_FOREACH_KEY(fd_ptr, filename, blobs) {
1410 int fd = PTR_TO_FD(fd_ptr);
1411
1412 if (fd == -EBADF) /* File marked for deletion */
1413 continue;
1414
1415 r = sd_bus_message_append(m, "{sh}", filename, fd);
1416 if (r < 0)
1417 return r;
1418 }
1419
1420 return sd_bus_message_close_container(m);
1421 }
1422
1423 static int create_home_common(sd_json_variant *input, bool show_enforce_password_policy_hint) {
1424 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1425 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
1426 _cleanup_hashmap_free_ Hashmap *blobs = NULL;
1427 int r;
1428
1429 r = acquire_new_home_record(input, &hr);
1430 if (r < 0)
1431 return r;
1432
1433 r = acquire_merged_blob_dir(hr, false, &blobs);
1434 if (r < 0)
1435 return r;
1436
1437 /* If the JSON record carries no plain text password (besides the recovery key), then let's query it
1438 * manually. */
1439 if (strv_length(hr->password) <= arg_recovery_key) {
1440
1441 if (strv_isempty(hr->hashed_password)) {
1442 _cleanup_(erase_and_freep) char *new_password = NULL;
1443
1444 /* No regular (i.e. non-PKCS#11) hashed passwords set in the record, let's fix that. */
1445 r = acquire_new_password(hr->user_name, hr, /* suggest = */ true, &new_password);
1446 if (r < 0)
1447 return r;
1448
1449 r = user_record_make_hashed_password(hr, STRV_MAKE(new_password), /* extend = */ false);
1450 if (r < 0)
1451 return log_error_errno(r, "Failed to hash password: %m");
1452 } else {
1453 /* There's a hash password set in the record, acquire the unhashed version of it. */
1454 r = acquire_existing_password(
1455 hr->user_name,
1456 hr,
1457 /* emphasize_current_password= */ false,
1458 ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_PUSH_CACHE);
1459 if (r < 0)
1460 return r;
1461 }
1462 }
1463
1464 if (hr->enforce_password_policy == 0) {
1465 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1466
1467 /* If password quality enforcement is disabled, let's at least warn client side */
1468
1469 r = user_record_check_password_quality(hr, hr, &error);
1470 if (r < 0)
1471 log_warning_errno(r, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error, r));
1472 }
1473
1474 if (arg_dry_run) {
1475 sd_json_variant_dump(hr->json, SD_JSON_FORMAT_COLOR_AUTO|SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_NEWLINE, stderr, /* prefix= */ NULL);
1476 return 0;
1477 }
1478
1479 r = acquire_bus(&bus);
1480 if (r < 0)
1481 return r;
1482
1483 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1484
1485 for (;;) {
1486 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1487 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1488 _cleanup_(erase_and_freep) char *formatted = NULL;
1489
1490 r = sd_json_variant_format(hr->json, 0, &formatted);
1491 if (r < 0)
1492 return log_error_errno(r, "Failed to format user record: %m");
1493
1494 r = bus_message_new_method_call(bus, &m, bus_mgr, "CreateHomeEx");
1495 if (r < 0)
1496 return bus_log_create_error(r);
1497
1498 (void) sd_bus_message_sensitive(m);
1499
1500 r = sd_bus_message_append(m, "s", formatted);
1501 if (r < 0)
1502 return bus_log_create_error(r);
1503
1504 r = bus_message_append_blobs(m, blobs);
1505 if (r < 0)
1506 return bus_log_create_error(r);
1507
1508 r = sd_bus_message_append(m, "t", UINT64_C(0));
1509 if (r < 0)
1510 return bus_log_create_error(r);
1511
1512 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1513 if (r < 0) {
1514 if (sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY)) {
1515 _cleanup_(erase_and_freep) char *new_password = NULL;
1516
1517 log_error_errno(r, "%s", bus_error_message(&error, r));
1518 if (show_enforce_password_policy_hint)
1519 log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
1520
1521 r = acquire_new_password(hr->user_name, hr, /* suggest = */ false, &new_password);
1522 if (r < 0)
1523 return r;
1524
1525 r = user_record_make_hashed_password(hr, STRV_MAKE(new_password), /* extend = */ false);
1526 if (r < 0)
1527 return log_error_errno(r, "Failed to hash passwords: %m");
1528 } else {
1529 r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
1530 if (r < 0)
1531 return r;
1532 }
1533 } else
1534 break; /* done */
1535 }
1536
1537 return 0;
1538 }
1539
1540 static int create_home(int argc, char *argv[], void *userdata) {
1541 int r;
1542
1543 if (argc >= 2) {
1544 /* If a username was specified, use it */
1545
1546 if (valid_user_group_name(argv[1], 0))
1547 r = sd_json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]);
1548 else {
1549 _cleanup_free_ char *un = NULL, *rr = NULL;
1550
1551 /* Before we consider the user name invalid, let's check if we can split it? */
1552 r = split_user_name_realm(argv[1], &un, &rr);
1553 if (r < 0)
1554 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid.", argv[1]);
1555
1556 if (rr) {
1557 r = sd_json_variant_set_field_string(&arg_identity_extra, "realm", rr);
1558 if (r < 0)
1559 return log_error_errno(r, "Failed to set realm field: %m");
1560 }
1561
1562 r = sd_json_variant_set_field_string(&arg_identity_extra, "userName", un);
1563 }
1564 if (r < 0)
1565 return log_error_errno(r, "Failed to set userName field: %m");
1566 } else {
1567 /* If neither a username nor an identity have been specified we cannot operate. */
1568 if (!arg_identity)
1569 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required.");
1570 }
1571
1572 return create_home_common(/* input= */ NULL, /* show_enforce_password_policy_hint= */ true);
1573 }
1574
1575 static int verb_adopt_home(int argc, char *argv[], void *userdata) {
1576 int r, ret = 0;
1577
1578 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1579 r = acquire_bus(&bus);
1580 if (r < 0)
1581 return r;
1582
1583 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1584
1585 STRV_FOREACH(i, strv_skip(argv, 1)) {
1586 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1587 r = bus_message_new_method_call(bus, &m, bus_mgr, "AdoptHome");
1588 if (r < 0)
1589 return bus_log_create_error(r);
1590
1591 r = sd_bus_message_append(m, "st", *i, UINT64_C(0));
1592 if (r < 0)
1593 return bus_log_create_error(r);
1594
1595 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1596 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1597 if (r < 0) {
1598 log_error_errno(r, "Failed to adopt home: %s", bus_error_message(&error, r));
1599 if (ret == 0)
1600 ret = r;
1601 }
1602 }
1603
1604 return ret;
1605 }
1606
1607 static int register_home_common(sd_bus *bus, sd_json_variant *v) {
1608 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *_bus = NULL;
1609 int r;
1610
1611 assert(v);
1612
1613 if (!bus) {
1614 r = acquire_bus(&_bus);
1615 if (r < 0)
1616 return r;
1617 bus = _bus;
1618 }
1619
1620 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1621 r = bus_message_new_method_call(bus, &m, bus_mgr, "RegisterHome");
1622 if (r < 0)
1623 return bus_log_create_error(r);
1624
1625 _cleanup_free_ char *formatted = NULL;
1626 r = sd_json_variant_format(v, /* flags= */ 0, &formatted);
1627 if (r < 0)
1628 return log_error_errno(r, "Failed to format JSON record: %m");
1629
1630 r = sd_bus_message_append(m, "s", formatted);
1631 if (r < 0)
1632 return bus_log_create_error(r);
1633
1634 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1635 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1636 if (r < 0)
1637 return log_error_errno(r, "Failed to register home: %s", bus_error_message(&error, r));
1638
1639 return 0;
1640 }
1641
1642 static int register_home_one(sd_bus *bus, FILE *f, const char *path) {
1643 int r;
1644
1645 assert(bus);
1646 assert(path);
1647
1648 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
1649 unsigned line = 0, column = 0;
1650 r = sd_json_parse_file(f, path, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
1651 if (r < 0)
1652 return log_error_errno(r, "[%s:%u:%u] Failed to parse user record: %m", path, line, column);
1653
1654 return register_home_common(bus, v);
1655 }
1656
1657 static int verb_register_home(int argc, char *argv[], void *userdata) {
1658 int r;
1659
1660 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1661 r = acquire_bus(&bus);
1662 if (r < 0)
1663 return r;
1664
1665 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1666
1667 if (arg_identity) {
1668 if (argc > 1)
1669 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not accepting an arguments if --identity= is specified, refusing.");
1670
1671 return register_home_one(bus, /* f= */ NULL, arg_identity);
1672 }
1673
1674 if (argc == 1 || (argc == 2 && streq(argv[1], "-")))
1675 return register_home_one(bus, /* f= */ stdin, "<stdio>");
1676
1677 r = 0;
1678 STRV_FOREACH(i, strv_skip(argv, 1)) {
1679 if (streq(*i, "-"))
1680 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Refusing reading from standard input if multiple user records are specified.");
1681
1682 RET_GATHER(r, register_home_one(bus, /* f= */ NULL, *i));
1683 }
1684
1685 return r;
1686 }
1687
1688 static int verb_unregister_home(int argc, char *argv[], void *userdata) {
1689 int r;
1690
1691 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1692 r = acquire_bus(&bus);
1693 if (r < 0)
1694 return r;
1695
1696 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1697
1698 int ret = 0;
1699 STRV_FOREACH(i, strv_skip(argv, 1)) {
1700 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1701 r = bus_message_new_method_call(bus, &m, bus_mgr, "UnregisterHome");
1702 if (r < 0)
1703 return bus_log_create_error(r);
1704
1705 r = sd_bus_message_append(m, "s", *i);
1706 if (r < 0)
1707 return bus_log_create_error(r);
1708
1709 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1710 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, /* ret_reply= */ NULL);
1711 if (r < 0)
1712 RET_GATHER(ret, log_error_errno(r, "Failed to unregister home: %s", bus_error_message(&error, r)));
1713 }
1714
1715 return ret;
1716 }
1717
1718 static int remove_home(int argc, char *argv[], void *userdata) {
1719 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1720 int r, ret = 0;
1721
1722 r = acquire_bus(&bus);
1723 if (r < 0)
1724 return r;
1725
1726 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1727
1728 STRV_FOREACH(i, strv_skip(argv, 1)) {
1729 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1730 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1731
1732 r = bus_message_new_method_call(bus, &m, bus_mgr, "RemoveHome");
1733 if (r < 0)
1734 return bus_log_create_error(r);
1735
1736 r = sd_bus_message_append(m, "s", *i);
1737 if (r < 0)
1738 return bus_log_create_error(r);
1739
1740 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1741 if (r < 0) {
1742 log_error_errno(r, "Failed to remove home: %s", bus_error_message(&error, r));
1743 if (ret == 0)
1744 ret = r;
1745 }
1746 }
1747
1748 return ret;
1749 }
1750
1751 static int acquire_updated_home_record(
1752 sd_bus *bus,
1753 const char *username,
1754 UserRecord **ret) {
1755
1756 _cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL;
1757 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
1758 int r;
1759
1760 assert(ret);
1761
1762 if (arg_identity) {
1763 unsigned line = 0, column = 0;
1764 sd_json_variant *un;
1765
1766 r = sd_json_parse_file(
1767 streq(arg_identity, "-") ? stdin : NULL,
1768 streq(arg_identity, "-") ? "<stdin>" : arg_identity, SD_JSON_PARSE_SENSITIVE, &json, &line, &column);
1769 if (r < 0)
1770 return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
1771
1772 un = sd_json_variant_by_key(json, "userName");
1773 if (un) {
1774 if (!sd_json_variant_is_string(un) || (username && !streq(sd_json_variant_string(un), username)))
1775 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name specified on command line and in JSON record do not match.");
1776 } else {
1777 if (!username)
1778 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No username specified.");
1779
1780 r = sd_json_variant_set_field_string(&arg_identity_extra, "userName", username);
1781 if (r < 0)
1782 return log_error_errno(r, "Failed to set userName field: %m");
1783 }
1784
1785 } else {
1786 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1787 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1788 int incomplete;
1789 const char *text;
1790
1791 if (!identity_properties_specified())
1792 return log_error_errno(SYNTHETIC_ERRNO(EALREADY), "No field to change specified.");
1793
1794 r = bus_call_method(bus, bus_mgr, "GetUserRecordByName", &error, &reply, "s", username);
1795 if (r < 0)
1796 return log_error_errno(r, "Failed to acquire user home record: %s", bus_error_message(&error, r));
1797
1798 r = sd_bus_message_read(reply, "sbo", &text, &incomplete, NULL);
1799 if (r < 0)
1800 return bus_log_parse_error(r);
1801
1802 if (incomplete)
1803 return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Lacking rights to acquire user record including privileged metadata, can't update record.");
1804
1805 r = sd_json_parse(text, SD_JSON_PARSE_SENSITIVE, &json, NULL, NULL);
1806 if (r < 0)
1807 return log_error_errno(r, "Failed to parse JSON identity: %m");
1808
1809 reply = sd_bus_message_unref(reply);
1810
1811 r = sd_json_variant_filter(&json, STRV_MAKE("binding", "status", "signature", "blobManifest"));
1812 if (r < 0)
1813 return log_error_errno(r, "Failed to strip binding and status from record to update: %m");
1814 }
1815
1816 r = apply_identity_changes(&json);
1817 if (r < 0)
1818 return r;
1819
1820 STRV_FOREACH(i, arg_pkcs11_token_uri) {
1821 r = identity_add_pkcs11_key_data(&json, *i);
1822 if (r < 0)
1823 return r;
1824 }
1825
1826 STRV_FOREACH(i, arg_fido2_device) {
1827 r = identity_add_fido2_parameters(&json, *i, arg_fido2_lock_with, arg_fido2_cred_alg);
1828 if (r < 0)
1829 return r;
1830 }
1831
1832 /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
1833 * override. */
1834 r = update_last_change(&json, arg_pkcs11_token_uri || arg_fido2_device, !arg_identity);
1835 if (r < 0)
1836 return r;
1837
1838 if (DEBUG_LOGGING)
1839 sd_json_variant_dump(json, SD_JSON_FORMAT_PRETTY, NULL, NULL);
1840
1841 hr = user_record_new();
1842 if (!hr)
1843 return log_oom();
1844
1845 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);
1846 if (r < 0)
1847 return r;
1848
1849 *ret = TAKE_PTR(hr);
1850 return 0;
1851 }
1852
1853 static int home_record_reset_human_interaction_permission(UserRecord *hr) {
1854 int r;
1855
1856 assert(hr);
1857
1858 /* When we execute multiple operations one after the other, let's reset the permission to ask the
1859 * user each time, so that if interaction is necessary we will be told so again and thus can print a
1860 * nice message to the user, telling the user so. */
1861
1862 r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, -1);
1863 if (r < 0)
1864 return log_error_errno(r, "Failed to reset PKCS#11 protected authentication path permission flag: %m");
1865
1866 r = user_record_set_fido2_user_presence_permitted(hr, -1);
1867 if (r < 0)
1868 return log_error_errno(r, "Failed to reset FIDO2 user presence permission flag: %m");
1869
1870 r = user_record_set_fido2_user_verification_permitted(hr, -1);
1871 if (r < 0)
1872 return log_error_errno(r, "Failed to reset FIDO2 user verification permission flag: %m");
1873
1874 return 0;
1875 }
1876
1877 static int update_home(int argc, char *argv[], void *userdata) {
1878 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1879 _cleanup_(user_record_unrefp) UserRecord *hr = NULL, *secret = NULL;
1880 _cleanup_free_ char *buffer = NULL;
1881 _cleanup_hashmap_free_ Hashmap *blobs = NULL;
1882 const char *username;
1883 uint64_t flags = 0;
1884 int r;
1885
1886 if (argc >= 2)
1887 username = argv[1];
1888 else if (!arg_identity) {
1889 buffer = getusername_malloc();
1890 if (!buffer)
1891 return log_oom();
1892
1893 username = buffer;
1894 } else
1895 username = NULL;
1896
1897 r = acquire_bus(&bus);
1898 if (r < 0)
1899 return r;
1900
1901 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1902
1903 r = acquire_updated_home_record(bus, username, &hr);
1904 if (r < 0)
1905 return r;
1906
1907 /* Add in all secrets we can acquire cheaply */
1908 r = acquire_passed_secrets(username, &secret);
1909 if (r < 0)
1910 return r;
1911
1912 r = user_record_merge_secret(hr, secret);
1913 if (r < 0)
1914 return r;
1915
1916 r = acquire_merged_blob_dir(hr, true, &blobs);
1917 if (r < 0)
1918 return r;
1919
1920 if (arg_dry_run) {
1921 sd_json_variant_dump(hr->json, SD_JSON_FORMAT_COLOR_AUTO|SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_NEWLINE, stderr, /* prefix= */ NULL);
1922 return 0;
1923 }
1924
1925 /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
1926 * authentication might be confusing. */
1927
1928 if (arg_and_resize || arg_and_change_password)
1929 log_info("Updating home directory.");
1930
1931 if (arg_offline)
1932 flags |= SD_HOMED_UPDATE_OFFLINE;
1933
1934 for (;;) {
1935 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1936 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1937 _cleanup_free_ char *formatted = NULL;
1938
1939 r = bus_message_new_method_call(bus, &m, bus_mgr, "UpdateHomeEx");
1940 if (r < 0)
1941 return bus_log_create_error(r);
1942
1943 r = sd_json_variant_format(hr->json, 0, &formatted);
1944 if (r < 0)
1945 return log_error_errno(r, "Failed to format user record: %m");
1946
1947 (void) sd_bus_message_sensitive(m);
1948
1949 r = sd_bus_message_append(m, "s", formatted);
1950 if (r < 0)
1951 return bus_log_create_error(r);
1952
1953 r = bus_message_append_blobs(m, blobs);
1954 if (r < 0)
1955 return bus_log_create_error(r);
1956
1957 r = sd_bus_message_append(m, "t", flags);
1958 if (r < 0)
1959 return bus_log_create_error(r);
1960
1961 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1962 if (r < 0) {
1963 if (arg_and_change_password &&
1964 sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
1965 /* In the generic handler we'd ask for a password in this case, but when
1966 * changing passwords that's not sufficient, as we need to acquire all keys
1967 * first. */
1968 return log_error_errno(r, "Security token not inserted, refusing.");
1969
1970 r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
1971 if (r < 0)
1972 return r;
1973 } else
1974 break;
1975 }
1976
1977 if (arg_and_resize)
1978 log_info("Resizing home.");
1979
1980 (void) home_record_reset_human_interaction_permission(hr);
1981
1982 /* Also sync down disk size to underlying LUKS/fscrypt/quota */
1983 while (arg_and_resize) {
1984 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1985 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1986
1987 r = bus_message_new_method_call(bus, &m, bus_mgr, "ResizeHome");
1988 if (r < 0)
1989 return bus_log_create_error(r);
1990
1991 /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
1992 r = sd_bus_message_append(m, "st", hr->user_name, UINT64_MAX);
1993 if (r < 0)
1994 return bus_log_create_error(r);
1995
1996 r = bus_message_append_secret(m, hr);
1997 if (r < 0)
1998 return bus_log_create_error(r);
1999
2000 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2001 if (r < 0) {
2002 if (arg_and_change_password &&
2003 sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
2004 return log_error_errno(r, "Security token not inserted, refusing.");
2005
2006 r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
2007 if (r < 0)
2008 return r;
2009 } else
2010 break;
2011 }
2012
2013 if (arg_and_change_password)
2014 log_info("Synchronizing passwords and encryption keys.");
2015
2016 (void) home_record_reset_human_interaction_permission(hr);
2017
2018 /* Also sync down passwords to underlying LUKS/fscrypt */
2019 while (arg_and_change_password) {
2020 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2021 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2022
2023 r = bus_message_new_method_call(bus, &m, bus_mgr, "ChangePasswordHome");
2024 if (r < 0)
2025 return bus_log_create_error(r);
2026
2027 /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
2028 r = sd_bus_message_append(m, "ss", hr->user_name, "{}");
2029 if (r < 0)
2030 return bus_log_create_error(r);
2031
2032 r = bus_message_append_secret(m, hr);
2033 if (r < 0)
2034 return bus_log_create_error(r);
2035
2036 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2037 if (r < 0) {
2038 if (sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
2039 return log_error_errno(r, "Security token not inserted, refusing.");
2040
2041 r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
2042 if (r < 0)
2043 return r;
2044 } else
2045 break;
2046 }
2047
2048 return 0;
2049 }
2050
2051 static int passwd_home(int argc, char *argv[], void *userdata) {
2052 _cleanup_(user_record_unrefp) UserRecord *old_secret = NULL, *new_secret = NULL;
2053 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2054 _cleanup_free_ char *buffer = NULL;
2055 const char *username;
2056 int r;
2057
2058 if (arg_pkcs11_token_uri)
2059 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2060 "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=%s'.",
2061 glyph(GLYPH_ELLIPSIS));
2062 if (arg_fido2_device)
2063 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2064 "To change the FIDO2 security token use 'homectl update --fido2-device=%s'.",
2065 glyph(GLYPH_ELLIPSIS));
2066 if (identity_properties_specified())
2067 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The 'passwd' verb does not permit changing other record properties at the same time.");
2068
2069 if (argc >= 2)
2070 username = argv[1];
2071 else {
2072 buffer = getusername_malloc();
2073 if (!buffer)
2074 return log_oom();
2075
2076 username = buffer;
2077 }
2078
2079 r = acquire_bus(&bus);
2080 if (r < 0)
2081 return r;
2082
2083 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2084
2085 r = acquire_passed_secrets(username, &old_secret);
2086 if (r < 0)
2087 return r;
2088
2089 new_secret = user_record_new();
2090 if (!new_secret)
2091 return log_oom();
2092
2093 r = acquire_new_password(username, new_secret, /* suggest = */ true, NULL);
2094 if (r < 0)
2095 return r;
2096
2097 for (;;) {
2098 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2099 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2100
2101 r = bus_message_new_method_call(bus, &m, bus_mgr, "ChangePasswordHome");
2102 if (r < 0)
2103 return bus_log_create_error(r);
2104
2105 r = sd_bus_message_append(m, "s", username);
2106 if (r < 0)
2107 return bus_log_create_error(r);
2108
2109 r = bus_message_append_secret(m, new_secret);
2110 if (r < 0)
2111 return bus_log_create_error(r);
2112
2113 r = bus_message_append_secret(m, old_secret);
2114 if (r < 0)
2115 return bus_log_create_error(r);
2116
2117 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2118 if (r < 0) {
2119 if (sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY)) {
2120
2121 log_error_errno(r, "%s", bus_error_message(&error, r));
2122
2123 r = acquire_new_password(username, new_secret, /* suggest = */ false, NULL);
2124
2125 } else if (sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
2126
2127 /* In the generic handler we'd ask for a password in this case, but when
2128 * changing passwords that's not sufficeint, as we need to acquire all keys
2129 * first. */
2130 return log_error_errno(r, "Security token not inserted, refusing.");
2131 else
2132 r = handle_generic_user_record_error(username, old_secret, &error, r, true);
2133 if (r < 0)
2134 return r;
2135 } else
2136 break;
2137 }
2138
2139 return 0;
2140 }
2141
2142 static int parse_disk_size(const char *t, uint64_t *ret) {
2143 int r;
2144
2145 assert(t);
2146 assert(ret);
2147
2148 if (streq(t, "min"))
2149 *ret = 0;
2150 else if (streq(t, "max"))
2151 *ret = UINT64_MAX-1; /* Largest size that isn't UINT64_MAX special marker */
2152 else {
2153 uint64_t ds;
2154
2155 r = parse_size(t, 1024, &ds);
2156 if (r < 0)
2157 return log_error_errno(r, "Failed to parse disk size parameter: %s", t);
2158
2159 if (ds >= UINT64_MAX) /* UINT64_MAX has special meaning for us ("dont change"), refuse */
2160 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Disk size out of range: %s", t);
2161
2162 *ret = ds;
2163 }
2164
2165 return 0;
2166 }
2167
2168 static int resize_home(int argc, char *argv[], void *userdata) {
2169 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2170 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
2171 uint64_t ds = UINT64_MAX;
2172 int r;
2173
2174 r = acquire_bus(&bus);
2175 if (r < 0)
2176 return r;
2177
2178 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2179
2180 if (arg_disk_size_relative != UINT64_MAX ||
2181 (argc > 2 && parse_permyriad(argv[2]) >= 0))
2182 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
2183 "Relative disk size specification currently not supported when resizing.");
2184
2185 if (argc > 2) {
2186 r = parse_disk_size(argv[2], &ds);
2187 if (r < 0)
2188 return r;
2189 }
2190
2191 if (arg_disk_size != UINT64_MAX) {
2192 if (ds != UINT64_MAX && ds != arg_disk_size)
2193 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Disk size specified twice and doesn't match, refusing.");
2194
2195 ds = arg_disk_size;
2196 }
2197
2198 r = acquire_passed_secrets(argv[1], &secret);
2199 if (r < 0)
2200 return r;
2201
2202 for (;;) {
2203 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2204 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2205
2206 r = bus_message_new_method_call(bus, &m, bus_mgr, "ResizeHome");
2207 if (r < 0)
2208 return bus_log_create_error(r);
2209
2210 r = sd_bus_message_append(m, "st", argv[1], ds);
2211 if (r < 0)
2212 return bus_log_create_error(r);
2213
2214 r = bus_message_append_secret(m, secret);
2215 if (r < 0)
2216 return bus_log_create_error(r);
2217
2218 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2219 if (r < 0) {
2220 r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
2221 if (r < 0)
2222 return r;
2223 } else
2224 break;
2225 }
2226
2227 return 0;
2228 }
2229
2230 static int lock_home(int argc, char *argv[], void *userdata) {
2231 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2232 int r, ret = 0;
2233
2234 r = acquire_bus(&bus);
2235 if (r < 0)
2236 return r;
2237
2238 STRV_FOREACH(i, strv_skip(argv, 1)) {
2239 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2240 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2241
2242 r = bus_message_new_method_call(bus, &m, bus_mgr, "LockHome");
2243 if (r < 0)
2244 return bus_log_create_error(r);
2245
2246 r = sd_bus_message_append(m, "s", *i);
2247 if (r < 0)
2248 return bus_log_create_error(r);
2249
2250 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2251 if (r < 0) {
2252 log_error_errno(r, "Failed to lock home: %s", bus_error_message(&error, r));
2253 if (ret == 0)
2254 ret = r;
2255 }
2256 }
2257
2258 return ret;
2259 }
2260
2261 static int unlock_home(int argc, char *argv[], void *userdata) {
2262 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2263 int r, ret = 0;
2264
2265 r = acquire_bus(&bus);
2266 if (r < 0)
2267 return r;
2268
2269 STRV_FOREACH(i, strv_skip(argv, 1)) {
2270 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
2271
2272 r = acquire_passed_secrets(*i, &secret);
2273 if (r < 0)
2274 return r;
2275
2276 for (;;) {
2277 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2278 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2279
2280 r = bus_message_new_method_call(bus, &m, bus_mgr, "UnlockHome");
2281 if (r < 0)
2282 return bus_log_create_error(r);
2283
2284 r = sd_bus_message_append(m, "s", *i);
2285 if (r < 0)
2286 return bus_log_create_error(r);
2287
2288 r = bus_message_append_secret(m, secret);
2289 if (r < 0)
2290 return bus_log_create_error(r);
2291
2292 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2293 if (r < 0) {
2294 r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
2295 if (r < 0) {
2296 if (ret == 0)
2297 ret = r;
2298
2299 break;
2300 }
2301 } else
2302 break;
2303 }
2304 }
2305
2306 return ret;
2307 }
2308
2309 static int with_home(int argc, char *argv[], void *userdata) {
2310 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2311 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
2312 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2313 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
2314 _cleanup_close_ int acquired_fd = -EBADF;
2315 _cleanup_strv_free_ char **cmdline = NULL;
2316 const char *home;
2317 int r, ret;
2318 pid_t pid;
2319
2320 r = acquire_bus(&bus);
2321 if (r < 0)
2322 return r;
2323
2324 if (argc < 3) {
2325 _cleanup_free_ char *shell = NULL;
2326
2327 /* If no command is specified, spawn a shell */
2328 r = get_shell(&shell);
2329 if (r < 0)
2330 return log_error_errno(r, "Failed to acquire shell: %m");
2331
2332 cmdline = strv_new(shell);
2333 } else
2334 cmdline = strv_copy(argv + 2);
2335 if (!cmdline)
2336 return log_oom();
2337
2338 r = acquire_passed_secrets(argv[1], &secret);
2339 if (r < 0)
2340 return r;
2341
2342 for (;;) {
2343 r = bus_message_new_method_call(bus, &m, bus_mgr, "AcquireHome");
2344 if (r < 0)
2345 return bus_log_create_error(r);
2346
2347 r = sd_bus_message_append(m, "s", argv[1]);
2348 if (r < 0)
2349 return bus_log_create_error(r);
2350
2351 r = bus_message_append_secret(m, secret);
2352 if (r < 0)
2353 return bus_log_create_error(r);
2354
2355 r = sd_bus_message_append(m, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
2356 if (r < 0)
2357 return bus_log_create_error(r);
2358
2359 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
2360 m = sd_bus_message_unref(m);
2361 if (r < 0) {
2362 r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
2363 if (r < 0)
2364 return r;
2365
2366 sd_bus_error_free(&error);
2367 } else {
2368 int fd;
2369
2370 r = sd_bus_message_read(reply, "h", &fd);
2371 if (r < 0)
2372 return bus_log_parse_error(r);
2373
2374 acquired_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
2375 if (acquired_fd < 0)
2376 return log_error_errno(errno, "Failed to duplicate acquired fd: %m");
2377
2378 reply = sd_bus_message_unref(reply);
2379 break;
2380 }
2381 }
2382
2383 r = bus_call_method(bus, bus_mgr, "GetHomeByName", &error, &reply, "s", argv[1]);
2384 if (r < 0)
2385 return log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
2386
2387 r = sd_bus_message_read(reply, "usussso", NULL, NULL, NULL, NULL, &home, NULL, NULL);
2388 if (r < 0)
2389 return bus_log_parse_error(r);
2390
2391 r = safe_fork("(with)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REOPEN_LOG, &pid);
2392 if (r < 0)
2393 return r;
2394 if (r == 0) {
2395 if (chdir(home) < 0) {
2396 log_error_errno(errno, "Failed to change to directory %s: %m", home);
2397 _exit(255);
2398 }
2399
2400 execvp(cmdline[0], cmdline);
2401 log_error_errno(errno, "Failed to execute %s: %m", cmdline[0]);
2402 _exit(255);
2403 }
2404
2405 ret = wait_for_terminate_and_check(cmdline[0], pid, WAIT_LOG_ABNORMAL);
2406
2407 /* Close the fd that pings the home now. */
2408 acquired_fd = safe_close(acquired_fd);
2409
2410 r = bus_message_new_method_call(bus, &m, bus_mgr, "ReleaseHome");
2411 if (r < 0)
2412 return bus_log_create_error(r);
2413
2414 r = sd_bus_message_append(m, "s", argv[1]);
2415 if (r < 0)
2416 return bus_log_create_error(r);
2417
2418 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2419 if (r < 0) {
2420 if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_BUSY))
2421 log_notice("Not deactivating home directory of %s, as it is still used.", argv[1]);
2422 else
2423 return log_error_errno(r, "Failed to release user home: %s", bus_error_message(&error, r));
2424 }
2425
2426 return ret;
2427 }
2428
2429 static int lock_all_homes(int argc, char *argv[], void *userdata) {
2430 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2431 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2432 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2433 int r;
2434
2435 r = acquire_bus(&bus);
2436 if (r < 0)
2437 return r;
2438
2439 r = bus_message_new_method_call(bus, &m, bus_mgr, "LockAllHomes");
2440 if (r < 0)
2441 return bus_log_create_error(r);
2442
2443 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2444 if (r < 0)
2445 return log_error_errno(r, "Failed to lock all homes: %s", bus_error_message(&error, r));
2446
2447 return 0;
2448 }
2449
2450 static int deactivate_all_homes(int argc, char *argv[], void *userdata) {
2451 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2452 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2453 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2454 int r;
2455
2456 r = acquire_bus(&bus);
2457 if (r < 0)
2458 return r;
2459
2460 r = bus_message_new_method_call(bus, &m, bus_mgr, "DeactivateAllHomes");
2461 if (r < 0)
2462 return bus_log_create_error(r);
2463
2464 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2465 if (r < 0)
2466 return log_error_errno(r, "Failed to deactivate all homes: %s", bus_error_message(&error, r));
2467
2468 return 0;
2469 }
2470
2471 static int rebalance(int argc, char *argv[], void *userdata) {
2472 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2473 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2474 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2475 int r;
2476
2477 r = acquire_bus(&bus);
2478 if (r < 0)
2479 return r;
2480
2481 r = bus_message_new_method_call(bus, &m, bus_mgr, "Rebalance");
2482 if (r < 0)
2483 return bus_log_create_error(r);
2484
2485 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2486 if (r < 0) {
2487 if (sd_bus_error_has_name(&error, BUS_ERROR_REBALANCE_NOT_NEEDED))
2488 log_info("No homes needed rebalancing.");
2489 else
2490 return log_error_errno(r, "Failed to rebalance: %s", bus_error_message(&error, r));
2491 } else
2492 log_info("Completed rebalancing.");
2493
2494 return 0;
2495 }
2496
2497 static int create_or_register_from_credentials(void) {
2498 int r;
2499
2500 _cleanup_close_ int fd = open_credentials_dir();
2501 if (IN_SET(fd, -ENXIO, -ENOENT)) /* Credential env var not set, or dir doesn't exist. */
2502 return 0;
2503 if (fd < 0)
2504 return log_error_errno(fd, "Failed to open credentials directory: %m");
2505
2506 _cleanup_free_ DirectoryEntries *des = NULL;
2507 r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
2508 if (r < 0)
2509 return log_error_errno(r, "Failed to enumerate credentials: %m");
2510
2511 int ret = 0, n_processed = 0;
2512 FOREACH_ARRAY(i, des->entries, des->n_entries) {
2513 struct dirent *de = *i;
2514 if (de->d_type != DT_REG)
2515 continue;
2516
2517 enum {
2518 OPERATION_CREATE,
2519 OPERATION_REGISTER,
2520 } op;
2521 const char *e;
2522 if ((e = startswith(de->d_name, "home.create.")))
2523 op = OPERATION_CREATE;
2524 else if ((e = startswith(de->d_name, "home.register.")))
2525 op = OPERATION_REGISTER;
2526 else
2527 continue;
2528
2529 if (!valid_user_group_name(e, 0)) {
2530 log_notice("Skipping over credential with name that is not a suitable user name: %s", de->d_name);
2531 continue;
2532 }
2533
2534 _cleanup_(sd_json_variant_unrefp) sd_json_variant *identity = NULL;
2535 unsigned line = 0, column = 0;
2536 r = sd_json_parse_file_at(
2537 /* f= */ NULL,
2538 fd,
2539 de->d_name,
2540 /* flags= */ 0,
2541 &identity,
2542 &line,
2543 &column);
2544 if (r < 0) {
2545 log_warning_errno(r, "[%s:%u:%u] Failed to parse user record in credential, ignoring: %m", de->d_name, line, column);
2546 continue;
2547 }
2548
2549 sd_json_variant *un = sd_json_variant_by_key(identity, "userName");
2550 if (un) {
2551 if (!sd_json_variant_is_string(un)) {
2552 log_warning("User record from credential '%s' contains 'userName' field of invalid type, ignoring.", de->d_name);
2553 continue;
2554 }
2555
2556 if (!streq(sd_json_variant_string(un), e)) {
2557 log_warning("User record from credential '%s' contains 'userName' field (%s) that doesn't match credential name (%s), ignoring.", de->d_name, sd_json_variant_string(un), e);
2558 continue;
2559 }
2560 } else {
2561 r = sd_json_variant_set_field_string(&identity, "userName", e);
2562 if (r < 0)
2563 return log_warning_errno(r, "Failed to set userName field: %m");
2564 }
2565
2566 log_notice("Processing user '%s' from credentials.", e);
2567
2568 if (op == OPERATION_CREATE)
2569 r = create_home_common(identity, /* show_enforce_password_policy_hint= */ false);
2570 else
2571 r = register_home_common(/* bus= */ NULL, identity);
2572 if (r >= 0)
2573 n_processed++;
2574
2575 RET_GATHER(ret, r);
2576 }
2577
2578 return ret < 0 ? ret : n_processed;
2579 }
2580
2581 static int has_regular_user(void) {
2582 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
2583 UserDBMatch match = USERDB_MATCH_NULL;
2584 int r;
2585
2586 match.disposition_mask = INDEX_TO_MASK(uint64_t, USER_REGULAR);
2587
2588 r = userdb_all(&match, USERDB_SUPPRESS_SHADOW, &iterator);
2589 if (r < 0)
2590 return log_error_errno(r, "Failed to create user enumerator: %m");
2591
2592 r = userdb_iterator_get(iterator, &match, /* ret= */ NULL);
2593 if (r == -ESRCH)
2594 return false;
2595 if (r < 0)
2596 return log_error_errno(r, "Failed to enumerate users: %m");
2597
2598 return true;
2599 }
2600
2601 static int acquire_group_list(char ***ret) {
2602 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
2603 _cleanup_strv_free_ char **groups = NULL;
2604 UserDBMatch match = USERDB_MATCH_NULL;
2605 int r;
2606
2607 assert(ret);
2608
2609 match.disposition_mask = INDEXES_TO_MASK(uint64_t, USER_REGULAR, USER_SYSTEM);
2610
2611 r = groupdb_all(&match, USERDB_SUPPRESS_SHADOW, &iterator);
2612 if (r == -ENOLINK)
2613 log_debug_errno(r, "No groups found. (Didn't check via Varlink.)");
2614 else if (r == -ESRCH)
2615 log_debug_errno(r, "No groups found.");
2616 else if (r < 0)
2617 return log_debug_errno(r, "Failed to enumerate groups, ignoring: %m");
2618 else
2619 for (;;) {
2620 _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
2621
2622 r = groupdb_iterator_get(iterator, &match, &gr);
2623 if (r == -ESRCH)
2624 break;
2625 if (r < 0)
2626 return log_debug_errno(r, "Failed acquire next group: %m");
2627
2628 if (group_record_disposition(gr) == USER_REGULAR) {
2629 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
2630
2631 /* Filter groups here that belong to a specific user, and are named like them */
2632
2633 UserDBMatch user_match = USERDB_MATCH_NULL;
2634 user_match.disposition_mask = INDEX_TO_MASK(uint64_t, USER_REGULAR);
2635
2636 r = userdb_by_name(gr->group_name, &user_match, USERDB_SUPPRESS_SHADOW, &ur);
2637 if (r < 0 && r != -ESRCH)
2638 return log_debug_errno(r, "Failed to check if matching user exists for group '%s': %m", gr->group_name);
2639
2640 if (r >= 0 && user_record_gid(ur) == gr->gid)
2641 continue;
2642 }
2643
2644 r = strv_extend(&groups, gr->group_name);
2645 if (r < 0)
2646 return log_oom();
2647 }
2648
2649 strv_sort(groups);
2650
2651 *ret = TAKE_PTR(groups);
2652 return !!*ret;
2653 }
2654
2655 static int group_completion_callback(const char *key, char ***ret_list, void *userdata) {
2656 char ***available = userdata;
2657 int r;
2658
2659 if (!*available) {
2660 r = acquire_group_list(available);
2661 if (r < 0)
2662 log_debug_errno(r, "Failed to enumerate available groups, ignoring: %m");
2663 }
2664
2665 _cleanup_strv_free_ char **l = strv_copy(*available);
2666 if (!l)
2667 return -ENOMEM;
2668
2669 r = strv_extend(&l, "list");
2670 if (r < 0)
2671 return r;
2672
2673 *ret_list = TAKE_PTR(l);
2674 return 0;
2675 }
2676
2677 static int create_interactively(void) {
2678 _cleanup_free_ char *username = NULL;
2679 int r;
2680
2681 if (!arg_prompt_new_user) {
2682 log_debug("Prompting for user creation was not requested.");
2683 return 0;
2684 }
2685
2686 putchar('\n');
2687 if (emoji_enabled()) {
2688 fputs(glyph(GLYPH_HOME), stdout);
2689 putchar(' ');
2690 }
2691 printf("Please create your user account!\n");
2692
2693 if (!any_key_to_proceed()) {
2694 log_notice("Skipping.");
2695 return 0;
2696 }
2697
2698 (void) terminal_reset_defensive_locked(STDOUT_FILENO, /* flags= */ 0);
2699
2700 for (;;) {
2701 username = mfree(username);
2702
2703 r = ask_string(&username,
2704 "%s Please enter user name to create (empty to skip): ",
2705 glyph(GLYPH_TRIANGULAR_BULLET));
2706 if (r < 0)
2707 return log_error_errno(r, "Failed to query user for username: %m");
2708
2709 if (isempty(username)) {
2710 log_info("No data entered, skipping.");
2711 return 0;
2712 }
2713
2714 if (!valid_user_group_name(username, /* flags= */ 0)) {
2715 log_notice("Specified user name is not a valid UNIX user name, try again: %s", username);
2716 continue;
2717 }
2718
2719 r = userdb_by_name(username, /* match= */ NULL, USERDB_SUPPRESS_SHADOW, /* ret= */ NULL);
2720 if (r == -ESRCH)
2721 break;
2722 if (r < 0)
2723 return log_error_errno(r, "Failed to check if specified user '%s' already exists: %m", username);
2724
2725 log_notice("Specified user '%s' exists already, try again.", username);
2726 }
2727
2728 r = sd_json_variant_set_field_string(&arg_identity_extra, "userName", username);
2729 if (r < 0)
2730 return log_error_errno(r, "Failed to set userName field: %m");
2731
2732 /* Let's not insist on a strong password in the firstboot interactive interface. Insisting on this is
2733 * really annoying, as the user cannot just invoke the tool again with "--enforce-password-policy=no"
2734 * because after all the tool is called from the boot process, and not from an interactive
2735 * shell. Moreover, when setting up an initial system we can assume the user owns it, and hence we
2736 * don't need to hard enforce some policy on password strength some organization or OS vendor
2737 * requires. Note that this just disables the *strict* enforcement of the password policy. Even with
2738 * this disabled we'll still tell the user in the UI that the password is too weak and suggest better
2739 * ones, even if we then accept the weak ones if the user insists, by repeating it. */
2740 r = sd_json_variant_set_field_boolean(&arg_identity_extra, "enforcePasswordPolicy", false);
2741 if (r < 0)
2742 return log_error_errno(r, "Failed to set enforcePasswordPolicy field: %m");
2743
2744 _cleanup_strv_free_ char **available = NULL, **groups = NULL;
2745 for (;;) {
2746 _cleanup_free_ char *s = NULL;
2747
2748 strv_sort_uniq(groups);
2749
2750 if (!strv_isempty(groups)) {
2751 _cleanup_free_ char *j = strv_join(groups, ", ");
2752 if (!j)
2753 return log_oom();
2754
2755 log_info("Currently selected groups: %s", j);
2756 }
2757
2758 r = ask_string_full(&s,
2759 group_completion_callback, &available,
2760 "%s Please enter an auxiliary group for user %s (empty to continue, \"list\" to list available groups): ",
2761 glyph(GLYPH_TRIANGULAR_BULLET), username);
2762 if (r < 0)
2763 return log_error_errno(r, "Failed to query user for auxiliary group: %m");
2764
2765 if (isempty(s))
2766 break;
2767
2768 if (streq(s, "list")) {
2769 if (!available) {
2770 r = acquire_group_list(&available);
2771 if (r < 0)
2772 log_warning_errno(r, "Failed to enumerate available groups, ignoring: %m");
2773 if (r == 0)
2774 log_notice("Did not find any available groups");
2775 if (r <= 0)
2776 continue;
2777 }
2778
2779 r = show_menu(available,
2780 /* n_columns= */ 3,
2781 /* column_width= */ 20,
2782 /* ellipsize_percentage= */ 60,
2783 /* grey_prefix= */ NULL,
2784 /* with_numbers= */ true);
2785 if (r < 0)
2786 return log_error_errno(r, "Failed to show menu: %m");
2787
2788 putchar('\n');
2789 continue;
2790 };
2791
2792 if (!strv_isempty(available)) {
2793 unsigned u;
2794 r = safe_atou(s, &u);
2795 if (r >= 0) {
2796 if (u <= 0 || u > strv_length(available)) {
2797 log_error("Specified entry number out of range.");
2798 continue;
2799 }
2800
2801 log_info("Selected '%s'.", available[u-1]);
2802
2803 r = strv_extend(&groups, available[u-1]);
2804 if (r < 0)
2805 return log_oom();
2806
2807 continue;
2808 }
2809 }
2810
2811 if (!valid_user_group_name(s, /* flags= */ 0)) {
2812 log_notice("Specified group name is not a valid UNIX group name, try again: %s", s);
2813 continue;
2814 }
2815
2816 r = groupdb_by_name(s, /* match= */ NULL, USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, /*ret=*/ NULL);
2817 if (r == -ESRCH) {
2818 log_notice("Specified auxiliary group does not exist, try again: %s", s);
2819 continue;
2820 }
2821 if (r < 0)
2822 return log_error_errno(r, "Failed to check if specified group '%s' already exists: %m", s);
2823
2824 log_info("Selected '%s'.", s);
2825
2826 r = strv_extend(&groups, s);
2827 if (r < 0)
2828 return log_oom();
2829 }
2830
2831 if (!strv_isempty(groups)) {
2832 strv_sort_uniq(groups);
2833
2834 r = sd_json_variant_set_field_strv(&arg_identity_extra, "memberOf", groups);
2835 if (r < 0)
2836 return log_error_errno(r, "Failed to set memberOf field: %m");
2837 }
2838
2839 _cleanup_free_ char *shell = NULL;
2840
2841 for (;;) {
2842 shell = mfree(shell);
2843
2844 r = ask_string(&shell,
2845 "%s Please enter the shell to use for user %s (empty for default): ",
2846 glyph(GLYPH_TRIANGULAR_BULLET), username);
2847 if (r < 0)
2848 return log_error_errno(r, "Failed to query user for username: %m");
2849
2850 if (isempty(shell)) {
2851 log_info("No data entered, leaving at default.");
2852 break;
2853 }
2854
2855 if (!valid_shell(shell)) {
2856 log_notice("Specified shell is not a valid UNIX shell path, try again: %s", shell);
2857 continue;
2858 }
2859
2860 r = RET_NERRNO(access(shell, X_OK));
2861 if (r >= 0)
2862 break;
2863
2864 if (r != -ENOENT)
2865 return log_error_errno(r, "Failed to check if shell %s exists: %m", shell);
2866
2867 log_notice("Specified shell '%s' is not installed, try another one.", shell);
2868 }
2869
2870 if (!isempty(shell)) {
2871 log_info("Selected %s as the shell for user %s", shell, username);
2872
2873 r = sd_json_variant_set_field_string(&arg_identity_extra, "shell", shell);
2874 if (r < 0)
2875 return log_error_errno(r, "Failed to set shell field: %m");
2876 }
2877
2878 return create_home_common(/* input= */ NULL, /* show_enforce_password_policy_hint= */ false);
2879 }
2880
2881 static int add_signing_keys_from_credentials(void);
2882
2883 static int verb_firstboot(int argc, char *argv[], void *userdata) {
2884 int r;
2885
2886 /* Let's honour the systemd.firstboot kernel command line option, just like the systemd-firstboot
2887 * tool. */
2888
2889 bool enabled;
2890 r = proc_cmdline_get_bool("systemd.firstboot", /* flags = */ 0, &enabled);
2891 if (r < 0)
2892 return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
2893 if (r > 0 && !enabled) {
2894 log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
2895 arg_prompt_new_user = false;
2896 }
2897
2898 int ret = 0;
2899
2900 RET_GATHER(ret, add_signing_keys_from_credentials());
2901
2902 r = create_or_register_from_credentials();
2903 RET_GATHER(ret, r);
2904 bool existing_users = r > 0;
2905
2906 r = getenv_bool("SYSTEMD_HOME_FIRSTBOOT_OVERRIDE");
2907 if (r == 0)
2908 return 0;
2909 if (r < 0) {
2910 if (r != -ENXIO)
2911 log_warning_errno(r, "Failed to parse $SYSTEMD_HOME_FIRSTBOOT_OVERRIDE, ignoring: %m");
2912
2913 if (!existing_users) {
2914 r = has_regular_user();
2915 if (r < 0)
2916 return r;
2917
2918 existing_users = r > 0;
2919 }
2920 if (existing_users) {
2921 log_info("Regular user already present in user database, skipping interactive user creation.");
2922 return 0;
2923 }
2924 }
2925
2926 RET_GATHER(ret, create_interactively());
2927 return ret;
2928 }
2929
2930 static int drop_from_identity(const char *field) {
2931 int r;
2932
2933 assert(field);
2934
2935 /* If we are called to update an identity record and drop some field, let's keep track of what to
2936 * remove from the old record */
2937 r = strv_extend(&arg_identity_filter, field);
2938 if (r < 0)
2939 return log_oom();
2940
2941 /* Let's also drop the field if it was previously set to a new value on the same command line */
2942 r = sd_json_variant_filter(&arg_identity_extra, STRV_MAKE(field));
2943 if (r < 0)
2944 return log_error_errno(r, "Failed to filter JSON identity data: %m");
2945
2946 r = sd_json_variant_filter(&arg_identity_extra_this_machine, STRV_MAKE(field));
2947 if (r < 0)
2948 return log_error_errno(r, "Failed to filter JSON identity data: %m");
2949
2950 r = sd_json_variant_filter(&arg_identity_extra_privileged, STRV_MAKE(field));
2951 if (r < 0)
2952 return log_error_errno(r, "Failed to filter JSON identity data: %m");
2953
2954 return 0;
2955 }
2956
2957 static int help(int argc, char *argv[], void *userdata) {
2958 _cleanup_free_ char *link = NULL;
2959 int r;
2960
2961 pager_open(arg_pager_flags);
2962
2963 r = terminal_urlify_man("homectl", "1", &link);
2964 if (r < 0)
2965 return log_oom();
2966
2967 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
2968 "%2$sCreate, manipulate or inspect home directories.%3$s\n"
2969 "\n%4$sBasic User Manipulation Commands:%5$s\n"
2970 " list List home areas\n"
2971 " inspect USER… Inspect a home area\n"
2972 " create USER Create a home area\n"
2973 " update USER Update a home area\n"
2974 " passwd USER Change password of a home area\n"
2975 " resize USER SIZE Resize a home area\n"
2976 " remove USER… Remove a home area\n"
2977 "\n%4$sAdvanced User Manipulation Commands:%5$s\n"
2978 " activate USER… Activate a home area\n"
2979 " deactivate USER… Deactivate a home area\n"
2980 " deactivate-all Deactivate all active home areas\n"
2981 " with USER [COMMAND…] Run shell or command with access to a home area\n"
2982 " authenticate USER… Authenticate a home area\n"
2983 "\n%4$sUser Migration Commands:%5$s\n"
2984 " adopt PATH… Add an existing home area on this system\n"
2985 " register PATH… Register a user record locally\n"
2986 " unregister USER… Unregister a user record locally\n"
2987 "\n%4$sSigning Keys Commands:%5$s\n"
2988 " list-signing-keys List home signing keys\n"
2989 " get-signing-key [NAME…] Get a named home signing key\n"
2990 " add-signing-key FILE… Add home signing key\n"
2991 " remove-signing-key NAME… Remove home signing key\n"
2992 "\n%4$sLock/Unlock Commands:%5$s\n"
2993 " lock USER… Temporarily lock an active home area\n"
2994 " unlock USER… Unlock a temporarily locked home area\n"
2995 " lock-all Lock all suitable home areas\n"
2996 "\n%4$sOther Commands:%5$s\n"
2997 " rebalance Rebalance free space between home areas\n"
2998 " firstboot Run first-boot home area creation wizard\n"
2999 "\n%4$sOptions:%5$s\n"
3000 " -h --help Show this help\n"
3001 " --version Show package version\n"
3002 " --no-pager Do not pipe output into a pager\n"
3003 " --no-legend Do not show the headers and footers\n"
3004 " --no-ask-password Do not ask for system passwords\n"
3005 " --offline Don't update record embedded in home directory\n"
3006 " -H --host=[USER@]HOST Operate on remote host\n"
3007 " -M --machine=CONTAINER Operate on local container\n"
3008 " --identity=PATH Read JSON identity from file\n"
3009 " --json=FORMAT Output inspection data in JSON (takes one of\n"
3010 " pretty, short, off)\n"
3011 " -j Equivalent to --json=pretty (on TTY) or\n"
3012 " --json=short (otherwise)\n"
3013 " --export-format= Strip JSON inspection data (full, stripped,\n"
3014 " minimal)\n"
3015 " -E When specified once equals -j --export-format=\n"
3016 " stripped, when specified twice equals\n"
3017 " -j --export-format=minimal\n"
3018 " --prompt-new-user firstboot: Query user interactively for user\n"
3019 " to create\n"
3020 " --key-name=NAME Key name when adding a signing key\n"
3021 " --seize=no Do not strip existing signatures of user record\n"
3022 " when creating\n"
3023 "\n%4$sGeneral User Record Properties:%5$s\n"
3024 " -c --real-name=REALNAME Real name for user\n"
3025 " --realm=REALM Realm to create user in\n"
3026 " --alias=ALIAS Define alias usernames for this account\n"
3027 " --email-address=EMAIL Email address for user\n"
3028 " --location=LOCATION Set location of user on earth\n"
3029 " --icon-name=NAME Icon name for user\n"
3030 " -d --home-dir=PATH Home directory\n"
3031 " -u --uid=UID Numeric UID for user\n"
3032 " -G --member-of=GROUP Add user to group\n"
3033 " --capability-bounding-set=CAPS\n"
3034 " Bounding POSIX capability set\n"
3035 " --capability-ambient-set=CAPS\n"
3036 " Ambient POSIX capability set\n"
3037 " --access-mode=MODE User home directory access mode\n"
3038 " --umask=MODE Umask for user when logging in\n"
3039 " --skel=PATH Skeleton directory to use\n"
3040 " --shell=PATH Shell for account\n"
3041 " --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n"
3042 " --timezone=TIMEZONE Set a time-zone\n"
3043 " --language=LOCALE Set preferred languages\n"
3044 " --default-area=AREA Select default area\n"
3045 "\n%4$sAuthentication User Record Properties:%5$s\n"
3046 " --ssh-authorized-keys=KEYS\n"
3047 " Specify SSH public keys\n"
3048 " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
3049 " private key and matching X.509 certificate\n"
3050 " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
3051 " extension\n"
3052 " --fido2-with-client-pin=BOOL\n"
3053 " Whether to require entering a PIN to unlock the\n"
3054 " account\n"
3055 " --fido2-with-user-presence=BOOL\n"
3056 " Whether to require user presence to unlock the\n"
3057 " account\n"
3058 " --fido2-with-user-verification=BOOL\n"
3059 " Whether to require user verification to unlock\n"
3060 " the account\n"
3061 " --recovery-key=BOOL Add a recovery key\n"
3062 "\n%4$sBlob Directory User Record Properties:%5$s\n"
3063 " -b --blob=[FILENAME=]PATH\n"
3064 " Path to a replacement blob directory, or replace\n"
3065 " an individual files in the blob directory.\n"
3066 " --avatar=PATH Path to user avatar picture\n"
3067 " --login-background=PATH Path to user login background picture\n"
3068 "\n%4$sAccount Management User Record Properties:%5$s\n"
3069 " --locked=BOOL Set locked account state\n"
3070 " --not-before=TIMESTAMP Do not allow logins before\n"
3071 " --not-after=TIMESTAMP Do not allow logins after\n"
3072 " --rate-limit-interval=SECS\n"
3073 " Login rate-limit interval in seconds\n"
3074 " --rate-limit-burst=NUMBER\n"
3075 " Login rate-limit attempts per interval\n"
3076 "\n%4$sPassword Policy User Record Properties:%5$s\n"
3077 " --password-hint=HINT Set Password hint\n"
3078 " --enforce-password-policy=BOOL\n"
3079 " Control whether to enforce system's password\n"
3080 " policy for this user\n"
3081 " -P Same as --enforce-password-policy=no\n"
3082 " --password-change-now=BOOL\n"
3083 " Require the password to be changed on next login\n"
3084 " --password-change-min=TIME\n"
3085 " Require minimum time between password changes\n"
3086 " --password-change-max=TIME\n"
3087 " Require maximum time between password changes\n"
3088 " --password-change-warn=TIME\n"
3089 " How much time to warn before password expiry\n"
3090 " --password-change-inactive=TIME\n"
3091 " How much time to block password after expiry\n"
3092 "\n%4$sResource Management User Record Properties:%5$s\n"
3093 " --disk-size=BYTES Size to assign the user on disk\n"
3094 " --nice=NICE Nice level for user\n"
3095 " --rlimit=LIMIT=VALUE[:VALUE]\n"
3096 " Set resource limits\n"
3097 " --tasks-max=MAX Set maximum number of per-user tasks\n"
3098 " --memory-high=BYTES Set high memory threshold in bytes\n"
3099 " --memory-max=BYTES Set maximum memory limit\n"
3100 " --cpu-weight=WEIGHT Set CPU weight\n"
3101 " --io-weight=WEIGHT Set IO weight\n"
3102 " --tmp-limit=BYTES|PERCENT Set limit on /tmp/\n"
3103 " --dev-shm-limit=BYTES|PERCENT\n"
3104 " Set limit on /dev/shm/\n"
3105 "\n%4$sStorage User Record Properties:%5$s\n"
3106 " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
3107 " subvolume, cifs)\n"
3108 " --image-path=PATH Path to image file/directory\n"
3109 " --drop-caches=BOOL Whether to automatically drop caches on logout\n"
3110 "\n%4$sLUKS Storage User Record Properties:%5$s\n"
3111 " --fs-type=TYPE File system type to use in case of luks\n"
3112 " storage (btrfs, ext4, xfs)\n"
3113 " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
3114 " when activated (mounted)\n"
3115 " --luks-offline-discard=BOOL\n"
3116 " Whether to trim file on logout\n"
3117 " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
3118 " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
3119 " --luks-volume-key-size=BITS\n"
3120 " Volume key size to use for LUKS encryption\n"
3121 " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
3122 " --luks-pbkdf-hash-algorithm=ALGORITHM\n"
3123 " PBKDF hash algorithm to use\n"
3124 " --luks-pbkdf-time-cost=SECS\n"
3125 " Time cost for PBKDF in seconds\n"
3126 " --luks-pbkdf-memory-cost=BYTES\n"
3127 " Memory cost for PBKDF in bytes\n"
3128 " --luks-pbkdf-parallel-threads=NUMBER\n"
3129 " Number of parallel threads for PKBDF\n"
3130 " --luks-sector-size=BYTES\n"
3131 " Sector size for LUKS encryption in bytes\n"
3132 " --luks-extra-mount-options=OPTIONS\n"
3133 " LUKS extra mount options\n"
3134 " --auto-resize-mode=MODE Automatically grow/shrink home on login/logout\n"
3135 " --rebalance-weight=WEIGHT Weight while rebalancing\n"
3136 "\n%4$sMounting User Record Properties:%5$s\n"
3137 " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
3138 " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
3139 " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
3140 "\n%4$sCIFS User Record Properties:%5$s\n"
3141 " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
3142 " --cifs-user-name=USER CIFS (Windows) user name\n"
3143 " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
3144 " --cifs-extra-mount-options=OPTIONS\n"
3145 " CIFS (Windows) extra mount options\n"
3146 "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
3147 " --stop-delay=SECS How long to leave user services running after\n"
3148 " logout\n"
3149 " --kill-processes=BOOL Whether to kill user processes when sessions\n"
3150 " terminate\n"
3151 " --auto-login=BOOL Try to log this user in automatically\n"
3152 " --session-launcher=LAUNCHER\n"
3153 " Preferred session launcher file\n"
3154 " --session-type=TYPE Preferred session type\n"
3155 "\nSee the %6$s for details.\n",
3156 program_invocation_short_name,
3157 ansi_highlight(),
3158 ansi_normal(),
3159 ansi_underline(),
3160 ansi_normal(),
3161 link);
3162
3163 return 0;
3164 }
3165
3166 static int parse_argv(int argc, char *argv[]) {
3167 _cleanup_strv_free_ char **arg_languages = NULL;
3168
3169 enum {
3170 ARG_VERSION = 0x100,
3171 ARG_NO_PAGER,
3172 ARG_NO_LEGEND,
3173 ARG_NO_ASK_PASSWORD,
3174 ARG_OFFLINE,
3175 ARG_REALM,
3176 ARG_ALIAS,
3177 ARG_EMAIL_ADDRESS,
3178 ARG_DISK_SIZE,
3179 ARG_ACCESS_MODE,
3180 ARG_STORAGE,
3181 ARG_FS_TYPE,
3182 ARG_IMAGE_PATH,
3183 ARG_UMASK,
3184 ARG_LUKS_DISCARD,
3185 ARG_LUKS_OFFLINE_DISCARD,
3186 ARG_JSON,
3187 ARG_SETENV,
3188 ARG_TIMEZONE,
3189 ARG_LANGUAGE,
3190 ARG_LOCKED,
3191 ARG_SSH_AUTHORIZED_KEYS,
3192 ARG_LOCATION,
3193 ARG_ICON_NAME,
3194 ARG_PASSWORD_HINT,
3195 ARG_NICE,
3196 ARG_RLIMIT,
3197 ARG_NOT_BEFORE,
3198 ARG_NOT_AFTER,
3199 ARG_LUKS_CIPHER,
3200 ARG_LUKS_CIPHER_MODE,
3201 ARG_LUKS_VOLUME_KEY_SIZE,
3202 ARG_NOSUID,
3203 ARG_NODEV,
3204 ARG_NOEXEC,
3205 ARG_CIFS_DOMAIN,
3206 ARG_CIFS_USER_NAME,
3207 ARG_CIFS_SERVICE,
3208 ARG_CIFS_EXTRA_MOUNT_OPTIONS,
3209 ARG_TASKS_MAX,
3210 ARG_MEMORY_HIGH,
3211 ARG_MEMORY_MAX,
3212 ARG_CPU_WEIGHT,
3213 ARG_IO_WEIGHT,
3214 ARG_LUKS_PBKDF_TYPE,
3215 ARG_LUKS_PBKDF_HASH_ALGORITHM,
3216 ARG_LUKS_PBKDF_FORCE_ITERATIONS,
3217 ARG_LUKS_PBKDF_TIME_COST,
3218 ARG_LUKS_PBKDF_MEMORY_COST,
3219 ARG_LUKS_PBKDF_PARALLEL_THREADS,
3220 ARG_LUKS_SECTOR_SIZE,
3221 ARG_RATE_LIMIT_INTERVAL,
3222 ARG_RATE_LIMIT_BURST,
3223 ARG_STOP_DELAY,
3224 ARG_KILL_PROCESSES,
3225 ARG_ENFORCE_PASSWORD_POLICY,
3226 ARG_PASSWORD_CHANGE_NOW,
3227 ARG_PASSWORD_CHANGE_MIN,
3228 ARG_PASSWORD_CHANGE_MAX,
3229 ARG_PASSWORD_CHANGE_WARN,
3230 ARG_PASSWORD_CHANGE_INACTIVE,
3231 ARG_EXPORT_FORMAT,
3232 ARG_AUTO_LOGIN,
3233 ARG_SESSION_LAUNCHER,
3234 ARG_SESSION_TYPE,
3235 ARG_PKCS11_TOKEN_URI,
3236 ARG_FIDO2_DEVICE,
3237 ARG_FIDO2_WITH_PIN,
3238 ARG_FIDO2_WITH_UP,
3239 ARG_FIDO2_WITH_UV,
3240 ARG_RECOVERY_KEY,
3241 ARG_AND_RESIZE,
3242 ARG_AND_CHANGE_PASSWORD,
3243 ARG_DROP_CACHES,
3244 ARG_LUKS_EXTRA_MOUNT_OPTIONS,
3245 ARG_AUTO_RESIZE_MODE,
3246 ARG_REBALANCE_WEIGHT,
3247 ARG_FIDO2_CRED_ALG,
3248 ARG_CAPABILITY_BOUNDING_SET,
3249 ARG_CAPABILITY_AMBIENT_SET,
3250 ARG_PROMPT_NEW_USER,
3251 ARG_AVATAR,
3252 ARG_LOGIN_BACKGROUND,
3253 ARG_TMP_LIMIT,
3254 ARG_DEV_SHM_LIMIT,
3255 ARG_DEFAULT_AREA,
3256 ARG_KEY_NAME,
3257 ARG_SEIZE,
3258 ARG_MATCH,
3259 };
3260
3261 static const struct option options[] = {
3262 { "help", no_argument, NULL, 'h' },
3263 { "version", no_argument, NULL, ARG_VERSION },
3264 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
3265 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
3266 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
3267 { "offline", no_argument, NULL, ARG_OFFLINE },
3268 { "host", required_argument, NULL, 'H' },
3269 { "machine", required_argument, NULL, 'M' },
3270 { "identity", required_argument, NULL, 'I' },
3271 { "real-name", required_argument, NULL, 'c' },
3272 { "comment", required_argument, NULL, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
3273 { "realm", required_argument, NULL, ARG_REALM },
3274 { "alias", required_argument, NULL, ARG_ALIAS },
3275 { "email-address", required_argument, NULL, ARG_EMAIL_ADDRESS },
3276 { "location", required_argument, NULL, ARG_LOCATION },
3277 { "password-hint", required_argument, NULL, ARG_PASSWORD_HINT },
3278 { "icon-name", required_argument, NULL, ARG_ICON_NAME },
3279 { "home-dir", required_argument, NULL, 'd' }, /* Compatible with useradd(8) */
3280 { "uid", required_argument, NULL, 'u' }, /* Compatible with useradd(8) */
3281 { "member-of", required_argument, NULL, 'G' },
3282 { "groups", required_argument, NULL, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
3283 { "skel", required_argument, NULL, 'k' }, /* Compatible with useradd(8) */
3284 { "shell", required_argument, NULL, 's' }, /* Compatible with useradd(8) */
3285 { "setenv", required_argument, NULL, ARG_SETENV },
3286 { "timezone", required_argument, NULL, ARG_TIMEZONE },
3287 { "language", required_argument, NULL, ARG_LANGUAGE },
3288 { "locked", required_argument, NULL, ARG_LOCKED },
3289 { "not-before", required_argument, NULL, ARG_NOT_BEFORE },
3290 { "not-after", required_argument, NULL, ARG_NOT_AFTER },
3291 { "expiredate", required_argument, NULL, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
3292 { "ssh-authorized-keys", required_argument, NULL, ARG_SSH_AUTHORIZED_KEYS },
3293 { "disk-size", required_argument, NULL, ARG_DISK_SIZE },
3294 { "access-mode", required_argument, NULL, ARG_ACCESS_MODE },
3295 { "umask", required_argument, NULL, ARG_UMASK },
3296 { "nice", required_argument, NULL, ARG_NICE },
3297 { "rlimit", required_argument, NULL, ARG_RLIMIT },
3298 { "tasks-max", required_argument, NULL, ARG_TASKS_MAX },
3299 { "memory-high", required_argument, NULL, ARG_MEMORY_HIGH },
3300 { "memory-max", required_argument, NULL, ARG_MEMORY_MAX },
3301 { "cpu-weight", required_argument, NULL, ARG_CPU_WEIGHT },
3302 { "io-weight", required_argument, NULL, ARG_IO_WEIGHT },
3303 { "storage", required_argument, NULL, ARG_STORAGE },
3304 { "image-path", required_argument, NULL, ARG_IMAGE_PATH },
3305 { "fs-type", required_argument, NULL, ARG_FS_TYPE },
3306 { "luks-discard", required_argument, NULL, ARG_LUKS_DISCARD },
3307 { "luks-offline-discard", required_argument, NULL, ARG_LUKS_OFFLINE_DISCARD },
3308 { "luks-cipher", required_argument, NULL, ARG_LUKS_CIPHER },
3309 { "luks-cipher-mode", required_argument, NULL, ARG_LUKS_CIPHER_MODE },
3310 { "luks-volume-key-size", required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE },
3311 { "luks-pbkdf-type", required_argument, NULL, ARG_LUKS_PBKDF_TYPE },
3312 { "luks-pbkdf-hash-algorithm", required_argument, NULL, ARG_LUKS_PBKDF_HASH_ALGORITHM },
3313 { "luks-pbkdf-force-iterations", required_argument, NULL, ARG_LUKS_PBKDF_FORCE_ITERATIONS },
3314 { "luks-pbkdf-time-cost", required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST },
3315 { "luks-pbkdf-memory-cost", required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST },
3316 { "luks-pbkdf-parallel-threads", required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS },
3317 { "luks-sector-size", required_argument, NULL, ARG_LUKS_SECTOR_SIZE },
3318 { "nosuid", required_argument, NULL, ARG_NOSUID },
3319 { "nodev", required_argument, NULL, ARG_NODEV },
3320 { "noexec", required_argument, NULL, ARG_NOEXEC },
3321 { "cifs-user-name", required_argument, NULL, ARG_CIFS_USER_NAME },
3322 { "cifs-domain", required_argument, NULL, ARG_CIFS_DOMAIN },
3323 { "cifs-service", required_argument, NULL, ARG_CIFS_SERVICE },
3324 { "cifs-extra-mount-options", required_argument, NULL, ARG_CIFS_EXTRA_MOUNT_OPTIONS },
3325 { "rate-limit-interval", required_argument, NULL, ARG_RATE_LIMIT_INTERVAL },
3326 { "rate-limit-burst", required_argument, NULL, ARG_RATE_LIMIT_BURST },
3327 { "stop-delay", required_argument, NULL, ARG_STOP_DELAY },
3328 { "kill-processes", required_argument, NULL, ARG_KILL_PROCESSES },
3329 { "enforce-password-policy", required_argument, NULL, ARG_ENFORCE_PASSWORD_POLICY },
3330 { "password-change-now", required_argument, NULL, ARG_PASSWORD_CHANGE_NOW },
3331 { "password-change-min", required_argument, NULL, ARG_PASSWORD_CHANGE_MIN },
3332 { "password-change-max", required_argument, NULL, ARG_PASSWORD_CHANGE_MAX },
3333 { "password-change-warn", required_argument, NULL, ARG_PASSWORD_CHANGE_WARN },
3334 { "password-change-inactive", required_argument, NULL, ARG_PASSWORD_CHANGE_INACTIVE },
3335 { "auto-login", required_argument, NULL, ARG_AUTO_LOGIN },
3336 { "session-launcher", required_argument, NULL, ARG_SESSION_LAUNCHER, },
3337 { "session-type", required_argument, NULL, ARG_SESSION_TYPE, },
3338 { "json", required_argument, NULL, ARG_JSON },
3339 { "export-format", required_argument, NULL, ARG_EXPORT_FORMAT },
3340 { "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
3341 { "fido2-credential-algorithm", required_argument, NULL, ARG_FIDO2_CRED_ALG },
3342 { "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE },
3343 { "fido2-with-client-pin", required_argument, NULL, ARG_FIDO2_WITH_PIN },
3344 { "fido2-with-user-presence", required_argument, NULL, ARG_FIDO2_WITH_UP },
3345 { "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV },
3346 { "recovery-key", required_argument, NULL, ARG_RECOVERY_KEY },
3347 { "and-resize", required_argument, NULL, ARG_AND_RESIZE },
3348 { "and-change-password", required_argument, NULL, ARG_AND_CHANGE_PASSWORD },
3349 { "drop-caches", required_argument, NULL, ARG_DROP_CACHES },
3350 { "luks-extra-mount-options", required_argument, NULL, ARG_LUKS_EXTRA_MOUNT_OPTIONS },
3351 { "auto-resize-mode", required_argument, NULL, ARG_AUTO_RESIZE_MODE },
3352 { "rebalance-weight", required_argument, NULL, ARG_REBALANCE_WEIGHT },
3353 { "capability-bounding-set", required_argument, NULL, ARG_CAPABILITY_BOUNDING_SET },
3354 { "capability-ambient-set", required_argument, NULL, ARG_CAPABILITY_AMBIENT_SET },
3355 { "prompt-new-user", no_argument, NULL, ARG_PROMPT_NEW_USER },
3356 { "blob", required_argument, NULL, 'b' },
3357 { "avatar", required_argument, NULL, ARG_AVATAR },
3358 { "login-background", required_argument, NULL, ARG_LOGIN_BACKGROUND },
3359 { "tmp-limit", required_argument, NULL, ARG_TMP_LIMIT },
3360 { "dev-shm-limit", required_argument, NULL, ARG_DEV_SHM_LIMIT },
3361 { "default-area", required_argument, NULL, ARG_DEFAULT_AREA },
3362 { "key-name", required_argument, NULL, ARG_KEY_NAME },
3363 { "seize", required_argument, NULL, ARG_SEIZE },
3364 { "match", required_argument, NULL, ARG_MATCH },
3365 {}
3366 };
3367
3368 int r;
3369
3370 /* This points to one of arg_identity_extra, arg_identity_extra_this_machine,
3371 * arg_identity_extra_other_machines, in order to redirect changes on the next property being set to
3372 * this part of the identity, instead of the default. */
3373 sd_json_variant **match_identity = NULL;
3374
3375 assert(argc >= 0);
3376 assert(argv);
3377
3378 /* Eventually we should probably turn this into a proper --dry-run option, but as long as it is not hooked up everywhere let's make it an environment variable only. */
3379 r = getenv_bool("SYSTEMD_HOME_DRY_RUN");
3380 if (r >= 0)
3381 arg_dry_run = r;
3382 else if (r != -ENXIO)
3383 log_debug_errno(r, "Unable to parse $SYSTEMD_HOME_DRY_RUN, ignoring: %m");
3384
3385 for (;;) {
3386 int c;
3387
3388 c = getopt_long(argc, argv, "hH:M:I:c:d:u:G:k:s:e:b:jPENAT", options, NULL);
3389 if (c < 0)
3390 break;
3391
3392 switch (c) {
3393
3394 case 'h':
3395 return help(0, NULL, NULL);
3396
3397 case ARG_VERSION:
3398 return version();
3399
3400 case ARG_NO_PAGER:
3401 arg_pager_flags |= PAGER_DISABLE;
3402 break;
3403
3404 case ARG_NO_LEGEND:
3405 arg_legend = false;
3406 break;
3407
3408 case ARG_NO_ASK_PASSWORD:
3409 arg_ask_password = false;
3410 break;
3411
3412 case ARG_OFFLINE:
3413 arg_offline = true;
3414 break;
3415
3416 case 'H':
3417 arg_transport = BUS_TRANSPORT_REMOTE;
3418 arg_host = optarg;
3419 break;
3420
3421 case 'M':
3422 r = parse_machine_argument(optarg, &arg_host, &arg_transport);
3423 if (r < 0)
3424 return r;
3425 break;
3426
3427 case 'I':
3428 arg_identity = optarg;
3429 break;
3430
3431 case 'c':
3432 if (isempty(optarg)) {
3433 r = drop_from_identity("realName");
3434 if (r < 0)
3435 return r;
3436
3437 break;
3438 }
3439
3440 if (!valid_gecos(optarg))
3441 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Real name '%s' not a valid GECOS field.", optarg);
3442
3443 r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, "realName", optarg);
3444 if (r < 0)
3445 return log_error_errno(r, "Failed to set realName field: %m");
3446
3447 break;
3448
3449 case ARG_ALIAS: {
3450 if (isempty(optarg)) {
3451 r = drop_from_identity("aliases");
3452 if (r < 0)
3453 return r;
3454 break;
3455 }
3456
3457 for (const char *p = optarg;;) {
3458 _cleanup_free_ char *word = NULL;
3459
3460 r = extract_first_word(&p, &word, ",", 0);
3461 if (r < 0)
3462 return log_error_errno(r, "Failed to parse alias list: %m");
3463 if (r == 0)
3464 break;
3465
3466 if (!valid_user_group_name(word, 0))
3467 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid alias user name %s.", word);
3468
3469 _cleanup_(sd_json_variant_unrefp) sd_json_variant *av =
3470 sd_json_variant_ref(sd_json_variant_by_key(arg_identity_extra, "aliases"));
3471
3472 _cleanup_strv_free_ char **list = NULL;
3473 r = sd_json_variant_strv(av, &list);
3474 if (r < 0)
3475 return log_error_errno(r, "Failed to parse group list: %m");
3476
3477 r = strv_extend(&list, word);
3478 if (r < 0)
3479 return log_oom();
3480
3481 strv_sort_uniq(list);
3482
3483 av = sd_json_variant_unref(av);
3484 r = sd_json_variant_new_array_strv(&av, list);
3485 if (r < 0)
3486 return log_error_errno(r, "Failed to create alias list JSON: %m");
3487
3488 r = sd_json_variant_set_field(&arg_identity_extra, "aliases", av);
3489 if (r < 0)
3490 return log_error_errno(r, "Failed to update alias list: %m");
3491 }
3492
3493 break;
3494 }
3495
3496 case 'd': {
3497 _cleanup_free_ char *hd = NULL;
3498
3499 if (isempty(optarg)) {
3500 r = drop_from_identity("homeDirectory");
3501 if (r < 0)
3502 return r;
3503
3504 break;
3505 }
3506
3507 r = parse_path_argument(optarg, false, &hd);
3508 if (r < 0)
3509 return r;
3510
3511 if (!valid_home(hd))
3512 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Home directory '%s' not valid.", hd);
3513
3514 r = sd_json_variant_set_field_string(&arg_identity_extra, "homeDirectory", hd);
3515 if (r < 0)
3516 return log_error_errno(r, "Failed to set homeDirectory field: %m");
3517
3518 break;
3519 }
3520
3521 case ARG_REALM:
3522 if (isempty(optarg)) {
3523 r = drop_from_identity("realm");
3524 if (r < 0)
3525 return r;
3526
3527 break;
3528 }
3529
3530 r = dns_name_is_valid(optarg);
3531 if (r < 0)
3532 return log_error_errno(r, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg);
3533 if (r == 0)
3534 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Realm '%s' is not a valid DNS domain.", optarg);
3535
3536 r = sd_json_variant_set_field_string(&arg_identity_extra, "realm", optarg);
3537 if (r < 0)
3538 return log_error_errno(r, "Failed to set realm field: %m");
3539 break;
3540
3541 case ARG_EMAIL_ADDRESS:
3542 case ARG_LOCATION:
3543 case ARG_ICON_NAME:
3544 case ARG_CIFS_USER_NAME:
3545 case ARG_CIFS_DOMAIN:
3546 case ARG_CIFS_EXTRA_MOUNT_OPTIONS:
3547 case ARG_LUKS_EXTRA_MOUNT_OPTIONS:
3548 case ARG_SESSION_LAUNCHER:
3549 case ARG_SESSION_TYPE: {
3550
3551 const char *field =
3552 c == ARG_EMAIL_ADDRESS ? "emailAddress" :
3553 c == ARG_LOCATION ? "location" :
3554 c == ARG_ICON_NAME ? "iconName" :
3555 c == ARG_CIFS_USER_NAME ? "cifsUserName" :
3556 c == ARG_CIFS_DOMAIN ? "cifsDomain" :
3557 c == ARG_CIFS_EXTRA_MOUNT_OPTIONS ? "cifsExtraMountOptions" :
3558 c == ARG_LUKS_EXTRA_MOUNT_OPTIONS ? "luksExtraMountOptions" :
3559 c == ARG_SESSION_LAUNCHER ? "preferredSessionLauncher" :
3560 c == ARG_SESSION_TYPE ? "preferredSessionType" :
3561 NULL;
3562
3563 assert(field);
3564
3565 if (isempty(optarg)) {
3566 r = drop_from_identity(field);
3567 if (r < 0)
3568 return r;
3569
3570 break;
3571 }
3572
3573 r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, field, optarg);
3574 if (r < 0)
3575 return log_error_errno(r, "Failed to set %s field: %m", field);
3576
3577 break;
3578 }
3579
3580 case ARG_CIFS_SERVICE:
3581 if (isempty(optarg)) {
3582 r = drop_from_identity("cifsService");
3583 if (r < 0)
3584 return r;
3585
3586 break;
3587 }
3588
3589 r = parse_cifs_service(optarg, NULL, NULL, NULL);
3590 if (r < 0)
3591 return log_error_errno(r, "Failed to validate CIFS service name: %s", optarg);
3592
3593 r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, "cifsService", optarg);
3594 if (r < 0)
3595 return log_error_errno(r, "Failed to set cifsService field: %m");
3596
3597 break;
3598
3599 case ARG_PASSWORD_HINT:
3600 if (isempty(optarg)) {
3601 r = drop_from_identity("passwordHint");
3602 if (r < 0)
3603 return r;
3604
3605 break;
3606 }
3607
3608 r = sd_json_variant_set_field_string(&arg_identity_extra_privileged, "passwordHint", optarg);
3609 if (r < 0)
3610 return log_error_errno(r, "Failed to set passwordHint field: %m");
3611
3612 string_erase(optarg);
3613 break;
3614
3615 case ARG_NICE: {
3616 int nc;
3617
3618 if (isempty(optarg)) {
3619 r = drop_from_identity("niceLevel");
3620 if (r < 0)
3621 return r;
3622 break;
3623 }
3624
3625 r = parse_nice(optarg, &nc);
3626 if (r < 0)
3627 return log_error_errno(r, "Failed to parse nice level: %s", optarg);
3628
3629 r = sd_json_variant_set_field_integer(match_identity ?: &arg_identity_extra, "niceLevel", nc);
3630 if (r < 0)
3631 return log_error_errno(r, "Failed to set niceLevel field: %m");
3632
3633 break;
3634 }
3635
3636 case ARG_RLIMIT: {
3637 _cleanup_(sd_json_variant_unrefp) sd_json_variant *jcur = NULL, *jmax = NULL;
3638 _cleanup_free_ char *field = NULL, *t = NULL;
3639 const char *eq;
3640 struct rlimit rl;
3641 int l;
3642
3643 if (isempty(optarg)) {
3644 /* Remove all resource limits */
3645
3646 r = drop_from_identity("resourceLimits");
3647 if (r < 0)
3648 return r;
3649
3650 arg_identity_filter_rlimits = strv_free(arg_identity_filter_rlimits);
3651 arg_identity_extra_rlimits = sd_json_variant_unref(arg_identity_extra_rlimits);
3652 break;
3653 }
3654
3655 eq = strchr(optarg, '=');
3656 if (!eq)
3657 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't parse resource limit assignment: %s", optarg);
3658
3659 field = strndup(optarg, eq - optarg);
3660 if (!field)
3661 return log_oom();
3662
3663 l = rlimit_from_string_harder(field);
3664 if (l < 0)
3665 return log_error_errno(l, "Unknown resource limit type: %s", field);
3666
3667 if (isempty(eq + 1)) {
3668 /* Remove only the specific rlimit */
3669
3670 r = strv_extend(&arg_identity_filter_rlimits, rlimit_to_string(l));
3671 if (r < 0)
3672 return r;
3673
3674 r = sd_json_variant_filter(&arg_identity_extra_rlimits, STRV_MAKE(field));
3675 if (r < 0)
3676 return log_error_errno(r, "Failed to filter JSON identity data: %m");
3677
3678 break;
3679 }
3680
3681 r = rlimit_parse(l, eq + 1, &rl);
3682 if (r < 0)
3683 return log_error_errno(r, "Failed to parse resource limit value: %s", eq + 1);
3684
3685 r = rl.rlim_cur == RLIM_INFINITY ? sd_json_variant_new_null(&jcur) : sd_json_variant_new_unsigned(&jcur, rl.rlim_cur);
3686 if (r < 0)
3687 return log_error_errno(r, "Failed to allocate current integer: %m");
3688
3689 r = rl.rlim_max == RLIM_INFINITY ? sd_json_variant_new_null(&jmax) : sd_json_variant_new_unsigned(&jmax, rl.rlim_max);
3690 if (r < 0)
3691 return log_error_errno(r, "Failed to allocate maximum integer: %m");
3692
3693 t = strjoin("RLIMIT_", rlimit_to_string(l));
3694 if (!t)
3695 return log_oom();
3696
3697 r = sd_json_variant_set_fieldbo(
3698 &arg_identity_extra_rlimits, t,
3699 SD_JSON_BUILD_PAIR("cur", SD_JSON_BUILD_VARIANT(jcur)),
3700 SD_JSON_BUILD_PAIR("max", SD_JSON_BUILD_VARIANT(jmax)));
3701 if (r < 0)
3702 return log_error_errno(r, "Failed to set %s field: %m", rlimit_to_string(l));
3703
3704 break;
3705 }
3706
3707 case 'u': {
3708 uid_t uid;
3709
3710 if (isempty(optarg)) {
3711 r = drop_from_identity("uid");
3712 if (r < 0)
3713 return r;
3714
3715 break;
3716 }
3717
3718 r = parse_uid(optarg, &uid);
3719 if (r < 0)
3720 return log_error_errno(r, "Failed to parse UID '%s'.", optarg);
3721
3722 if (uid_is_system(uid))
3723 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in system range, refusing.", uid);
3724 if (uid_is_greeter(uid))
3725 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in greeter range, refusing.", uid);
3726 if (uid_is_dynamic(uid))
3727 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in dynamic range, refusing.", uid);
3728 if (uid == UID_NOBODY)
3729 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is nobody UID, refusing.", uid);
3730
3731 r = sd_json_variant_set_field_unsigned(&arg_identity_extra, "uid", uid);
3732 if (r < 0)
3733 return log_error_errno(r, "Failed to set realm field: %m");
3734
3735 break;
3736 }
3737
3738 case 'k':
3739 case ARG_IMAGE_PATH: {
3740 const char *field = c == 'k' ? "skeletonDirectory" : "imagePath";
3741 _cleanup_free_ char *v = NULL;
3742
3743 if (isempty(optarg)) {
3744 r = drop_from_identity(field);
3745 if (r < 0)
3746 return r;
3747
3748 break;
3749 }
3750
3751 r = parse_path_argument(optarg, false, &v);
3752 if (r < 0)
3753 return r;
3754
3755 r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra_this_machine, field, v);
3756 if (r < 0)
3757 return log_error_errno(r, "Failed to set %s field: %m", v);
3758
3759 break;
3760 }
3761
3762 case 's':
3763 if (isempty(optarg)) {
3764 r = drop_from_identity("shell");
3765 if (r < 0)
3766 return r;
3767
3768 break;
3769 }
3770
3771 if (!valid_shell(optarg))
3772 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Shell '%s' not valid.", optarg);
3773
3774 r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, "shell", optarg);
3775 if (r < 0)
3776 return log_error_errno(r, "Failed to set shell field: %m");
3777
3778 break;
3779
3780 case ARG_SETENV: {
3781 _cleanup_free_ char **l = NULL;
3782 _cleanup_(sd_json_variant_unrefp) sd_json_variant *ne = NULL;
3783 sd_json_variant *e;
3784
3785 if (isempty(optarg)) {
3786 r = drop_from_identity("environment");
3787 if (r < 0)
3788 return r;
3789
3790 break;
3791 }
3792
3793 e = sd_json_variant_by_key(match_identity ? *match_identity: arg_identity_extra, "environment");
3794 if (e) {
3795 r = sd_json_variant_strv(e, &l);
3796 if (r < 0)
3797 return log_error_errno(r, "Failed to parse JSON environment field: %m");
3798 }
3799
3800 r = strv_env_replace_strdup_passthrough(&l, optarg);
3801 if (r < 0)
3802 return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
3803
3804 strv_sort(l);
3805
3806 r = sd_json_variant_new_array_strv(&ne, l);
3807 if (r < 0)
3808 return log_error_errno(r, "Failed to allocate environment list JSON: %m");
3809
3810 r = sd_json_variant_set_field(match_identity ?: &arg_identity_extra, "environment", ne);
3811 if (r < 0)
3812 return log_error_errno(r, "Failed to set environment list: %m");
3813
3814 break;
3815 }
3816
3817 case ARG_TIMEZONE:
3818 if (isempty(optarg)) {
3819 r = drop_from_identity("timeZone");
3820 if (r < 0)
3821 return r;
3822
3823 break;
3824 }
3825
3826 if (!timezone_is_valid(optarg, LOG_DEBUG))
3827 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Timezone '%s' is not valid.", optarg);
3828
3829 r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, "timeZone", optarg);
3830 if (r < 0)
3831 return log_error_errno(r, "Failed to set timezone field: %m");
3832
3833 break;
3834
3835 case ARG_LANGUAGE: {
3836 const char *p = optarg;
3837
3838 if (isempty(p)) {
3839 r = drop_from_identity("preferredLanguage");
3840 if (r < 0)
3841 return r;
3842
3843 r = drop_from_identity("additionalLanguages");
3844 if (r < 0)
3845 return r;
3846
3847 arg_languages = strv_free(arg_languages);
3848 break;
3849 }
3850
3851 for (;;) {
3852 _cleanup_free_ char *word = NULL;
3853
3854 r = extract_first_word(&p, &word, ",:", 0);
3855 if (r < 0)
3856 return log_error_errno(r, "Failed to parse locale list: %m");
3857 if (r == 0)
3858 break;
3859
3860 if (!locale_is_valid(word))
3861 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale '%s' is not valid.", word);
3862
3863 if (locale_is_installed(word) <= 0)
3864 log_warning("Locale '%s' is not installed, accepting anyway.", word);
3865
3866 r = strv_consume(&arg_languages, TAKE_PTR(word));
3867 if (r < 0)
3868 return log_oom();
3869
3870 strv_uniq(arg_languages);
3871 }
3872
3873 break;
3874 }
3875
3876 case ARG_NOSUID:
3877 case ARG_NODEV:
3878 case ARG_NOEXEC:
3879 case ARG_LOCKED:
3880 case ARG_KILL_PROCESSES:
3881 case ARG_ENFORCE_PASSWORD_POLICY:
3882 case ARG_AUTO_LOGIN:
3883 case ARG_PASSWORD_CHANGE_NOW: {
3884 const char *field =
3885 c == ARG_LOCKED ? "locked" :
3886 c == ARG_NOSUID ? "mountNoSuid" :
3887 c == ARG_NODEV ? "mountNoDevices" :
3888 c == ARG_NOEXEC ? "mountNoExecute" :
3889 c == ARG_KILL_PROCESSES ? "killProcesses" :
3890 c == ARG_ENFORCE_PASSWORD_POLICY ? "enforcePasswordPolicy" :
3891 c == ARG_AUTO_LOGIN ? "autoLogin" :
3892 c == ARG_PASSWORD_CHANGE_NOW ? "passwordChangeNow" :
3893 NULL;
3894
3895 assert(field);
3896
3897 if (isempty(optarg)) {
3898 r = drop_from_identity(field);
3899 if (r < 0)
3900 return r;
3901
3902 break;
3903 }
3904
3905 r = parse_boolean(optarg);
3906 if (r < 0)
3907 return log_error_errno(r, "Failed to parse %s boolean: %m", field);
3908
3909 r = sd_json_variant_set_field_boolean(match_identity ?: &arg_identity_extra, field, r > 0);
3910 if (r < 0)
3911 return log_error_errno(r, "Failed to set %s field: %m", field);
3912
3913 break;
3914 }
3915
3916 case 'P':
3917 r = sd_json_variant_set_field_boolean(&arg_identity_extra, "enforcePasswordPolicy", false);
3918 if (r < 0)
3919 return log_error_errno(r, "Failed to set enforcePasswordPolicy field: %m");
3920
3921 break;
3922
3923 case ARG_DISK_SIZE:
3924 if (isempty(optarg)) {
3925 FOREACH_STRING(prop, "diskSize", "diskSizeRelative", "rebalanceWeight") {
3926 r = drop_from_identity(prop);
3927 if (r < 0)
3928 return r;
3929 }
3930
3931 arg_disk_size = arg_disk_size_relative = UINT64_MAX;
3932 break;
3933 }
3934
3935 r = parse_permyriad(optarg);
3936 if (r < 0) {
3937 r = parse_disk_size(optarg, &arg_disk_size);
3938 if (r < 0)
3939 return r;
3940
3941 r = drop_from_identity("diskSizeRelative");
3942 if (r < 0)
3943 return r;
3944
3945 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra_this_machine, "diskSize", arg_disk_size);
3946 if (r < 0)
3947 return log_error_errno(r, "Failed to set diskSize field: %m");
3948
3949 arg_disk_size_relative = UINT64_MAX;
3950 } else {
3951 /* Normalize to UINT32_MAX == 100% */
3952 arg_disk_size_relative = UINT32_SCALE_FROM_PERMYRIAD(r);
3953
3954 r = drop_from_identity("diskSize");
3955 if (r < 0)
3956 return r;
3957
3958 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra_this_machine, "diskSizeRelative", arg_disk_size_relative);
3959 if (r < 0)
3960 return log_error_errno(r, "Failed to set diskSizeRelative field: %m");
3961
3962 arg_disk_size = UINT64_MAX;
3963 }
3964
3965 /* Automatically turn off the rebalance logic if user configured a size explicitly */
3966 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra_this_machine, "rebalanceWeight", REBALANCE_WEIGHT_OFF);
3967 if (r < 0)
3968 return log_error_errno(r, "Failed to set rebalanceWeight field: %m");
3969
3970 break;
3971
3972 case ARG_ACCESS_MODE: {
3973 mode_t mode;
3974
3975 if (isempty(optarg)) {
3976 r = drop_from_identity("accessMode");
3977 if (r < 0)
3978 return r;
3979
3980 break;
3981 }
3982
3983 r = parse_mode(optarg, &mode);
3984 if (r < 0)
3985 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Access mode '%s' not valid.", optarg);
3986
3987 r = sd_json_variant_set_field_unsigned(&arg_identity_extra, "accessMode", mode);
3988 if (r < 0)
3989 return log_error_errno(r, "Failed to set access mode field: %m");
3990
3991 break;
3992 }
3993
3994 case ARG_LUKS_DISCARD:
3995 if (isempty(optarg)) {
3996 r = drop_from_identity("luksDiscard");
3997 if (r < 0)
3998 return r;
3999
4000 break;
4001 }
4002
4003 r = parse_boolean(optarg);
4004 if (r < 0)
4005 return log_error_errno(r, "Failed to parse --luks-discard= parameter: %s", optarg);
4006
4007 r = sd_json_variant_set_field_boolean(match_identity ?: &arg_identity_extra, "luksDiscard", r);
4008 if (r < 0)
4009 return log_error_errno(r, "Failed to set discard field: %m");
4010
4011 break;
4012
4013 case ARG_LUKS_OFFLINE_DISCARD:
4014 if (isempty(optarg)) {
4015 r = drop_from_identity("luksOfflineDiscard");
4016 if (r < 0)
4017 return r;
4018
4019 break;
4020 }
4021
4022 r = parse_boolean(optarg);
4023 if (r < 0)
4024 return log_error_errno(r, "Failed to parse --luks-offline-discard= parameter: %s", optarg);
4025
4026 r = sd_json_variant_set_field_boolean(match_identity ?: &arg_identity_extra, "luksOfflineDiscard", r);
4027 if (r < 0)
4028 return log_error_errno(r, "Failed to set offline discard field: %m");
4029
4030 break;
4031
4032 case ARG_LUKS_VOLUME_KEY_SIZE:
4033 case ARG_LUKS_PBKDF_FORCE_ITERATIONS:
4034 case ARG_LUKS_PBKDF_PARALLEL_THREADS:
4035 case ARG_RATE_LIMIT_BURST: {
4036 const char *field =
4037 c == ARG_LUKS_VOLUME_KEY_SIZE ? "luksVolumeKeySize" :
4038 c == ARG_LUKS_PBKDF_FORCE_ITERATIONS ? "luksPbkdfForceIterations" :
4039 c == ARG_LUKS_PBKDF_PARALLEL_THREADS ? "luksPbkdfParallelThreads" :
4040 c == ARG_RATE_LIMIT_BURST ? "rateLimitBurst" : NULL;
4041 unsigned n;
4042
4043 assert(field);
4044
4045 if (isempty(optarg)) {
4046 r = drop_from_identity(field);
4047 if (r < 0)
4048 return r;
4049 }
4050
4051 r = safe_atou(optarg, &n);
4052 if (r < 0)
4053 return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
4054
4055 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field, n);
4056 if (r < 0)
4057 return log_error_errno(r, "Failed to set %s field: %m", field);
4058
4059 break;
4060 }
4061
4062 case ARG_LUKS_SECTOR_SIZE: {
4063 uint64_t ss;
4064
4065 if (isempty(optarg)) {
4066 r = drop_from_identity("luksSectorSize");
4067 if (r < 0)
4068 return r;
4069
4070 break;
4071 }
4072
4073 r = parse_sector_size(optarg, &ss);
4074 if (r < 0)
4075 return r;
4076
4077 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, "luksSectorSize", ss);
4078 if (r < 0)
4079 return log_error_errno(r, "Failed to set sector size field: %m");
4080
4081 break;
4082 }
4083
4084 case ARG_UMASK: {
4085 mode_t m;
4086
4087 if (isempty(optarg)) {
4088 r = drop_from_identity("umask");
4089 if (r < 0)
4090 return r;
4091
4092 break;
4093 }
4094
4095 r = parse_mode(optarg, &m);
4096 if (r < 0)
4097 return log_error_errno(r, "Failed to parse umask: %m");
4098
4099 r = sd_json_variant_set_field_integer(match_identity ?: &arg_identity_extra, "umask", m);
4100 if (r < 0)
4101 return log_error_errno(r, "Failed to set umask field: %m");
4102
4103 break;
4104 }
4105
4106 case ARG_SSH_AUTHORIZED_KEYS: {
4107 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
4108 _cleanup_strv_free_ char **l = NULL, **add = NULL;
4109
4110 if (isempty(optarg)) {
4111 r = drop_from_identity("sshAuthorizedKeys");
4112 if (r < 0)
4113 return r;
4114
4115 break;
4116 }
4117
4118 if (optarg[0] == '@') {
4119 _cleanup_fclose_ FILE *f = NULL;
4120
4121 /* If prefixed with '@' read from a file */
4122
4123 f = fopen(optarg+1, "re");
4124 if (!f)
4125 return log_error_errno(errno, "Failed to open '%s': %m", optarg+1);
4126
4127 for (;;) {
4128 _cleanup_free_ char *line = NULL;
4129
4130 r = read_line(f, LONG_LINE_MAX, &line);
4131 if (r < 0)
4132 return log_error_errno(r, "Failed to read from '%s': %m", optarg+1);
4133 if (r == 0)
4134 break;
4135
4136 if (isempty(line))
4137 continue;
4138
4139 if (line[0] == '#')
4140 continue;
4141
4142 r = strv_consume(&add, TAKE_PTR(line));
4143 if (r < 0)
4144 return log_oom();
4145 }
4146 } else {
4147 /* Otherwise, assume it's a literal key. Let's do some superficial checks
4148 * before accept it though. */
4149
4150 if (string_has_cc(optarg, NULL))
4151 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Authorized key contains control characters, refusing.");
4152 if (optarg[0] == '#')
4153 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified key is a comment?");
4154
4155 add = strv_new(optarg);
4156 if (!add)
4157 return log_oom();
4158 }
4159
4160 v = sd_json_variant_ref(sd_json_variant_by_key(arg_identity_extra_privileged, "sshAuthorizedKeys"));
4161 if (v) {
4162 r = sd_json_variant_strv(v, &l);
4163 if (r < 0)
4164 return log_error_errno(r, "Failed to parse SSH authorized keys list: %m");
4165 }
4166
4167 r = strv_extend_strv_consume(&l, TAKE_PTR(add), /* filter_duplicates = */ true);
4168 if (r < 0)
4169 return log_oom();
4170
4171 v = sd_json_variant_unref(v);
4172
4173 r = sd_json_variant_new_array_strv(&v, l);
4174 if (r < 0)
4175 return log_oom();
4176
4177 r = sd_json_variant_set_field(&arg_identity_extra_privileged, "sshAuthorizedKeys", v);
4178 if (r < 0)
4179 return log_error_errno(r, "Failed to set authorized keys: %m");
4180
4181 break;
4182 }
4183
4184 case ARG_NOT_BEFORE:
4185 case ARG_NOT_AFTER:
4186 case 'e': {
4187 const char *field;
4188 usec_t n;
4189
4190 field = c == ARG_NOT_BEFORE ? "notBeforeUSec" :
4191 IN_SET(c, ARG_NOT_AFTER, 'e') ? "notAfterUSec" : NULL;
4192
4193 assert(field);
4194
4195 if (isempty(optarg)) {
4196 r = drop_from_identity(field);
4197 if (r < 0)
4198 return r;
4199
4200 break;
4201 }
4202
4203 /* Note the minor discrepancy regarding -e parsing here: we support that for compat
4204 * reasons, and in the original useradd(8) implementation it accepts dates in the
4205 * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
4206 * with greater precision. */
4207 r = parse_timestamp(optarg, &n);
4208 if (r < 0)
4209 return log_error_errno(r, "Failed to parse %s parameter: %m", field);
4210
4211 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field, n);
4212 if (r < 0)
4213 return log_error_errno(r, "Failed to set %s field: %m", field);
4214 break;
4215 }
4216
4217 case ARG_PASSWORD_CHANGE_MIN:
4218 case ARG_PASSWORD_CHANGE_MAX:
4219 case ARG_PASSWORD_CHANGE_WARN:
4220 case ARG_PASSWORD_CHANGE_INACTIVE: {
4221 const char *field;
4222 usec_t n;
4223
4224 field = c == ARG_PASSWORD_CHANGE_MIN ? "passwordChangeMinUSec" :
4225 c == ARG_PASSWORD_CHANGE_MAX ? "passwordChangeMaxUSec" :
4226 c == ARG_PASSWORD_CHANGE_WARN ? "passwordChangeWarnUSec" :
4227 c == ARG_PASSWORD_CHANGE_INACTIVE ? "passwordChangeInactiveUSec" :
4228 NULL;
4229
4230 assert(field);
4231
4232 if (isempty(optarg)) {
4233 r = drop_from_identity(field);
4234 if (r < 0)
4235 return r;
4236
4237 break;
4238 }
4239
4240 r = parse_sec(optarg, &n);
4241 if (r < 0)
4242 return log_error_errno(r, "Failed to parse %s parameter: %m", field);
4243
4244 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field, n);
4245 if (r < 0)
4246 return log_error_errno(r, "Failed to set %s field: %m", field);
4247 break;
4248 }
4249
4250 case ARG_STORAGE:
4251 case ARG_FS_TYPE:
4252 case ARG_LUKS_CIPHER:
4253 case ARG_LUKS_CIPHER_MODE:
4254 case ARG_LUKS_PBKDF_TYPE:
4255 case ARG_LUKS_PBKDF_HASH_ALGORITHM: {
4256
4257 const char *field =
4258 c == ARG_STORAGE ? "storage" :
4259 c == ARG_FS_TYPE ? "fileSystemType" :
4260 c == ARG_LUKS_CIPHER ? "luksCipher" :
4261 c == ARG_LUKS_CIPHER_MODE ? "luksCipherMode" :
4262 c == ARG_LUKS_PBKDF_TYPE ? "luksPbkdfType" :
4263 c == ARG_LUKS_PBKDF_HASH_ALGORITHM ? "luksPbkdfHashAlgorithm" : NULL;
4264
4265 assert(field);
4266
4267 if (isempty(optarg)) {
4268 r = drop_from_identity(field);
4269 if (r < 0)
4270 return r;
4271
4272 break;
4273 }
4274
4275 if (!string_is_safe(optarg))
4276 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parameter for %s field not valid: %s", field, optarg);
4277
4278 r = sd_json_variant_set_field_string(
4279 match_identity ?: (IN_SET(c, ARG_STORAGE, ARG_FS_TYPE) ?
4280 &arg_identity_extra_this_machine :
4281 &arg_identity_extra), field, optarg);
4282 if (r < 0)
4283 return log_error_errno(r, "Failed to set %s field: %m", field);
4284
4285 break;
4286 }
4287
4288 case ARG_LUKS_PBKDF_TIME_COST:
4289 case ARG_RATE_LIMIT_INTERVAL:
4290 case ARG_STOP_DELAY: {
4291 const char *field =
4292 c == ARG_LUKS_PBKDF_TIME_COST ? "luksPbkdfTimeCostUSec" :
4293 c == ARG_RATE_LIMIT_INTERVAL ? "rateLimitIntervalUSec" :
4294 c == ARG_STOP_DELAY ? "stopDelayUSec" :
4295 NULL;
4296 usec_t t;
4297
4298 assert(field);
4299
4300 if (isempty(optarg)) {
4301 r = drop_from_identity(field);
4302 if (r < 0)
4303 return r;
4304
4305 break;
4306 }
4307
4308 r = parse_sec(optarg, &t);
4309 if (r < 0)
4310 return log_error_errno(r, "Failed to parse %s field: %s", field, optarg);
4311
4312 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field, t);
4313 if (r < 0)
4314 return log_error_errno(r, "Failed to set %s field: %m", field);
4315
4316 break;
4317 }
4318
4319 case 'G': {
4320 const char *p = optarg;
4321
4322 if (isempty(p)) {
4323 r = drop_from_identity("memberOf");
4324 if (r < 0)
4325 return r;
4326
4327 break;
4328 }
4329
4330 for (;;) {
4331 _cleanup_(sd_json_variant_unrefp) sd_json_variant *mo = NULL;
4332 _cleanup_strv_free_ char **list = NULL;
4333 _cleanup_free_ char *word = NULL;
4334
4335 r = extract_first_word(&p, &word, ",", 0);
4336 if (r < 0)
4337 return log_error_errno(r, "Failed to parse group list: %m");
4338 if (r == 0)
4339 break;
4340
4341 if (!valid_user_group_name(word, 0))
4342 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid group name %s.", word);
4343
4344 mo = sd_json_variant_ref(sd_json_variant_by_key(arg_identity_extra, "memberOf"));
4345
4346 r = sd_json_variant_strv(mo, &list);
4347 if (r < 0)
4348 return log_error_errno(r, "Failed to parse group list: %m");
4349
4350 r = strv_extend(&list, word);
4351 if (r < 0)
4352 return log_oom();
4353
4354 strv_sort_uniq(list);
4355
4356 mo = sd_json_variant_unref(mo);
4357 r = sd_json_variant_new_array_strv(&mo, list);
4358 if (r < 0)
4359 return log_error_errno(r, "Failed to create group list JSON: %m");
4360
4361 r = sd_json_variant_set_field(match_identity ?: &arg_identity_extra, "memberOf", mo);
4362 if (r < 0)
4363 return log_error_errno(r, "Failed to update group list: %m");
4364 }
4365
4366 break;
4367 }
4368
4369 case ARG_TASKS_MAX: {
4370 uint64_t u;
4371
4372 if (isempty(optarg)) {
4373 r = drop_from_identity("tasksMax");
4374 if (r < 0)
4375 return r;
4376 break;
4377 }
4378
4379 r = safe_atou64(optarg, &u);
4380 if (r < 0)
4381 return log_error_errno(r, "Failed to parse --tasks-max= parameter: %s", optarg);
4382
4383 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, "tasksMax", u);
4384 if (r < 0)
4385 return log_error_errno(r, "Failed to set tasksMax field: %m");
4386
4387 break;
4388 }
4389
4390 case ARG_MEMORY_MAX:
4391 case ARG_MEMORY_HIGH:
4392 case ARG_LUKS_PBKDF_MEMORY_COST: {
4393 const char *field =
4394 c == ARG_MEMORY_MAX ? "memoryMax" :
4395 c == ARG_MEMORY_HIGH ? "memoryHigh" :
4396 c == ARG_LUKS_PBKDF_MEMORY_COST ? "luksPbkdfMemoryCost" : NULL;
4397
4398 uint64_t u;
4399
4400 assert(field);
4401
4402 if (isempty(optarg)) {
4403 r = drop_from_identity(field);
4404 if (r < 0)
4405 return r;
4406 break;
4407 }
4408
4409 r = parse_size(optarg, 1024, &u);
4410 if (r < 0)
4411 return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
4412
4413 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra_this_machine, field, u);
4414 if (r < 0)
4415 return log_error_errno(r, "Failed to set %s field: %m", field);
4416
4417 break;
4418 }
4419
4420 case ARG_CPU_WEIGHT:
4421 case ARG_IO_WEIGHT: {
4422 const char *field = c == ARG_CPU_WEIGHT ? "cpuWeight" :
4423 c == ARG_IO_WEIGHT ? "ioWeight" : NULL;
4424 uint64_t u;
4425
4426 assert(field);
4427
4428 if (isempty(optarg)) {
4429 r = drop_from_identity(field);
4430 if (r < 0)
4431 return r;
4432 break;
4433 }
4434
4435 r = safe_atou64(optarg, &u);
4436 if (r < 0)
4437 return log_error_errno(r, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg);
4438
4439 if (!CGROUP_WEIGHT_IS_OK(u))
4440 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Weight %" PRIu64 " is out of valid weight range.", u);
4441
4442 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field, u);
4443 if (r < 0)
4444 return log_error_errno(r, "Failed to set %s field: %m", field);
4445
4446 break;
4447 }
4448
4449 case ARG_PKCS11_TOKEN_URI:
4450 if (streq(optarg, "list"))
4451 return pkcs11_list_tokens();
4452
4453 /* If --pkcs11-token-uri= is specified we always drop everything old */
4454 FOREACH_STRING(p, "pkcs11TokenUri", "pkcs11EncryptedKey") {
4455 r = drop_from_identity(p);
4456 if (r < 0)
4457 return r;
4458 }
4459
4460 if (isempty(optarg)) {
4461 arg_pkcs11_token_uri = strv_free(arg_pkcs11_token_uri);
4462 break;
4463 }
4464
4465 if (streq(optarg, "auto")) {
4466 _cleanup_free_ char *found = NULL;
4467
4468 r = pkcs11_find_token_auto(&found);
4469 if (r < 0)
4470 return r;
4471 r = strv_consume(&arg_pkcs11_token_uri, TAKE_PTR(found));
4472 } else {
4473 if (!pkcs11_uri_valid(optarg))
4474 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg);
4475
4476 r = strv_extend(&arg_pkcs11_token_uri, optarg);
4477 }
4478 if (r < 0)
4479 return r;
4480
4481 strv_uniq(arg_pkcs11_token_uri);
4482 break;
4483
4484 case ARG_FIDO2_CRED_ALG:
4485 r = parse_fido2_algorithm(optarg, &arg_fido2_cred_alg);
4486 if (r < 0)
4487 return log_error_errno(r, "Failed to parse COSE algorithm: %s", optarg);
4488 break;
4489
4490 case ARG_FIDO2_DEVICE:
4491 if (streq(optarg, "list"))
4492 return fido2_list_devices();
4493
4494 FOREACH_STRING(p, "fido2HmacCredential", "fido2HmacSalt") {
4495 r = drop_from_identity(p);
4496 if (r < 0)
4497 return r;
4498 }
4499
4500 if (isempty(optarg)) {
4501 arg_fido2_device = strv_free(arg_fido2_device);
4502 break;
4503 }
4504
4505 if (streq(optarg, "auto")) {
4506 _cleanup_free_ char *found = NULL;
4507
4508 r = fido2_find_device_auto(&found);
4509 if (r < 0)
4510 return r;
4511
4512 r = strv_consume(&arg_fido2_device, TAKE_PTR(found));
4513 } else
4514 r = strv_extend(&arg_fido2_device, optarg);
4515 if (r < 0)
4516 return r;
4517
4518 strv_uniq(arg_fido2_device);
4519 break;
4520
4521 case ARG_FIDO2_WITH_PIN:
4522 r = parse_boolean_argument("--fido2-with-client-pin=", optarg, NULL);
4523 if (r < 0)
4524 return r;
4525
4526 SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_PIN, r);
4527 break;
4528
4529 case ARG_FIDO2_WITH_UP:
4530 r = parse_boolean_argument("--fido2-with-user-presence=", optarg, NULL);
4531 if (r < 0)
4532 return r;
4533
4534 SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UP, r);
4535 break;
4536
4537 case ARG_FIDO2_WITH_UV:
4538 r = parse_boolean_argument("--fido2-with-user-verification=", optarg, NULL);
4539 if (r < 0)
4540 return r;
4541
4542 SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UV, r);
4543 break;
4544
4545 case ARG_RECOVERY_KEY:
4546 r = parse_boolean(optarg);
4547 if (r < 0)
4548 return log_error_errno(r, "Failed to parse --recovery-key= argument: %s", optarg);
4549
4550 arg_recovery_key = r;
4551
4552 FOREACH_STRING(p, "recoveryKey", "recoveryKeyType") {
4553 r = drop_from_identity(p);
4554 if (r < 0)
4555 return r;
4556 }
4557
4558 break;
4559
4560 case ARG_AUTO_RESIZE_MODE:
4561 if (isempty(optarg)) {
4562 r = drop_from_identity("autoResizeMode");
4563 if (r < 0)
4564 return r;
4565
4566 break;
4567 }
4568
4569 r = auto_resize_mode_from_string(optarg);
4570 if (r < 0)
4571 return log_error_errno(r, "Failed to parse --auto-resize-mode= argument: %s", optarg);
4572
4573 r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, "autoResizeMode", auto_resize_mode_to_string(r));
4574 if (r < 0)
4575 return log_error_errno(r, "Failed to set autoResizeMode field: %m");
4576
4577 break;
4578
4579 case ARG_REBALANCE_WEIGHT: {
4580 uint64_t u;
4581
4582 if (isempty(optarg)) {
4583 r = drop_from_identity("rebalanceWeight");
4584 if (r < 0)
4585 return r;
4586 break;
4587 }
4588
4589 if (streq(optarg, "off"))
4590 u = REBALANCE_WEIGHT_OFF;
4591 else {
4592 r = safe_atou64(optarg, &u);
4593 if (r < 0)
4594 return log_error_errno(r, "Failed to parse --rebalance-weight= argument: %s", optarg);
4595
4596 if (u < REBALANCE_WEIGHT_MIN || u > REBALANCE_WEIGHT_MAX)
4597 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Rebalancing weight out of valid range %" PRIu64 "%s%" PRIu64 ": %s",
4598 REBALANCE_WEIGHT_MIN, glyph(GLYPH_ELLIPSIS), REBALANCE_WEIGHT_MAX, optarg);
4599 }
4600
4601 /* Drop from per machine stuff and everywhere */
4602 r = drop_from_identity("rebalanceWeight");
4603 if (r < 0)
4604 return r;
4605
4606 /* Add to main identity */
4607 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, "rebalanceWeight", u);
4608 if (r < 0)
4609 return log_error_errno(r, "Failed to set rebalanceWeight field: %m");
4610
4611 break;
4612 }
4613
4614 case 'j':
4615 arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
4616 break;
4617
4618 case ARG_JSON:
4619 r = parse_json_argument(optarg, &arg_json_format_flags);
4620 if (r <= 0)
4621 return r;
4622
4623 break;
4624
4625 case 'E':
4626 if (arg_export_format == EXPORT_FORMAT_FULL)
4627 arg_export_format = EXPORT_FORMAT_STRIPPED;
4628 else if (arg_export_format == EXPORT_FORMAT_STRIPPED)
4629 arg_export_format = EXPORT_FORMAT_MINIMAL;
4630 else
4631 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specifying -E more than twice is not supported.");
4632
4633 arg_json_format_flags &= ~SD_JSON_FORMAT_OFF;
4634 if (arg_json_format_flags == 0)
4635 arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
4636 break;
4637
4638 case ARG_EXPORT_FORMAT:
4639 if (streq(optarg, "full"))
4640 arg_export_format = EXPORT_FORMAT_FULL;
4641 else if (streq(optarg, "stripped"))
4642 arg_export_format = EXPORT_FORMAT_STRIPPED;
4643 else if (streq(optarg, "minimal"))
4644 arg_export_format = EXPORT_FORMAT_MINIMAL;
4645 else if (streq(optarg, "help")) {
4646 puts("full\n"
4647 "stripped\n"
4648 "minimal");
4649 return 0;
4650 }
4651
4652 break;
4653
4654 case ARG_AND_RESIZE:
4655 arg_and_resize = true;
4656 break;
4657
4658 case ARG_AND_CHANGE_PASSWORD:
4659 arg_and_change_password = true;
4660 break;
4661
4662 case ARG_DROP_CACHES: {
4663 if (isempty(optarg)) {
4664 r = drop_from_identity("dropCaches");
4665 if (r < 0)
4666 return r;
4667 break;
4668 }
4669
4670 r = parse_boolean_argument("--drop-caches=", optarg, NULL);
4671 if (r < 0)
4672 return r;
4673
4674 r = sd_json_variant_set_field_boolean(match_identity ?: &arg_identity_extra, "dropCaches", r);
4675 if (r < 0)
4676 return log_error_errno(r, "Failed to set drop caches field: %m");
4677
4678 break;
4679 }
4680
4681 case ARG_CAPABILITY_AMBIENT_SET:
4682 case ARG_CAPABILITY_BOUNDING_SET: {
4683 _cleanup_strv_free_ char **l = NULL;
4684 bool subtract = false;
4685 uint64_t parsed, *which, updated;
4686 const char *p, *field;
4687
4688 if (c == ARG_CAPABILITY_AMBIENT_SET) {
4689 which = &arg_capability_ambient_set;
4690 field = "capabilityAmbientSet";
4691 } else {
4692 assert(c == ARG_CAPABILITY_BOUNDING_SET);
4693 which = &arg_capability_bounding_set;
4694 field = "capabilityBoundingSet";
4695 }
4696
4697 if (isempty(optarg)) {
4698 r = drop_from_identity(field);
4699 if (r < 0)
4700 return r;
4701
4702 *which = UINT64_MAX;
4703 break;
4704 }
4705
4706 p = optarg;
4707 if (*p == '~') {
4708 subtract = true;
4709 p++;
4710 }
4711
4712 r = capability_set_from_string(p, &parsed);
4713 if (r == 0)
4714 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid capabilities in capability string '%s'.", p);
4715 if (r < 0)
4716 return log_error_errno(r, "Failed to parse capability string '%s': %m", p);
4717
4718 if (*which == UINT64_MAX)
4719 updated = subtract ? all_capabilities() & ~parsed : parsed;
4720 else if (subtract)
4721 updated = *which & ~parsed;
4722 else
4723 updated = *which | parsed;
4724
4725 if (capability_set_to_strv(updated, &l) < 0)
4726 return log_oom();
4727
4728 r = sd_json_variant_set_field_strv(match_identity ?: &arg_identity_extra, field, l);
4729 if (r < 0)
4730 return log_error_errno(r, "Failed to set %s field: %m", field);
4731
4732 *which = updated;
4733 break;
4734 }
4735
4736 case ARG_PROMPT_NEW_USER:
4737 arg_prompt_new_user = true;
4738 break;
4739
4740 case 'b':
4741 case ARG_AVATAR:
4742 case ARG_LOGIN_BACKGROUND: {
4743 _cleanup_close_ int fd = -EBADF;
4744 _cleanup_free_ char *path = NULL, *filename = NULL;
4745
4746 if (c == 'b') {
4747 char *eq;
4748
4749 if (isempty(optarg)) { /* --blob= deletes everything, including existing blob dirs */
4750 hashmap_clear(arg_blob_files);
4751 arg_blob_dir = mfree(arg_blob_dir);
4752 arg_blob_clear = true;
4753 break;
4754 }
4755
4756 eq = strrchr(optarg, '=');
4757 if (!eq) { /* --blob=/some/path replaces the blob dir */
4758 r = parse_path_argument(optarg, false, &arg_blob_dir);
4759 if (r < 0)
4760 return log_error_errno(r, "Failed to parse path %s: %m", optarg);
4761 break;
4762 }
4763
4764 /* --blob=filename=/some/path replaces the file "filename" with /some/path */
4765 filename = strndup(optarg, eq - optarg);
4766 if (!filename)
4767 return log_oom();
4768
4769 if (isempty(filename))
4770 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't parse blob file assignment: %s", optarg);
4771 if (!suitable_blob_filename(filename))
4772 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid blob filename: %s", filename);
4773
4774 r = parse_path_argument(eq + 1, false, &path);
4775 if (r < 0)
4776 return log_error_errno(r, "Failed to parse path %s: %m", eq + 1);
4777 } else {
4778 const char *well_known_filename =
4779 c == ARG_AVATAR ? "avatar" :
4780 c == ARG_LOGIN_BACKGROUND ? "login-background" :
4781 NULL;
4782 assert(well_known_filename);
4783
4784 filename = strdup(well_known_filename);
4785 if (!filename)
4786 return log_oom();
4787
4788 r = parse_path_argument(optarg, false, &path);
4789 if (r < 0)
4790 return log_error_errno(r, "Failed to parse path %s: %m", optarg);
4791 }
4792
4793 if (path) {
4794 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
4795 if (fd < 0)
4796 return log_error_errno(errno, "Failed to open %s: %m", path);
4797
4798 if (fd_verify_regular(fd) < 0)
4799 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Provided blob is not a regular file: %s", path);
4800 } else
4801 fd = -EBADF; /* Delete the file */
4802
4803 r = hashmap_ensure_put(&arg_blob_files, &blob_fd_hash_ops, filename, FD_TO_PTR(fd));
4804 if (r < 0)
4805 return log_error_errno(r, "Failed to map %s to %s in blob directory: %m", path, filename);
4806 TAKE_PTR(filename); /* hashmap takes ownership */
4807 TAKE_FD(fd);
4808
4809 break;
4810 }
4811
4812 case ARG_TMP_LIMIT:
4813 case ARG_DEV_SHM_LIMIT: {
4814 const char *field =
4815 c == ARG_TMP_LIMIT ? "tmpLimit" :
4816 c == ARG_DEV_SHM_LIMIT ? "devShmLimit" : NULL;
4817 const char *field_scale =
4818 c == ARG_TMP_LIMIT ? "tmpLimitScale" :
4819 c == ARG_DEV_SHM_LIMIT ? "devShmLimitScale" : NULL;
4820
4821 assert(field);
4822 assert(field_scale);
4823
4824 if (isempty(optarg)) {
4825 r = drop_from_identity(field);
4826 if (r < 0)
4827 return r;
4828 r = drop_from_identity(field_scale);
4829 if (r < 0)
4830 return r;
4831 break;
4832 }
4833
4834 r = parse_permyriad(optarg);
4835 if (r < 0) {
4836 uint64_t u;
4837
4838 r = parse_size(optarg, 1024, &u);
4839 if (r < 0)
4840 return log_error_errno(r, "Failed to parse %s/%s parameter: %s", field, field_scale, optarg);
4841
4842 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field, u);
4843 if (r < 0)
4844 return log_error_errno(r, "Failed to set %s field: %m", field);
4845
4846 r = drop_from_identity(field_scale);
4847 if (r < 0)
4848 return r;
4849 } else {
4850 r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field_scale, UINT32_SCALE_FROM_PERMYRIAD(r));
4851 if (r < 0)
4852 return log_error_errno(r, "Failed to set %s field: %m", field_scale);
4853
4854 r = drop_from_identity(field);
4855 if (r < 0)
4856 return r;
4857 }
4858
4859 break;
4860 }
4861
4862 case ARG_DEFAULT_AREA:
4863 if (isempty(optarg)) {
4864 r = drop_from_identity("defaultArea");
4865 if (r < 0)
4866 return r;
4867
4868 break;
4869 }
4870
4871 if (!filename_is_valid(optarg))
4872 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parameter for default area field not valid: %s", optarg);
4873
4874 r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, "defaultArea", optarg);
4875 if (r < 0)
4876 return log_error_errno(r, "Failed to set default area field: %m");
4877
4878 break;
4879
4880 case ARG_KEY_NAME:
4881 if (isempty(optarg)) {
4882 arg_key_name = mfree(arg_key_name);
4883 return 0;
4884 }
4885
4886 if (!filename_is_valid(optarg))
4887 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified key name not valid: %s", optarg);
4888
4889 r = free_and_strdup_warn(&arg_key_name, optarg);
4890 if (r < 0)
4891 return r;
4892
4893 break;
4894
4895 case ARG_SEIZE:
4896 r = parse_boolean_argument("--seize=", optarg, &arg_seize);
4897 if (r < 0)
4898 return r;
4899 break;
4900
4901 case ARG_MATCH:
4902 if (streq(optarg, "any"))
4903 match_identity = &arg_identity_extra;
4904 else if (streq(optarg, "this"))
4905 match_identity = &arg_identity_extra_this_machine;
4906 else if (streq(optarg, "other"))
4907 match_identity = &arg_identity_extra_other_machines;
4908 else if (streq(optarg, "auto"))
4909 match_identity = NULL;
4910 else
4911 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--machine= argument not understood. Refusing.");
4912 break;
4913
4914 case 'A':
4915 match_identity = &arg_identity_extra;
4916 break;
4917 case 'T':
4918 match_identity = &arg_identity_extra_this_machine;
4919 break;
4920 case 'N':
4921 match_identity = &arg_identity_extra_other_machines;
4922 break;
4923
4924 case '?':
4925 return -EINVAL;
4926
4927 default:
4928 assert_not_reached();
4929 }
4930 }
4931
4932 if (!strv_isempty(arg_pkcs11_token_uri) || !strv_isempty(arg_fido2_device))
4933 arg_and_change_password = true;
4934
4935 if (arg_disk_size != UINT64_MAX || arg_disk_size_relative != UINT64_MAX)
4936 arg_and_resize = true;
4937
4938 if (!strv_isempty(arg_languages)) {
4939 char **additional;
4940
4941 r = sd_json_variant_set_field_string(&arg_identity_extra, "preferredLanguage", arg_languages[0]);
4942 if (r < 0)
4943 return log_error_errno(r, "Failed to update preferred language: %m");
4944
4945 additional = strv_skip(arg_languages, 1);
4946 if (!strv_isempty(additional)) {
4947 r = sd_json_variant_set_field_strv(&arg_identity_extra, "additionalLanguages", additional);
4948 if (r < 0)
4949 return log_error_errno(r, "Failed to update additional language list: %m");
4950 } else {
4951 r = drop_from_identity("additionalLanguages");
4952 if (r < 0)
4953 return r;
4954 }
4955 }
4956
4957 return 1;
4958 }
4959
4960 static int redirect_bus_mgr(void) {
4961 const char *suffix;
4962
4963 /* Talk to a different service if that's requested. (The same env var is also understood by homed, so
4964 * that it is relatively easily possible to invoke a second instance of homed for debug purposes and
4965 * have homectl talk to it, without colliding with the host version. This is handy when operating
4966 * from a homed-managed account.) */
4967
4968 suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
4969 if (suffix) {
4970 static BusLocator locator = {
4971 .path = "/org/freedesktop/home1",
4972 .interface = "org.freedesktop.home1.Manager",
4973 };
4974
4975 /* Yes, we leak this memory, but there's little point to collect this, given that we only do
4976 * this in a debug environment, do it only once, and the string shall live for out entire
4977 * process runtime. */
4978
4979 locator.destination = strjoin("org.freedesktop.home1.", suffix);
4980 if (!locator.destination)
4981 return log_oom();
4982
4983 bus_mgr = &locator;
4984 } else
4985 bus_mgr = bus_home_mgr;
4986
4987 return 0;
4988 }
4989
4990 static bool is_fallback_shell(const char *p) {
4991 const char *q;
4992
4993 if (!p)
4994 return false;
4995
4996 if (p[0] == '-') {
4997 /* Skip over login shell dash */
4998 p++;
4999
5000 if (streq(p, "ystemd-home-fallback-shell")) /* maybe the dash was used to override the binary name? */
5001 return true;
5002 }
5003
5004 q = strrchr(p, '/'); /* Skip over path */
5005 if (q)
5006 p = q + 1;
5007
5008 return streq(p, "systemd-home-fallback-shell");
5009 }
5010
5011 static int fallback_shell(int argc, char *argv[]) {
5012 _cleanup_(user_record_unrefp) UserRecord *secret = NULL, *hr = NULL;
5013 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
5014 _cleanup_strv_free_ char **l = NULL;
5015 _cleanup_free_ char *argv0 = NULL;
5016 const char *json, *hd, *shell;
5017 int r, incomplete;
5018
5019 /* So here's the deal: if users log into a system via ssh, and their homed-managed home directory
5020 * wasn't activated yet, SSH will permit the access but the home directory isn't actually available
5021 * yet. SSH doesn't allow us to ask authentication questions from the PAM session stack, and doesn't
5022 * run the PAM authentication stack (because it authenticates via its own key management, after
5023 * all). So here's our way to support this: homectl can be invoked as a multi-call binary under the
5024 * name "systemd-home-fallback-shell". If so, it will chainload a login shell, but first try to
5025 * unlock the home directory of the user it is invoked as. systemd-homed will then override the shell
5026 * listed in user records whose home directory is not activated yet with this pseudo-shell. Net
5027 * effect: one SSH auth succeeds this pseudo shell gets invoked, which will unlock the homedir
5028 * (possibly asking for a passphrase) and then chainload the regular shell. Once the login is
5029 * complete the user record will look like any other. */
5030
5031 r = acquire_bus(&bus);
5032 if (r < 0)
5033 return r;
5034
5035 for (unsigned n_tries = 0;; n_tries++) {
5036 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5037 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
5038 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
5039
5040 if (n_tries >= 5)
5041 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
5042 "Failed to activate home dir, even after %u tries.", n_tries);
5043
5044 /* Let's start by checking if this all is even necessary, i.e. if the useFallback boolean field is actually set. */
5045 r = bus_call_method(bus, bus_mgr, "GetUserRecordByName", &error, &reply, "s", NULL); /* empty user string means: our calling user */
5046 if (r < 0)
5047 return log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
5048
5049 r = sd_bus_message_read(reply, "sbo", &json, NULL, NULL);
5050 if (r < 0)
5051 return bus_log_parse_error(r);
5052
5053 r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, NULL, NULL);
5054 if (r < 0)
5055 return log_error_errno(r, "Failed to parse JSON identity: %m");
5056
5057 hr = user_record_new();
5058 if (!hr)
5059 return log_oom();
5060
5061 r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG|USER_RECORD_PERMISSIVE);
5062 if (r < 0)
5063 return r;
5064
5065 if (!hr->use_fallback) /* Nice! We are done, fallback logic not necessary */
5066 break;
5067
5068 if (!secret) {
5069 r = acquire_passed_secrets(hr->user_name, &secret);
5070 if (r < 0)
5071 return r;
5072 }
5073
5074 for (;;) {
5075 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
5076
5077 r = bus_message_new_method_call(bus, &m, bus_mgr, "ActivateHomeIfReferenced");
5078 if (r < 0)
5079 return bus_log_create_error(r);
5080
5081 r = sd_bus_message_append(m, "s", NULL); /* empty user string means: our calling user */
5082 if (r < 0)
5083 return bus_log_create_error(r);
5084
5085 r = bus_message_append_secret(m, secret);
5086 if (r < 0)
5087 return bus_log_create_error(r);
5088
5089 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
5090 if (r < 0) {
5091 if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_NOT_REFERENCED))
5092 return log_error_errno(r, "Called without reference on home taken, can't operate.");
5093
5094 r = handle_generic_user_record_error(hr->user_name, secret, &error, r, false);
5095 if (r < 0)
5096 return r;
5097
5098 sd_bus_error_free(&error);
5099 } else
5100 break;
5101 }
5102
5103 /* Try again */
5104 hr = user_record_unref(hr);
5105 }
5106
5107 incomplete = getenv_bool("XDG_SESSION_INCOMPLETE"); /* pam_systemd_home reports this state via an environment variable to us. */
5108 if (incomplete < 0 && incomplete != -ENXIO)
5109 return log_error_errno(incomplete, "Failed to parse $XDG_SESSION_INCOMPLETE environment variable: %m");
5110 if (incomplete > 0) {
5111 /* We are still in an "incomplete" session here. Now upgrade it to a full one. This will make logind
5112 * start the user@.service instance for us. */
5113 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5114 r = sd_bus_call_method(
5115 bus,
5116 "org.freedesktop.login1",
5117 "/org/freedesktop/login1/session/self",
5118 "org.freedesktop.login1.Session",
5119 "SetClass",
5120 &error,
5121 /* ret_reply= */ NULL,
5122 "s",
5123 "user");
5124 if (r < 0)
5125 return log_error_errno(r, "Failed to upgrade session: %s", bus_error_message(&error, r));
5126
5127 if (setenv("XDG_SESSION_CLASS", "user", /* overwrite= */ true) < 0) /* Update the XDG_SESSION_CLASS environment variable to match the above */
5128 return log_error_errno(errno, "Failed to set $XDG_SESSION_CLASS: %m");
5129
5130 if (unsetenv("XDG_SESSION_INCOMPLETE") < 0) /* Unset the 'incomplete' env var */
5131 return log_error_errno(errno, "Failed to unset $XDG_SESSION_INCOMPLETE: %m");
5132 }
5133
5134 /* We are going to invoke execv() soon. Let's be extra accurate and flush/close our bus connection
5135 * first, just to make sure anything queued is flushed out (though there shouldn't be anything) */
5136 bus = sd_bus_flush_close_unref(bus);
5137
5138 assert(!hr->use_fallback);
5139 assert_se(shell = user_record_shell(hr));
5140 assert_se(hd = user_record_home_directory(hr));
5141
5142 /* Extra protection: avoid loops */
5143 if (is_fallback_shell(shell))
5144 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Primary shell of '%s' is fallback shell, refusing loop.", hr->user_name);
5145
5146 if (chdir(hd) < 0)
5147 return log_error_errno(errno, "Failed to change directory to home directory '%s': %m", hd);
5148
5149 if (setenv("SHELL", shell, /* overwrite= */ true) < 0)
5150 return log_error_errno(errno, "Failed to set $SHELL: %m");
5151
5152 if (setenv("HOME", hd, /* overwrite= */ true) < 0)
5153 return log_error_errno(errno, "Failed to set $HOME: %m");
5154
5155 /* Paranoia: in case the client passed some passwords to us to help us unlock, unlock things now */
5156 FOREACH_STRING(ue, "PASSWORD", "NEWPASSWORD", "PIN")
5157 if (unsetenv(ue) < 0)
5158 return log_error_errno(errno, "Failed to unset $%s: %m", ue);
5159
5160 r = path_extract_filename(shell, &argv0);
5161 if (r < 0)
5162 return log_error_errno(r, "Unable to extract file name from '%s': %m", shell);
5163 if (r == O_DIRECTORY)
5164 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Shell '%s' is a path to a directory, refusing.", shell);
5165
5166 /* Invoke this as login shell, by setting argv[0][0] to '-' (unless we ourselves weren't called as login shell) */
5167 if (!argv || isempty(argv[0]) || argv[0][0] == '-') {
5168 _cleanup_free_ char *prefixed = strjoin("-", argv0);
5169 if (!prefixed)
5170 return log_oom();
5171
5172 free_and_replace(argv0, prefixed);
5173 }
5174
5175 l = strv_new(argv0);
5176 if (!l)
5177 return log_oom();
5178
5179 if (strv_extend_strv(&l, strv_skip(argv, 1), /* filter_duplicates= */ false) < 0)
5180 return log_oom();
5181
5182 execv(shell, l);
5183 return log_error_errno(errno, "Failed to execute shell '%s': %m", shell);
5184 }
5185
5186 static int verb_list_signing_keys(int argc, char *argv[], void *userdata) {
5187 int r;
5188
5189 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
5190 r = acquire_bus(&bus);
5191 if (r < 0)
5192 return r;
5193
5194 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5195 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
5196 r = bus_call_method(bus, bus_mgr, "ListSigningKeys", &error, &reply, NULL);
5197 if (r < 0)
5198 return log_error_errno(r, "Failed to list signing keys: %s", bus_error_message(&error, r));
5199
5200 _cleanup_(table_unrefp) Table *table = table_new("name", "key");
5201 if (!table)
5202 return log_oom();
5203
5204 r = sd_bus_message_enter_container(reply, 'a', "(sst)");
5205 if (r < 0)
5206 return bus_log_parse_error(r);
5207
5208 for (;;) {
5209 const char *name, *pem;
5210
5211 r = sd_bus_message_read(reply, "(sst)", &name, &pem, NULL);
5212 if (r < 0)
5213 return bus_log_parse_error(r);
5214 if (r == 0)
5215 break;
5216
5217 _cleanup_free_ char *h = NULL;
5218 if (!sd_json_format_enabled(arg_json_format_flags)) {
5219 /* Let's decode the PEM key to DER (so that we lose prefix/suffix), then truncate it
5220 * for display reasons. */
5221
5222 _cleanup_(EVP_PKEY_freep) EVP_PKEY *key = NULL;
5223 r = openssl_pubkey_from_pem(pem, SIZE_MAX, &key);
5224 if (r < 0)
5225 return log_error_errno(r, "Failed to parse PEM: %m");
5226
5227 _cleanup_free_ void *der = NULL;
5228 int n = i2d_PUBKEY(key, (unsigned char**) &der);
5229 if (n < 0)
5230 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to encode key as DER: %m");
5231
5232 ssize_t m = base64mem(der, MIN(n, 64), &h);
5233 if (m < 0)
5234 return log_oom();
5235 if (n > 64) /* check if we truncated the original version */
5236 if (!strextend(&h, glyph(GLYPH_ELLIPSIS)))
5237 return log_oom();
5238 }
5239
5240 r = table_add_many(
5241 table,
5242 TABLE_STRING, name,
5243 TABLE_STRING, h ?: pem);
5244 if (r < 0)
5245 return table_log_add_error(r);
5246 }
5247
5248 r = sd_bus_message_exit_container(reply);
5249 if (r < 0)
5250 return bus_log_parse_error(r);
5251
5252 if (!table_isempty(table) || sd_json_format_enabled(arg_json_format_flags)) {
5253 r = table_set_sort(table, (size_t) 0);
5254 if (r < 0)
5255 return table_log_sort_error(r);
5256
5257 r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
5258 if (r < 0)
5259 return r;
5260 }
5261
5262 if (arg_legend && !sd_json_format_enabled(arg_json_format_flags)) {
5263 if (table_isempty(table))
5264 printf("No signing keys.\n");
5265 else
5266 printf("\n%zu signing keys listed.\n", table_get_rows(table) - 1);
5267 }
5268
5269 return 0;
5270 }
5271
5272 static int verb_get_signing_key(int argc, char *argv[], void *userdata) {
5273 int r;
5274
5275 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
5276 r = acquire_bus(&bus);
5277 if (r < 0)
5278 return r;
5279
5280 char **keys = argc >= 2 ? strv_skip(argv, 1) : STRV_MAKE("local.public");
5281 int ret = 0;
5282 STRV_FOREACH(k, keys) {
5283 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5284 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
5285 r = bus_call_method(bus, bus_mgr, "GetSigningKey", &error, &reply, "s", *k);
5286 if (r < 0) {
5287 RET_GATHER(ret, log_error_errno(r, "Failed to get signing key '%s': %s", *k, bus_error_message(&error, r)));
5288 continue;
5289 }
5290
5291 const char *pem;
5292 r = sd_bus_message_read(reply, "st", &pem, NULL);
5293 if (r < 0) {
5294 RET_GATHER(ret, bus_log_parse_error(r));
5295 continue;
5296 }
5297
5298 fputs(pem, stdout);
5299 if (!endswith(pem, "\n"))
5300 fputc('\n', stdout);
5301
5302 fflush(stdout);
5303 }
5304
5305 return ret;
5306 }
5307
5308 static int add_signing_key_one(sd_bus *bus, const char *fn, FILE *key) {
5309 int r;
5310
5311 assert_se(bus);
5312 assert_se(fn);
5313 assert_se(key);
5314
5315 _cleanup_free_ char *pem = NULL;
5316 r = read_full_stream(key, &pem, /* ret_size= */ NULL);
5317 if (r < 0)
5318 return log_error_errno(r, "Failed to read key '%s': %m", fn);
5319
5320 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5321 r = bus_call_method(bus, bus_mgr, "AddSigningKey", &error, /* ret_reply= */ NULL, "sst", fn, pem, UINT64_C(0));
5322 if (r < 0)
5323 return log_error_errno(r, "Failed to add signing key '%s': %s", fn, bus_error_message(&error, r));
5324
5325 return 0;
5326 }
5327
5328 static int verb_add_signing_key(int argc, char *argv[], void *userdata) {
5329 int r;
5330
5331 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
5332 r = acquire_bus(&bus);
5333 if (r < 0)
5334 return r;
5335
5336 int ret = EXIT_SUCCESS;
5337 if (argc < 2 || streq(argv[1], "-")) {
5338 if (!arg_key_name)
5339 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key name must be specified via --key-name= when reading key from standard input, refusing.");
5340
5341 RET_GATHER(ret, add_signing_key_one(bus, arg_key_name, stdin));
5342 } else {
5343 /* Refuse if more han one key is specified in combination with --key-name= */
5344 if (argc >= 3 && arg_key_name)
5345 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--key-name= is not supported if multiple signing keys are specified, refusing.");
5346
5347 STRV_FOREACH(k, strv_skip(argv, 1)) {
5348
5349 if (streq(*k, "-"))
5350 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Refusing to read from standard input if multiple keys are specified.");
5351
5352 _cleanup_free_ char *fn = NULL;
5353 if (!arg_key_name) {
5354 r = path_extract_filename(*k, &fn);
5355 if (r < 0) {
5356 RET_GATHER(ret, log_error_errno(r, "Failed to extract filename from path '%s': %m", *k));
5357 continue;
5358 }
5359 }
5360
5361 _cleanup_fclose_ FILE *f = fopen(*k, "re");
5362 if (!f) {
5363 RET_GATHER(ret, log_error_errno(errno, "Failed to open '%s': %m", *k));
5364 continue;
5365 }
5366
5367 RET_GATHER(ret, add_signing_key_one(bus, fn ?: arg_key_name, f));
5368 }
5369 }
5370
5371 return ret;
5372 }
5373
5374 static int add_signing_keys_from_credentials(void) {
5375 int r;
5376
5377 _cleanup_close_ int fd = open_credentials_dir();
5378 if (IN_SET(fd, -ENXIO, -ENOENT)) /* Credential env var not set, or dir doesn't exist. */
5379 return 0;
5380 if (fd < 0)
5381 return log_error_errno(fd, "Failed to open credentials directory: %m");
5382
5383 _cleanup_free_ DirectoryEntries *des = NULL;
5384 r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
5385 if (r < 0)
5386 return log_error_errno(r, "Failed to enumerate credentials: %m");
5387
5388 int ret = 0;
5389 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
5390 FOREACH_ARRAY(i, des->entries, des->n_entries) {
5391 struct dirent *de = *i;
5392 if (de->d_type != DT_REG)
5393 continue;
5394
5395 const char *e = startswith(de->d_name, "home.add-signing-key.");
5396 if (!e)
5397 continue;
5398
5399 if (!filename_is_valid(e))
5400 continue;
5401
5402 if (!bus) {
5403 r = acquire_bus(&bus);
5404 if (r < 0)
5405 return r;
5406 }
5407
5408 _cleanup_fclose_ FILE *f = NULL;
5409 r = xfopenat(fd, de->d_name, "re", O_NOFOLLOW, &f);
5410 if (r < 0) {
5411 RET_GATHER(ret, log_error_errno(r, "Failed to open credential '%s': %m", de->d_name));
5412 continue;
5413 }
5414
5415 RET_GATHER(ret, add_signing_key_one(bus, e, f));
5416 }
5417
5418 return ret;
5419 }
5420
5421 static int remove_signing_key_one(sd_bus *bus, const char *fn) {
5422 int r;
5423
5424 assert_se(bus);
5425 assert_se(fn);
5426
5427 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5428 r = bus_call_method(bus, bus_mgr, "RemoveSigningKey", &error, /* ret_reply= */ NULL, "st", fn, UINT64_C(0));
5429 if (r < 0)
5430 return log_error_errno(r, "Failed to remove signing key '%s': %s", fn, bus_error_message(&error, r));
5431
5432 return 0;
5433 }
5434
5435 static int verb_remove_signing_key(int argc, char *argv[], void *userdata) {
5436 int r;
5437
5438 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
5439 r = acquire_bus(&bus);
5440 if (r < 0)
5441 return r;
5442
5443 r = EXIT_SUCCESS;
5444 STRV_FOREACH(k, strv_skip(argv, 1))
5445 RET_GATHER(r, remove_signing_key_one(bus, *k));
5446
5447 return r;
5448 }
5449
5450 static int run(int argc, char *argv[]) {
5451 static const Verb verbs[] = {
5452 { "help", VERB_ANY, VERB_ANY, 0, help },
5453 { "list", VERB_ANY, 1, VERB_DEFAULT, list_homes },
5454 { "activate", 2, VERB_ANY, 0, activate_home },
5455 { "deactivate", 2, VERB_ANY, 0, deactivate_home },
5456 { "inspect", VERB_ANY, VERB_ANY, 0, inspect_homes },
5457 { "authenticate", VERB_ANY, VERB_ANY, 0, authenticate_homes },
5458 { "create", VERB_ANY, 2, 0, create_home },
5459 { "adopt", VERB_ANY, VERB_ANY, 0, verb_adopt_home },
5460 { "register", VERB_ANY, VERB_ANY, 0, verb_register_home },
5461 { "unregister", 2, VERB_ANY, 0, verb_unregister_home },
5462 { "remove", 2, VERB_ANY, 0, remove_home },
5463 { "update", VERB_ANY, 2, 0, update_home },
5464 { "passwd", VERB_ANY, 2, 0, passwd_home },
5465 { "resize", 2, 3, 0, resize_home },
5466 { "lock", 2, VERB_ANY, 0, lock_home },
5467 { "unlock", 2, VERB_ANY, 0, unlock_home },
5468 { "with", 2, VERB_ANY, 0, with_home },
5469 { "lock-all", VERB_ANY, 1, 0, lock_all_homes },
5470 { "deactivate-all", VERB_ANY, 1, 0, deactivate_all_homes },
5471 { "rebalance", VERB_ANY, 1, 0, rebalance },
5472 { "firstboot", VERB_ANY, 1, 0, verb_firstboot },
5473 { "list-signing-keys", VERB_ANY, 1, 0, verb_list_signing_keys },
5474 { "get-signing-key", VERB_ANY, VERB_ANY, 0, verb_get_signing_key },
5475 { "add-signing-key", VERB_ANY, VERB_ANY, 0, verb_add_signing_key },
5476 { "remove-signing-key", 2, VERB_ANY, 0, verb_remove_signing_key },
5477 {}
5478 };
5479
5480 int r;
5481
5482 log_setup();
5483
5484 r = redirect_bus_mgr();
5485 if (r < 0)
5486 return r;
5487
5488 if (is_fallback_shell(argv[0]))
5489 return fallback_shell(argc, argv);
5490
5491 r = parse_argv(argc, argv);
5492 if (r <= 0)
5493 return r;
5494
5495 return dispatch_verb(argc, argv, verbs, NULL);
5496 }
5497
5498 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);