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