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