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