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