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