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