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