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