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