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