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