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