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