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