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