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