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