1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "ask-password-api.h"
9 #include "bus-common-errors.h"
10 #include "bus-error.h"
11 #include "bus-locator.h"
13 #include "capability-util.h"
14 #include "cgroup-util.h"
15 #include "creds-util.h"
16 #include "dns-domain.h"
20 #include "format-table.h"
22 #include "glyph-util.h"
23 #include "home-util.h"
24 #include "homectl-fido2.h"
25 #include "homectl-pkcs11.h"
26 #include "homectl-recovery-key.h"
27 #include "libfido2-util.h"
28 #include "locale-util.h"
29 #include "main-func.h"
30 #include "memory-util.h"
32 #include "parse-argument.h"
33 #include "parse-util.h"
34 #include "password-quality-util.h"
35 #include "path-util.h"
36 #include "percent-util.h"
37 #include "pkcs11-util.h"
38 #include "pretty-print.h"
39 #include "proc-cmdline.h"
40 #include "process-util.h"
41 #include "recurse-dir.h"
42 #include "rlimit-util.h"
43 #include "spawn-polkit-agent.h"
44 #include "terminal-util.h"
45 #include "uid-alloc-range.h"
46 #include "user-record.h"
47 #include "user-record-password-quality.h"
48 #include "user-record-show.h"
49 #include "user-record-util.h"
50 #include "user-util.h"
54 static PagerFlags arg_pager_flags
= 0;
55 static bool arg_legend
= true;
56 static bool arg_ask_password
= true;
57 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
58 static const char *arg_host
= NULL
;
59 static const char *arg_identity
= NULL
;
60 static JsonVariant
*arg_identity_extra
= NULL
;
61 static JsonVariant
*arg_identity_extra_privileged
= NULL
;
62 static JsonVariant
*arg_identity_extra_this_machine
= NULL
;
63 static JsonVariant
*arg_identity_extra_rlimits
= NULL
;
64 static char **arg_identity_filter
= NULL
; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
65 static char **arg_identity_filter_rlimits
= NULL
;
66 static uint64_t arg_disk_size
= UINT64_MAX
;
67 static uint64_t arg_disk_size_relative
= UINT64_MAX
;
68 static char **arg_pkcs11_token_uri
= NULL
;
69 static char **arg_fido2_device
= NULL
;
70 static Fido2EnrollFlags arg_fido2_lock_with
= FIDO2ENROLL_PIN
| FIDO2ENROLL_UP
;
72 static int arg_fido2_cred_alg
= COSE_ES256
;
74 static int arg_fido2_cred_alg
= 0;
76 static bool arg_recovery_key
= false;
77 static JsonFormatFlags arg_json_format_flags
= JSON_FORMAT_OFF
;
78 static bool arg_and_resize
= false;
79 static bool arg_and_change_password
= false;
81 EXPORT_FORMAT_FULL
, /* export the full record */
82 EXPORT_FORMAT_STRIPPED
, /* strip "state" + "binding", but leave signature in place */
83 EXPORT_FORMAT_MINIMAL
, /* also strip signature */
84 } arg_export_format
= EXPORT_FORMAT_FULL
;
85 static uint64_t arg_capability_bounding_set
= UINT64_MAX
;
86 static uint64_t arg_capability_ambient_set
= UINT64_MAX
;
87 static bool arg_prompt_new_user
= false;
89 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra
, json_variant_unrefp
);
90 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine
, json_variant_unrefp
);
91 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged
, json_variant_unrefp
);
92 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits
, json_variant_unrefp
);
93 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter
, strv_freep
);
94 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits
, strv_freep
);
95 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri
, strv_freep
);
96 STATIC_DESTRUCTOR_REGISTER(arg_fido2_device
, strv_freep
);
98 static const BusLocator
*bus_mgr
;
100 static bool identity_properties_specified(void) {
103 !json_variant_is_blank_object(arg_identity_extra
) ||
104 !json_variant_is_blank_object(arg_identity_extra_privileged
) ||
105 !json_variant_is_blank_object(arg_identity_extra_this_machine
) ||
106 !json_variant_is_blank_object(arg_identity_extra_rlimits
) ||
107 !strv_isempty(arg_identity_filter
) ||
108 !strv_isempty(arg_identity_filter_rlimits
) ||
109 !strv_isempty(arg_pkcs11_token_uri
) ||
110 !strv_isempty(arg_fido2_device
);
113 static int acquire_bus(sd_bus
**bus
) {
121 r
= bus_connect_transport(arg_transport
, arg_host
, RUNTIME_SCOPE_SYSTEM
, bus
);
123 return bus_log_connect_error(r
, arg_transport
);
125 (void) sd_bus_set_allow_interactive_authorization(*bus
, arg_ask_password
);
130 static int list_homes(int argc
, char *argv
[], void *userdata
) {
131 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
132 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
133 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
134 _cleanup_(table_unrefp
) Table
*table
= NULL
;
137 r
= acquire_bus(&bus
);
141 r
= bus_call_method(bus
, bus_mgr
, "ListHomes", &error
, &reply
, NULL
);
143 return log_error_errno(r
, "Failed to list homes: %s", bus_error_message(&error
, r
));
145 table
= table_new("name", "uid", "gid", "state", "realname", "home", "shell");
149 r
= sd_bus_message_enter_container(reply
, 'a', "(susussso)");
151 return bus_log_parse_error(r
);
154 const char *name
, *state
, *realname
, *home
, *shell
, *color
;
158 r
= sd_bus_message_read(reply
, "(susussso)", &name
, &uid
, &state
, &gid
, &realname
, &home
, &shell
, NULL
);
160 return bus_log_parse_error(r
);
164 r
= table_add_many(table
,
169 return table_log_add_error(r
);
172 r
= table_add_cell(table
, &cell
, TABLE_STRING
, state
);
174 return table_log_add_error(r
);
176 color
= user_record_state_color(state
);
178 (void) table_set_color(table
, cell
, color
);
180 r
= table_add_many(table
,
181 TABLE_STRING
, strna(empty_to_null(realname
)),
183 TABLE_STRING
, strna(empty_to_null(shell
)));
185 return table_log_add_error(r
);
188 r
= sd_bus_message_exit_container(reply
);
190 return bus_log_parse_error(r
);
192 if (table_get_rows(table
) > 1 || !FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
193 r
= table_set_sort(table
, (size_t) 0);
195 return table_log_sort_error(r
);
197 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, arg_legend
);
202 if (arg_legend
&& (arg_json_format_flags
& JSON_FORMAT_OFF
)) {
203 if (table_get_rows(table
) > 1)
204 printf("\n%zu home areas listed.\n", table_get_rows(table
) - 1);
206 printf("No home areas.\n");
212 static int acquire_existing_password(
213 const char *user_name
,
215 bool emphasize_current
,
216 AskPasswordFlags flags
) {
218 _cleanup_strv_free_erase_
char **password
= NULL
;
219 _cleanup_(erase_and_freep
) char *envpw
= NULL
;
220 _cleanup_free_
char *question
= NULL
;
226 r
= getenv_steal_erase("PASSWORD", &envpw
);
228 return log_error_errno(r
, "Failed to acquire password from environment: %m");
230 /* People really shouldn't use environment variables for passing passwords. We support this
231 * only for testing purposes, and do not document the behaviour, so that people won't
232 * actually use this outside of testing. */
234 r
= user_record_set_password(hr
, STRV_MAKE(envpw
), true);
236 return log_error_errno(r
, "Failed to store password: %m");
241 /* If this is not our own user, then don't use the password cache */
242 if (is_this_me(user_name
) <= 0)
243 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
245 if (asprintf(&question
, emphasize_current
?
246 "Please enter current password for user %s:" :
247 "Please enter password for user %s:",
251 r
= ask_password_auto(question
,
252 /* icon= */ "user-home",
254 /* key_name= */ "home-password",
255 /* credential_name= */ "home.password",
259 if (r
== -EUNATCH
) { /* EUNATCH is returned if no password was found and asking interactively was
260 * disabled via the flags. Not an error for us. */
261 log_debug_errno(r
, "No passwords acquired.");
265 return log_error_errno(r
, "Failed to acquire password: %m");
267 r
= user_record_set_password(hr
, password
, true);
269 return log_error_errno(r
, "Failed to store password: %m");
274 static int acquire_recovery_key(
275 const char *user_name
,
277 AskPasswordFlags flags
) {
279 _cleanup_strv_free_erase_
char **recovery_key
= NULL
;
280 _cleanup_(erase_and_freep
) char *envpw
= NULL
;
281 _cleanup_free_
char *question
= NULL
;
287 r
= getenv_steal_erase("PASSWORD", &envpw
);
289 return log_error_errno(r
, "Failed to acquire password from environment: %m");
291 /* People really shouldn't use environment variables for passing secrets. We support this
292 * only for testing purposes, and do not document the behaviour, so that people won't
293 * actually use this outside of testing. */
295 r
= user_record_set_password(hr
, STRV_MAKE(envpw
), true); /* recovery keys are stored in the record exactly like regular passwords! */
297 return log_error_errno(r
, "Failed to store recovery key: %m");
302 /* If this is not our own user, then don't use the password cache */
303 if (is_this_me(user_name
) <= 0)
304 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
306 if (asprintf(&question
, "Please enter recovery key for user %s:", user_name
) < 0)
309 r
= ask_password_auto(question
,
310 /* icon= */ "user-home",
312 /* key_name= */ "home-recovery-key",
313 /* credential_name= */ "home.recovery-key",
317 if (r
== -EUNATCH
) { /* EUNATCH is returned if no recovery key was found and asking interactively was
318 * disabled via the flags. Not an error for us. */
319 log_debug_errno(r
, "No recovery keys acquired.");
323 return log_error_errno(r
, "Failed to acquire recovery keys: %m");
325 r
= user_record_set_password(hr
, recovery_key
, true);
327 return log_error_errno(r
, "Failed to store recovery keys: %m");
332 static int acquire_token_pin(
333 const char *user_name
,
335 AskPasswordFlags flags
) {
337 _cleanup_strv_free_erase_
char **pin
= NULL
;
338 _cleanup_(erase_and_freep
) char *envpin
= NULL
;
339 _cleanup_free_
char *question
= NULL
;
345 r
= getenv_steal_erase("PIN", &envpin
);
347 return log_error_errno(r
, "Failed to acquire PIN from environment: %m");
349 r
= user_record_set_token_pin(hr
, STRV_MAKE(envpin
), false);
351 return log_error_errno(r
, "Failed to store token PIN: %m");
356 /* If this is not our own user, then don't use the password cache */
357 if (is_this_me(user_name
) <= 0)
358 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
360 if (asprintf(&question
, "Please enter security token PIN for user %s:", user_name
) < 0)
363 r
= ask_password_auto(
365 /* icon= */ "user-home",
367 /* key_name= */ "token-pin",
368 /* credential_name= */ "home.token-pin",
372 if (r
== -EUNATCH
) { /* EUNATCH is returned if no PIN was found and asking interactively was disabled
373 * via the flags. Not an error for us. */
374 log_debug_errno(r
, "No security token PINs acquired.");
378 return log_error_errno(r
, "Failed to acquire security token PIN: %m");
380 r
= user_record_set_token_pin(hr
, pin
, false);
382 return log_error_errno(r
, "Failed to store security token PIN: %m");
387 static int handle_generic_user_record_error(
388 const char *user_name
,
390 const sd_bus_error
*error
,
392 bool emphasize_current_password
) {
398 if (sd_bus_error_has_name(error
, BUS_ERROR_HOME_ABSENT
))
399 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE
),
400 "Home of user %s is currently absent, please plug in the necessary storage device or backing file system.", user_name
);
402 else if (sd_bus_error_has_name(error
, BUS_ERROR_AUTHENTICATION_LIMIT_HIT
))
403 return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS
),
404 "Too frequent login attempts for user %s, try again later.", user_name
);
406 else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD
)) {
408 if (!strv_isempty(hr
->password
))
409 log_notice("Password incorrect or not sufficient, please try again.");
411 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
412 * let's push what we acquire here into the cache */
413 r
= acquire_existing_password(
416 emphasize_current_password
,
417 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
421 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_RECOVERY_KEY
)) {
423 if (!strv_isempty(hr
->password
))
424 log_notice("Recovery key incorrect or not sufficient, please try again.");
426 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
427 * let's push what we acquire here into the cache */
428 r
= acquire_recovery_key(
431 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
435 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
)) {
437 if (strv_isempty(hr
->password
))
438 log_notice("Security token not inserted, please enter password.");
440 log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
442 r
= acquire_existing_password(
445 emphasize_current_password
,
446 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
450 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_NEEDED
)) {
452 /* First time the PIN is requested, let's accept cached data, and allow using credential store */
453 r
= acquire_token_pin(
456 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_PUSH_CACHE
);
460 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED
)) {
462 log_notice("%s%sPlease authenticate physically on security token.",
463 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
464 emoji_enabled() ? " " : "");
466 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, true);
468 return log_error_errno(r
, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
470 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED
)) {
472 log_notice("%s%sPlease confirm presence on security token.",
473 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
474 emoji_enabled() ? " " : "");
476 r
= user_record_set_fido2_user_presence_permitted(hr
, true);
478 return log_error_errno(r
, "Failed to set FIDO2 user presence permitted flag: %m");
480 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED
)) {
482 log_notice("%s%sPlease verify user on security token.",
483 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
484 emoji_enabled() ? " " : "");
486 r
= user_record_set_fido2_user_verification_permitted(hr
, true);
488 return log_error_errno(r
, "Failed to set FIDO2 user verification permitted flag: %m");
490 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_LOCKED
))
491 return log_error_errno(SYNTHETIC_ERRNO(EPERM
), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
493 else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN
)) {
495 log_notice("Security token PIN incorrect, please try again.");
497 /* If the previous PIN was wrong don't accept cached info anymore, but add to cache. Also, don't use the credential data */
498 r
= acquire_token_pin(
501 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
505 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT
)) {
507 log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
509 r
= acquire_token_pin(
512 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
516 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT
)) {
518 log_notice("Security token PIN incorrect, please try again (only one try left!).");
520 r
= acquire_token_pin(
523 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
527 return log_error_errno(ret
, "Operation on home %s failed: %s", user_name
, bus_error_message(error
, ret
));
532 static int acquire_passed_secrets(const char *user_name
, UserRecord
**ret
) {
533 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
538 /* Generates an initial secret objects that contains passwords supplied via $PASSWORD, the password
539 * cache or the credentials subsystem, but excluding any interactive stuff. If nothing is passed,
540 * returns an empty secret object. */
542 secret
= user_record_new();
546 r
= acquire_existing_password(
549 /* emphasize_current_password = */ false,
550 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
554 r
= acquire_token_pin(
557 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
561 r
= acquire_recovery_key(
564 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
568 *ret
= TAKE_PTR(secret
);
572 static int activate_home(int argc
, char *argv
[], void *userdata
) {
573 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
576 r
= acquire_bus(&bus
);
580 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
581 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
583 r
= acquire_passed_secrets(*i
, &secret
);
588 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
589 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
591 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ActivateHome");
593 return bus_log_create_error(r
);
595 r
= sd_bus_message_append(m
, "s", *i
);
597 return bus_log_create_error(r
);
599 r
= bus_message_append_secret(m
, secret
);
601 return bus_log_create_error(r
);
603 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
605 r
= handle_generic_user_record_error(*i
, secret
, &error
, r
, /* emphasize_current_password= */ false);
620 static int deactivate_home(int argc
, char *argv
[], void *userdata
) {
621 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
624 r
= acquire_bus(&bus
);
628 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
629 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
630 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
632 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "DeactivateHome");
634 return bus_log_create_error(r
);
636 r
= sd_bus_message_append(m
, "s", *i
);
638 return bus_log_create_error(r
);
640 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
642 log_error_errno(r
, "Failed to deactivate user home: %s", bus_error_message(&error
, r
));
651 static void dump_home_record(UserRecord
*hr
) {
656 if (hr
->incomplete
) {
658 log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr
->user_name
);
661 if (arg_json_format_flags
& JSON_FORMAT_OFF
)
662 user_record_show(hr
, true);
664 _cleanup_(user_record_unrefp
) UserRecord
*stripped
= NULL
;
666 if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
667 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_EMBEDDED
|USER_RECORD_PERMISSIVE
, &stripped
);
668 else if (arg_export_format
== EXPORT_FORMAT_MINIMAL
)
669 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_SIGNABLE
|USER_RECORD_PERMISSIVE
, &stripped
);
673 log_warning_errno(r
, "Failed to strip user record, ignoring: %m");
677 json_variant_dump(hr
->json
, arg_json_format_flags
, stdout
, NULL
);
681 static char **mangle_user_list(char **list
, char ***ret_allocated
) {
682 _cleanup_free_
char *myself
= NULL
;
685 if (!strv_isempty(list
)) {
686 *ret_allocated
= NULL
;
690 myself
= getusername_malloc();
698 l
[0] = TAKE_PTR(myself
);
705 static int inspect_home(int argc
, char *argv
[], void *userdata
) {
706 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
707 _cleanup_strv_free_
char **mangled_list
= NULL
;
711 pager_open(arg_pager_flags
);
713 r
= acquire_bus(&bus
);
717 items
= mangle_user_list(strv_skip(argv
, 1), &mangled_list
);
721 STRV_FOREACH(i
, items
) {
722 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
723 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
724 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
725 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
730 r
= parse_uid(*i
, &uid
);
732 if (!valid_user_group_name(*i
, 0)) {
733 log_error("Invalid user name '%s'.", *i
);
740 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByName", &error
, &reply
, "s", *i
);
742 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByUID", &error
, &reply
, "u", (uint32_t) uid
);
745 log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
752 r
= sd_bus_message_read(reply
, "sbo", &json
, &incomplete
, NULL
);
754 bus_log_parse_error(r
);
761 r
= json_parse(json
, JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
763 log_error_errno(r
, "Failed to parse JSON identity: %m");
770 hr
= user_record_new();
774 r
= user_record_load(hr
, v
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_LOG
|USER_RECORD_PERMISSIVE
);
782 hr
->incomplete
= incomplete
;
783 dump_home_record(hr
);
789 static int authenticate_home(int argc
, char *argv
[], void *userdata
) {
790 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
791 _cleanup_strv_free_
char **mangled_list
= NULL
;
795 items
= mangle_user_list(strv_skip(argv
, 1), &mangled_list
);
799 r
= acquire_bus(&bus
);
803 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
805 STRV_FOREACH(i
, items
) {
806 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
808 r
= acquire_passed_secrets(*i
, &secret
);
813 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
814 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
816 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "AuthenticateHome");
818 return bus_log_create_error(r
);
820 r
= sd_bus_message_append(m
, "s", *i
);
822 return bus_log_create_error(r
);
824 r
= bus_message_append_secret(m
, secret
);
826 return bus_log_create_error(r
);
828 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
830 r
= handle_generic_user_record_error(*i
, secret
, &error
, r
, false);
845 static int update_last_change(JsonVariant
**v
, bool with_password
, bool override
) {
852 n
= now(CLOCK_REALTIME
);
854 c
= json_variant_by_key(*v
, "lastChangeUSec");
859 goto update_password
;
861 if (!json_variant_is_unsigned(c
))
862 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec field is not an unsigned integer, refusing.");
864 u
= json_variant_unsigned(c
);
866 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec is from the future, can't update.");
869 r
= json_variant_set_field_unsigned(v
, "lastChangeUSec", n
);
871 return log_error_errno(r
, "Failed to update lastChangeUSec: %m");
877 c
= json_variant_by_key(*v
, "lastPasswordChangeUSec");
884 if (!json_variant_is_unsigned(c
))
885 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec field is not an unsigned integer, refusing.");
887 u
= json_variant_unsigned(c
);
889 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec is from the future, can't update.");
892 r
= json_variant_set_field_unsigned(v
, "lastPasswordChangeUSec", n
);
894 return log_error_errno(r
, "Failed to update lastPasswordChangeUSec: %m");
899 static int apply_identity_changes(JsonVariant
**_v
) {
900 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
905 v
= json_variant_ref(*_v
);
907 r
= json_variant_filter(&v
, arg_identity_filter
);
909 return log_error_errno(r
, "Failed to filter identity: %m");
911 r
= json_variant_merge_object(&v
, arg_identity_extra
);
913 return log_error_errno(r
, "Failed to merge identities: %m");
915 if (arg_identity_extra_this_machine
|| !strv_isempty(arg_identity_filter
)) {
916 _cleanup_(json_variant_unrefp
) JsonVariant
*per_machine
= NULL
, *mmid
= NULL
;
919 r
= sd_id128_get_machine(&mid
);
921 return log_error_errno(r
, "Failed to acquire machine ID: %m");
923 r
= json_variant_new_string(&mmid
, SD_ID128_TO_STRING(mid
));
925 return log_error_errno(r
, "Failed to allocate matchMachineId object: %m");
927 per_machine
= json_variant_ref(json_variant_by_key(v
, "perMachine"));
929 _cleanup_(json_variant_unrefp
) JsonVariant
*npm
= NULL
, *add
= NULL
;
930 _cleanup_free_ JsonVariant
**array
= NULL
;
934 if (!json_variant_is_array(per_machine
))
935 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine field is not an array, refusing.");
937 array
= new(JsonVariant
*, json_variant_elements(per_machine
) + 1);
941 JSON_VARIANT_ARRAY_FOREACH(z
, per_machine
) {
944 if (!json_variant_is_object(z
))
945 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine entry is not an object, refusing.");
949 u
= json_variant_by_key(z
, "matchMachineId");
953 if (!json_variant_equal(u
, mmid
))
956 r
= json_variant_merge_object(&add
, z
);
958 return log_error_errno(r
, "Failed to merge perMachine entry: %m");
963 r
= json_variant_filter(&add
, arg_identity_filter
);
965 return log_error_errno(r
, "Failed to filter perMachine: %m");
967 r
= json_variant_merge_object(&add
, arg_identity_extra_this_machine
);
969 return log_error_errno(r
, "Failed to merge in perMachine fields: %m");
971 if (arg_identity_filter_rlimits
|| arg_identity_extra_rlimits
) {
972 _cleanup_(json_variant_unrefp
) JsonVariant
*rlv
= NULL
;
974 rlv
= json_variant_ref(json_variant_by_key(add
, "resourceLimits"));
976 r
= json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
978 return log_error_errno(r
, "Failed to filter resource limits: %m");
980 r
= json_variant_merge_object(&rlv
, arg_identity_extra_rlimits
);
982 return log_error_errno(r
, "Failed to set resource limits: %m");
984 if (json_variant_is_blank_object(rlv
)) {
985 r
= json_variant_filter(&add
, STRV_MAKE("resourceLimits"));
987 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
989 r
= json_variant_set_field(&add
, "resourceLimits", rlv
);
991 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
995 if (!json_variant_is_blank_object(add
)) {
996 r
= json_variant_set_field(&add
, "matchMachineId", mmid
);
998 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
1003 r
= json_variant_new_array(&npm
, array
, i
);
1005 return log_error_errno(r
, "Failed to allocate new perMachine array: %m");
1007 json_variant_unref(per_machine
);
1008 per_machine
= TAKE_PTR(npm
);
1010 _cleanup_(json_variant_unrefp
) JsonVariant
*item
= json_variant_ref(arg_identity_extra_this_machine
);
1012 if (arg_identity_extra_rlimits
) {
1013 r
= json_variant_set_field(&item
, "resourceLimits", arg_identity_extra_rlimits
);
1015 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
1018 r
= json_variant_set_field(&item
, "matchMachineId", mmid
);
1020 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
1022 r
= json_variant_append_array(&per_machine
, item
);
1024 return log_error_errno(r
, "Failed to append to perMachine array: %m");
1027 r
= json_variant_set_field(&v
, "perMachine", per_machine
);
1029 return log_error_errno(r
, "Failed to update per machine record: %m");
1032 if (arg_identity_extra_privileged
|| arg_identity_filter
) {
1033 _cleanup_(json_variant_unrefp
) JsonVariant
*privileged
= NULL
;
1035 privileged
= json_variant_ref(json_variant_by_key(v
, "privileged"));
1037 r
= json_variant_filter(&privileged
, arg_identity_filter
);
1039 return log_error_errno(r
, "Failed to filter identity (privileged part): %m");
1041 r
= json_variant_merge_object(&privileged
, arg_identity_extra_privileged
);
1043 return log_error_errno(r
, "Failed to merge identities (privileged part): %m");
1045 if (json_variant_is_blank_object(privileged
)) {
1046 r
= json_variant_filter(&v
, STRV_MAKE("privileged"));
1048 return log_error_errno(r
, "Failed to drop privileged part from identity: %m");
1050 r
= json_variant_set_field(&v
, "privileged", privileged
);
1052 return log_error_errno(r
, "Failed to update privileged part of identity: %m");
1056 if (arg_identity_filter_rlimits
) {
1057 _cleanup_(json_variant_unrefp
) JsonVariant
*rlv
= NULL
;
1059 rlv
= json_variant_ref(json_variant_by_key(v
, "resourceLimits"));
1061 r
= json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
1063 return log_error_errno(r
, "Failed to filter resource limits: %m");
1065 /* Note that we only filter resource limits here, but don't apply them. We do that in the perMachine section */
1067 if (json_variant_is_blank_object(rlv
)) {
1068 r
= json_variant_filter(&v
, STRV_MAKE("resourceLimits"));
1070 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
1072 r
= json_variant_set_field(&v
, "resourceLimits", rlv
);
1074 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
1078 json_variant_unref(*_v
);
1084 static int add_disposition(JsonVariant
**v
) {
1089 if (json_variant_by_key(*v
, "disposition"))
1092 /* Set the disposition to regular, if not configured explicitly */
1093 r
= json_variant_set_field_string(v
, "disposition", "regular");
1095 return log_error_errno(r
, "Failed to set disposition field: %m");
1100 static int acquire_new_home_record(JsonVariant
*input
, UserRecord
**ret
) {
1101 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
1102 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1108 unsigned line
, column
;
1111 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Two identity records specified, refusing.");
1113 r
= json_parse_file(
1114 streq(arg_identity
, "-") ? stdin
: NULL
,
1115 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, JSON_PARSE_SENSITIVE
, &v
, &line
, &column
);
1117 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
1119 v
= json_variant_ref(input
);
1121 r
= apply_identity_changes(&v
);
1125 r
= add_disposition(&v
);
1129 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
1130 r
= identity_add_pkcs11_key_data(&v
, *i
);
1135 STRV_FOREACH(i
, arg_fido2_device
) {
1136 r
= identity_add_fido2_parameters(&v
, *i
, arg_fido2_lock_with
, arg_fido2_cred_alg
);
1141 if (arg_recovery_key
) {
1142 r
= identity_add_recovery_key(&v
);
1147 r
= update_last_change(&v
, true, false);
1152 json_variant_dump(v
, JSON_FORMAT_PRETTY
, NULL
, NULL
);
1154 hr
= user_record_new();
1158 r
= user_record_load(
1161 USER_RECORD_REQUIRE_REGULAR
|
1162 USER_RECORD_ALLOW_SECRET
|
1163 USER_RECORD_ALLOW_PRIVILEGED
|
1164 USER_RECORD_ALLOW_PER_MACHINE
|
1165 USER_RECORD_STRIP_BINDING
|
1166 USER_RECORD_STRIP_STATUS
|
1167 USER_RECORD_STRIP_SIGNATURE
|
1169 USER_RECORD_PERMISSIVE
);
1173 *ret
= TAKE_PTR(hr
);
1177 static int acquire_new_password(
1178 const char *user_name
,
1183 _cleanup_(erase_and_freep
) char *envpw
= NULL
;
1190 r
= getenv_steal_erase("NEWPASSWORD", &envpw
);
1192 return log_error_errno(r
, "Failed to acquire password from environment: %m");
1194 /* As above, this is not for use, just for testing */
1196 r
= user_record_set_password(hr
, STRV_MAKE(envpw
), /* prepend = */ true);
1198 return log_error_errno(r
, "Failed to store password: %m");
1201 *ret
= TAKE_PTR(envpw
);
1207 (void) suggest_passwords();
1210 _cleanup_strv_free_erase_
char **first
= NULL
, **second
= NULL
;
1211 _cleanup_free_
char *question
= NULL
;
1214 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY
), "Too many attempts, giving up:");
1216 if (asprintf(&question
, "Please enter new password for user %s:", user_name
) < 0)
1219 r
= ask_password_auto(
1221 /* icon= */ "user-home",
1223 /* key_name= */ "home-password",
1224 /* credential_name= */ "home.new-password",
1226 0, /* no caching, we want to collect a new password here after all */
1229 return log_error_errno(r
, "Failed to acquire password: %m");
1231 question
= mfree(question
);
1232 if (asprintf(&question
, "Please enter new password for user %s (repeat):", user_name
) < 0)
1235 r
= ask_password_auto(
1237 /* icon= */ "user-home",
1239 /* key_name= */ "home-password",
1240 /* credential_name= */ "home.new-password",
1245 return log_error_errno(r
, "Failed to acquire password: %m");
1247 if (strv_equal(first
, second
)) {
1248 _cleanup_(erase_and_freep
) char *copy
= NULL
;
1251 copy
= strdup(first
[0]);
1256 r
= user_record_set_password(hr
, first
, /* prepend = */ true);
1258 return log_error_errno(r
, "Failed to store password: %m");
1261 *ret
= TAKE_PTR(copy
);
1266 log_error("Password didn't match, try again.");
1270 static int create_home_common(JsonVariant
*input
) {
1271 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1272 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1275 r
= acquire_bus(&bus
);
1279 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1281 r
= acquire_new_home_record(input
, &hr
);
1285 /* If the JSON record carries no plain text password (besides the recovery key), then let's query it
1287 if (strv_length(hr
->password
) <= arg_recovery_key
) {
1289 if (strv_isempty(hr
->hashed_password
)) {
1290 _cleanup_(erase_and_freep
) char *new_password
= NULL
;
1292 /* No regular (i.e. non-PKCS#11) hashed passwords set in the record, let's fix that. */
1293 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ true, &new_password
);
1297 r
= user_record_make_hashed_password(hr
, STRV_MAKE(new_password
), /* extend = */ false);
1299 return log_error_errno(r
, "Failed to hash password: %m");
1301 /* There's a hash password set in the record, acquire the unhashed version of it. */
1302 r
= acquire_existing_password(
1305 /* emphasize_current= */ false,
1306 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_PUSH_CACHE
);
1312 if (hr
->enforce_password_policy
== 0) {
1313 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1315 /* If password quality enforcement is disabled, let's at least warn client side */
1317 r
= user_record_check_password_quality(hr
, hr
, &error
);
1319 log_warning_errno(r
, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error
, r
));
1323 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1324 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1325 _cleanup_(erase_and_freep
) char *formatted
= NULL
;
1327 r
= json_variant_format(hr
->json
, 0, &formatted
);
1329 return log_error_errno(r
, "Failed to format user record: %m");
1331 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "CreateHome");
1333 return bus_log_create_error(r
);
1335 (void) sd_bus_message_sensitive(m
);
1337 r
= sd_bus_message_append(m
, "s", formatted
);
1339 return bus_log_create_error(r
);
1341 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1343 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
1344 _cleanup_(erase_and_freep
) char *new_password
= NULL
;
1346 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
1347 log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
1349 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ false, &new_password
);
1353 r
= user_record_make_hashed_password(hr
, STRV_MAKE(new_password
), /* extend = */ false);
1355 return log_error_errno(r
, "Failed to hash passwords: %m");
1357 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1368 static int create_home(int argc
, char *argv
[], void *userdata
) {
1372 /* If a username was specified, use it */
1374 if (valid_user_group_name(argv
[1], 0))
1375 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", argv
[1]);
1377 _cleanup_free_
char *un
= NULL
, *rr
= NULL
;
1379 /* Before we consider the user name invalid, let's check if we can split it? */
1380 r
= split_user_name_realm(argv
[1], &un
, &rr
);
1382 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name '%s' is not valid: %m", argv
[1]);
1385 r
= json_variant_set_field_string(&arg_identity_extra
, "realm", rr
);
1387 return log_error_errno(r
, "Failed to set realm field: %m");
1390 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", un
);
1393 return log_error_errno(r
, "Failed to set userName field: %m");
1395 /* If neither a username nor an identity have been specified we cannot operate. */
1397 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name required.");
1400 return create_home_common(/* input= */ NULL
);
1403 static int remove_home(int argc
, char *argv
[], void *userdata
) {
1404 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1407 r
= acquire_bus(&bus
);
1411 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1413 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1414 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1415 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1417 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "RemoveHome");
1419 return bus_log_create_error(r
);
1421 r
= sd_bus_message_append(m
, "s", *i
);
1423 return bus_log_create_error(r
);
1425 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1427 log_error_errno(r
, "Failed to remove home: %s", bus_error_message(&error
, r
));
1436 static int acquire_updated_home_record(
1438 const char *username
,
1441 _cleanup_(json_variant_unrefp
) JsonVariant
*json
= NULL
;
1442 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1448 unsigned line
, column
;
1451 r
= json_parse_file(
1452 streq(arg_identity
, "-") ? stdin
: NULL
,
1453 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, JSON_PARSE_SENSITIVE
, &json
, &line
, &column
);
1455 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
1457 un
= json_variant_by_key(json
, "userName");
1459 if (!json_variant_is_string(un
) || (username
&& !streq(json_variant_string(un
), username
)))
1460 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name specified on command line and in JSON record do not match.");
1463 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No username specified.");
1465 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", username
);
1467 return log_error_errno(r
, "Failed to set userName field: %m");
1471 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1472 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1476 if (!identity_properties_specified())
1477 return log_error_errno(SYNTHETIC_ERRNO(EALREADY
), "No field to change specified.");
1479 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByName", &error
, &reply
, "s", username
);
1481 return log_error_errno(r
, "Failed to acquire user home record: %s", bus_error_message(&error
, r
));
1483 r
= sd_bus_message_read(reply
, "sbo", &text
, &incomplete
, NULL
);
1485 return bus_log_parse_error(r
);
1488 return log_error_errno(SYNTHETIC_ERRNO(EACCES
), "Lacking rights to acquire user record including privileged metadata, can't update record.");
1490 r
= json_parse(text
, JSON_PARSE_SENSITIVE
, &json
, NULL
, NULL
);
1492 return log_error_errno(r
, "Failed to parse JSON identity: %m");
1494 reply
= sd_bus_message_unref(reply
);
1496 r
= json_variant_filter(&json
, STRV_MAKE("binding", "status", "signature"));
1498 return log_error_errno(r
, "Failed to strip binding and status from record to update: %m");
1501 r
= apply_identity_changes(&json
);
1505 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
1506 r
= identity_add_pkcs11_key_data(&json
, *i
);
1511 STRV_FOREACH(i
, arg_fido2_device
) {
1512 r
= identity_add_fido2_parameters(&json
, *i
, arg_fido2_lock_with
, arg_fido2_cred_alg
);
1517 /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
1519 r
= update_last_change(&json
, arg_pkcs11_token_uri
|| arg_fido2_device
, !arg_identity
);
1524 json_variant_dump(json
, JSON_FORMAT_PRETTY
, NULL
, NULL
);
1526 hr
= user_record_new();
1530 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
|USER_RECORD_PERMISSIVE
);
1534 *ret
= TAKE_PTR(hr
);
1538 static int home_record_reset_human_interaction_permission(UserRecord
*hr
) {
1543 /* When we execute multiple operations one after the other, let's reset the permission to ask the
1544 * user each time, so that if interaction is necessary we will be told so again and thus can print a
1545 * nice message to the user, telling the user so. */
1547 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, -1);
1549 return log_error_errno(r
, "Failed to reset PKCS#11 protected authentication path permission flag: %m");
1551 r
= user_record_set_fido2_user_presence_permitted(hr
, -1);
1553 return log_error_errno(r
, "Failed to reset FIDO2 user presence permission flag: %m");
1555 r
= user_record_set_fido2_user_verification_permitted(hr
, -1);
1557 return log_error_errno(r
, "Failed to reset FIDO2 user verification permission flag: %m");
1562 static int update_home(int argc
, char *argv
[], void *userdata
) {
1563 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1564 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
, *secret
= NULL
;
1565 _cleanup_free_
char *buffer
= NULL
;
1566 const char *username
;
1571 else if (!arg_identity
) {
1572 buffer
= getusername_malloc();
1580 r
= acquire_bus(&bus
);
1584 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1586 r
= acquire_updated_home_record(bus
, username
, &hr
);
1590 /* Add in all secrets we can acquire cheaply */
1591 r
= acquire_passed_secrets(username
, &secret
);
1595 r
= user_record_merge_secret(hr
, secret
);
1599 /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
1600 * authentication might be confusing. */
1602 if (arg_and_resize
|| arg_and_change_password
)
1603 log_info("Updating home directory.");
1606 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1607 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1608 _cleanup_free_
char *formatted
= NULL
;
1610 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "UpdateHome");
1612 return bus_log_create_error(r
);
1614 r
= json_variant_format(hr
->json
, 0, &formatted
);
1616 return log_error_errno(r
, "Failed to format user record: %m");
1618 (void) sd_bus_message_sensitive(m
);
1620 r
= sd_bus_message_append(m
, "s", formatted
);
1622 return bus_log_create_error(r
);
1624 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1626 if (arg_and_change_password
&&
1627 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1628 /* In the generic handler we'd ask for a password in this case, but when
1629 * changing passwords that's not sufficient, as we need to acquire all keys
1631 return log_error_errno(r
, "Security token not inserted, refusing.");
1633 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1641 log_info("Resizing home.");
1643 (void) home_record_reset_human_interaction_permission(hr
);
1645 /* Also sync down disk size to underlying LUKS/fscrypt/quota */
1646 while (arg_and_resize
) {
1647 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1648 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1650 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ResizeHome");
1652 return bus_log_create_error(r
);
1654 /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
1655 r
= sd_bus_message_append(m
, "st", hr
->user_name
, UINT64_MAX
);
1657 return bus_log_create_error(r
);
1659 r
= bus_message_append_secret(m
, hr
);
1661 return bus_log_create_error(r
);
1663 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1665 if (arg_and_change_password
&&
1666 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1667 return log_error_errno(r
, "Security token not inserted, refusing.");
1669 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1676 if (arg_and_change_password
)
1677 log_info("Synchronizing passwords and encryption keys.");
1679 (void) home_record_reset_human_interaction_permission(hr
);
1681 /* Also sync down passwords to underlying LUKS/fscrypt */
1682 while (arg_and_change_password
) {
1683 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1684 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1686 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ChangePasswordHome");
1688 return bus_log_create_error(r
);
1690 /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
1691 r
= sd_bus_message_append(m
, "ss", hr
->user_name
, "{}");
1693 return bus_log_create_error(r
);
1695 r
= bus_message_append_secret(m
, hr
);
1697 return bus_log_create_error(r
);
1699 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1701 if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1702 return log_error_errno(r
, "Security token not inserted, refusing.");
1704 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1714 static int passwd_home(int argc
, char *argv
[], void *userdata
) {
1715 _cleanup_(user_record_unrefp
) UserRecord
*old_secret
= NULL
, *new_secret
= NULL
;
1716 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1717 _cleanup_free_
char *buffer
= NULL
;
1718 const char *username
;
1721 if (arg_pkcs11_token_uri
)
1722 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1723 "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=%s'.",
1724 special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
1725 if (arg_fido2_device
)
1726 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1727 "To change the FIDO2 security token use 'homectl update --fido2-device=%s'.",
1728 special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
1729 if (identity_properties_specified())
1730 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "The 'passwd' verb does not permit changing other record properties at the same time.");
1735 buffer
= getusername_malloc();
1742 r
= acquire_bus(&bus
);
1746 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1748 r
= acquire_passed_secrets(username
, &old_secret
);
1752 new_secret
= user_record_new();
1756 r
= acquire_new_password(username
, new_secret
, /* suggest = */ true, NULL
);
1761 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1762 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1764 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ChangePasswordHome");
1766 return bus_log_create_error(r
);
1768 r
= sd_bus_message_append(m
, "s", username
);
1770 return bus_log_create_error(r
);
1772 r
= bus_message_append_secret(m
, new_secret
);
1774 return bus_log_create_error(r
);
1776 r
= bus_message_append_secret(m
, old_secret
);
1778 return bus_log_create_error(r
);
1780 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1782 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
1784 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
1786 r
= acquire_new_password(username
, new_secret
, /* suggest = */ false, NULL
);
1788 } else if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1790 /* In the generic handler we'd ask for a password in this case, but when
1791 * changing passwords that's not sufficeint, as we need to acquire all keys
1793 return log_error_errno(r
, "Security token not inserted, refusing.");
1795 r
= handle_generic_user_record_error(username
, old_secret
, &error
, r
, true);
1805 static int parse_disk_size(const char *t
, uint64_t *ret
) {
1811 if (streq(t
, "min"))
1813 else if (streq(t
, "max"))
1814 *ret
= UINT64_MAX
-1; /* Largest size that isn't UINT64_MAX special marker */
1818 r
= parse_size(t
, 1024, &ds
);
1820 return log_error_errno(r
, "Failed to parse disk size parameter: %s", t
);
1822 if (ds
>= UINT64_MAX
) /* UINT64_MAX has special meaning for us ("dont change"), refuse */
1823 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
), "Disk size out of range: %s", t
);
1831 static int resize_home(int argc
, char *argv
[], void *userdata
) {
1832 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1833 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
1834 uint64_t ds
= UINT64_MAX
;
1837 r
= acquire_bus(&bus
);
1841 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1843 if (arg_disk_size_relative
!= UINT64_MAX
||
1844 (argc
> 2 && parse_permyriad(argv
[2]) >= 0))
1845 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1846 "Relative disk size specification currently not supported when resizing.");
1849 r
= parse_disk_size(argv
[2], &ds
);
1854 if (arg_disk_size
!= UINT64_MAX
) {
1855 if (ds
!= UINT64_MAX
&& ds
!= arg_disk_size
)
1856 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Disk size specified twice and doesn't match, refusing.");
1861 r
= acquire_passed_secrets(argv
[1], &secret
);
1866 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1867 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1869 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ResizeHome");
1871 return bus_log_create_error(r
);
1873 r
= sd_bus_message_append(m
, "st", argv
[1], ds
);
1875 return bus_log_create_error(r
);
1877 r
= bus_message_append_secret(m
, secret
);
1879 return bus_log_create_error(r
);
1881 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1883 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
1893 static int lock_home(int argc
, char *argv
[], void *userdata
) {
1894 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1897 r
= acquire_bus(&bus
);
1901 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1902 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1903 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1905 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "LockHome");
1907 return bus_log_create_error(r
);
1909 r
= sd_bus_message_append(m
, "s", *i
);
1911 return bus_log_create_error(r
);
1913 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1915 log_error_errno(r
, "Failed to lock home: %s", bus_error_message(&error
, r
));
1924 static int unlock_home(int argc
, char *argv
[], void *userdata
) {
1925 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1928 r
= acquire_bus(&bus
);
1932 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1933 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
1935 r
= acquire_passed_secrets(*i
, &secret
);
1940 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1941 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1943 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "UnlockHome");
1945 return bus_log_create_error(r
);
1947 r
= sd_bus_message_append(m
, "s", *i
);
1949 return bus_log_create_error(r
);
1951 r
= bus_message_append_secret(m
, secret
);
1953 return bus_log_create_error(r
);
1955 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1957 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
1972 static int with_home(int argc
, char *argv
[], void *userdata
) {
1973 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1974 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
1975 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1976 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
1977 _cleanup_close_
int acquired_fd
= -EBADF
;
1978 _cleanup_strv_free_
char **cmdline
= NULL
;
1983 r
= acquire_bus(&bus
);
1988 _cleanup_free_
char *shell
= NULL
;
1990 /* If no command is specified, spawn a shell */
1991 r
= get_shell(&shell
);
1993 return log_error_errno(r
, "Failed to acquire shell: %m");
1995 cmdline
= strv_new(shell
);
1997 cmdline
= strv_copy(argv
+ 2);
2001 r
= acquire_passed_secrets(argv
[1], &secret
);
2006 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "AcquireHome");
2008 return bus_log_create_error(r
);
2010 r
= sd_bus_message_append(m
, "s", argv
[1]);
2012 return bus_log_create_error(r
);
2014 r
= bus_message_append_secret(m
, secret
);
2016 return bus_log_create_error(r
);
2018 r
= sd_bus_message_append(m
, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
2020 return bus_log_create_error(r
);
2022 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
2023 m
= sd_bus_message_unref(m
);
2025 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2029 sd_bus_error_free(&error
);
2033 r
= sd_bus_message_read(reply
, "h", &fd
);
2035 return bus_log_parse_error(r
);
2037 acquired_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
2038 if (acquired_fd
< 0)
2039 return log_error_errno(errno
, "Failed to duplicate acquired fd: %m");
2041 reply
= sd_bus_message_unref(reply
);
2046 r
= bus_call_method(bus
, bus_mgr
, "GetHomeByName", &error
, &reply
, "s", argv
[1]);
2048 return log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
2050 r
= sd_bus_message_read(reply
, "usussso", NULL
, NULL
, NULL
, NULL
, &home
, NULL
, NULL
);
2052 return bus_log_parse_error(r
);
2054 r
= safe_fork("(with)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG_SIGTERM
|FORK_LOG
|FORK_RLIMIT_NOFILE_SAFE
|FORK_REOPEN_LOG
, &pid
);
2058 if (chdir(home
) < 0) {
2059 log_error_errno(errno
, "Failed to change to directory %s: %m", home
);
2063 execvp(cmdline
[0], cmdline
);
2064 log_error_errno(errno
, "Failed to execute %s: %m", cmdline
[0]);
2068 ret
= wait_for_terminate_and_check(cmdline
[0], pid
, WAIT_LOG_ABNORMAL
);
2070 /* Close the fd that pings the home now. */
2071 acquired_fd
= safe_close(acquired_fd
);
2073 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ReleaseHome");
2075 return bus_log_create_error(r
);
2077 r
= sd_bus_message_append(m
, "s", argv
[1]);
2079 return bus_log_create_error(r
);
2081 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2083 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_BUSY
))
2084 log_notice("Not deactivating home directory of %s, as it is still used.", argv
[1]);
2086 return log_error_errno(r
, "Failed to release user home: %s", bus_error_message(&error
, r
));
2092 static int lock_all_homes(int argc
, char *argv
[], void *userdata
) {
2093 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2094 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2095 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2098 r
= acquire_bus(&bus
);
2102 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "LockAllHomes");
2104 return bus_log_create_error(r
);
2106 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2108 return log_error_errno(r
, "Failed to lock all homes: %s", bus_error_message(&error
, r
));
2113 static int deactivate_all_homes(int argc
, char *argv
[], void *userdata
) {
2114 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2115 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2116 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2119 r
= acquire_bus(&bus
);
2123 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "DeactivateAllHomes");
2125 return bus_log_create_error(r
);
2127 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2129 return log_error_errno(r
, "Failed to deactivate all homes: %s", bus_error_message(&error
, r
));
2134 static int rebalance(int argc
, char *argv
[], void *userdata
) {
2135 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2136 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2137 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2140 r
= acquire_bus(&bus
);
2144 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "Rebalance");
2146 return bus_log_create_error(r
);
2148 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2150 if (sd_bus_error_has_name(&error
, BUS_ERROR_REBALANCE_NOT_NEEDED
))
2151 log_info("No homes needed rebalancing.");
2153 return log_error_errno(r
, "Failed to rebalance: %s", bus_error_message(&error
, r
));
2155 log_info("Completed rebalancing.");
2160 static int create_from_credentials(void) {
2161 _cleanup_close_
int fd
= -EBADF
;
2162 int ret
= 0, n_created
= 0, r
;
2164 fd
= open_credentials_dir();
2165 if (IN_SET(fd
, -ENXIO
, -ENOENT
)) /* Credential env var not set, or dir doesn't exist. */
2168 return log_error_errno(fd
, "Failed to open credentials directory: %m");
2170 _cleanup_free_ DirectoryEntries
*des
= NULL
;
2171 r
= readdir_all(fd
, RECURSE_DIR_SORT
|RECURSE_DIR_IGNORE_DOT
|RECURSE_DIR_ENSURE_TYPE
, &des
);
2173 return log_error_errno(r
, "Failed to enumerate credentials: %m");
2175 FOREACH_ARRAY(i
, des
->entries
, des
->n_entries
) {
2176 _cleanup_(json_variant_unrefp
) JsonVariant
*identity
= NULL
;
2177 struct dirent
*de
= *i
;
2180 if (de
->d_type
!= DT_REG
)
2183 e
= startswith(de
->d_name
, "home.create.");
2187 if (!valid_user_group_name(e
, 0)) {
2188 log_notice("Skipping over credential with name that is not a suitable user name: %s", de
->d_name
);
2192 r
= json_parse_file_at(
2198 /* ret_line= */ NULL
,
2199 /* ret_column= */ NULL
);
2201 log_warning_errno(r
, "Failed to parse user record in credential '%s', ignoring: %m", de
->d_name
);
2206 un
= json_variant_by_key(identity
, "userName");
2208 if (!json_variant_is_string(un
)) {
2209 log_warning("User record from credential '%s' contains 'userName' field of invalid type, ignoring.", de
->d_name
);
2213 if (!streq(json_variant_string(un
), e
)) {
2214 log_warning("User record from credential '%s' contains 'userName' field (%s) that doesn't match credential name (%s), ignoring.", de
->d_name
, json_variant_string(un
), e
);
2218 r
= json_variant_set_field_string(&identity
, "userName", e
);
2220 return log_warning_errno(r
, "Failed to set userName field: %m");
2223 log_notice("Processing user '%s' from credentials.", e
);
2225 r
= create_home_common(identity
);
2232 return ret
< 0 ? ret
: n_created
;
2235 static int has_regular_user(void) {
2236 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
2239 r
= userdb_all(USERDB_SUPPRESS_SHADOW
, &iterator
);
2241 return log_error_errno(r
, "Failed to create user enumerator: %m");
2244 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
2246 r
= userdb_iterator_get(iterator
, &ur
);
2250 return log_error_errno(r
, "Failed to enumerate users: %m");
2252 if (user_record_disposition(ur
) == USER_REGULAR
)
2259 static int create_interactively(void) {
2260 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2261 _cleanup_free_
char *username
= NULL
;
2264 if (!arg_prompt_new_user
) {
2265 log_debug("Prompting for user creation was not requested.");
2269 r
= acquire_bus(&bus
);
2273 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2275 (void) reset_terminal_fd(STDIN_FILENO
, /* switch_to_text= */ false);
2278 username
= mfree(username
);
2280 r
= ask_string(&username
,
2281 "%s Please enter user name to create (empty to skip): ",
2282 special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET
));
2284 return log_error_errno(r
, "Failed to query user for username: %m");
2286 if (isempty(username
)) {
2287 log_info("No data entered, skipping.");
2291 if (!valid_user_group_name(username
, /* flags= */ 0)) {
2292 log_notice("Specified user name is not a valid UNIX user name, try again: %s", username
);
2296 r
= userdb_by_name(username
, USERDB_SUPPRESS_SHADOW
, /* ret= */ NULL
);
2300 return log_error_errno(r
, "Failed to check if specified user '%s' already exists: %m", username
);
2302 log_notice("Specified user '%s' exists already, try again.", username
);
2305 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", username
);
2307 return log_error_errno(r
, "Failed to set userName field: %m");
2309 return create_home_common(/* input= */ NULL
);
2312 static int verb_firstboot(int argc
, char *argv
[], void *userdata
) {
2315 /* Let's honour the systemd.firstboot kernel command line option, just like the systemd-firstboot
2319 r
= proc_cmdline_get_bool("systemd.firstboot", /* flags = */ 0, &enabled
);
2321 return log_error_errno(r
, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
2322 if (r
> 0 && !enabled
) {
2323 log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
2324 arg_prompt_new_user
= false;
2327 r
= create_from_credentials();
2330 if (r
> 0) /* Already created users from credentials */
2333 r
= has_regular_user();
2337 log_info("Regular user already present in user database, skipping user creation.");
2341 return create_interactively();
2344 static int drop_from_identity(const char *field
) {
2349 /* If we are called to update an identity record and drop some field, let's keep track of what to
2350 * remove from the old record */
2351 r
= strv_extend(&arg_identity_filter
, field
);
2355 /* Let's also drop the field if it was previously set to a new value on the same command line */
2356 r
= json_variant_filter(&arg_identity_extra
, STRV_MAKE(field
));
2358 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2360 r
= json_variant_filter(&arg_identity_extra_this_machine
, STRV_MAKE(field
));
2362 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2364 r
= json_variant_filter(&arg_identity_extra_privileged
, STRV_MAKE(field
));
2366 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2371 static int help(int argc
, char *argv
[], void *userdata
) {
2372 _cleanup_free_
char *link
= NULL
;
2375 pager_open(arg_pager_flags
);
2377 r
= terminal_urlify_man("homectl", "1", &link
);
2381 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
2382 "%2$sCreate, manipulate or inspect home directories.%3$s\n"
2383 "\n%4$sCommands:%5$s\n"
2384 " list List home areas\n"
2385 " activate USER… Activate a home area\n"
2386 " deactivate USER… Deactivate a home area\n"
2387 " inspect USER… Inspect a home area\n"
2388 " authenticate USER… Authenticate a home area\n"
2389 " create USER Create a home area\n"
2390 " remove USER… Remove a home area\n"
2391 " update USER Update a home area\n"
2392 " passwd USER Change password of a home area\n"
2393 " resize USER SIZE Resize a home area\n"
2394 " lock USER… Temporarily lock an active home area\n"
2395 " unlock USER… Unlock a temporarily locked home area\n"
2396 " lock-all Lock all suitable home areas\n"
2397 " deactivate-all Deactivate all active home areas\n"
2398 " rebalance Rebalance free space between home areas\n"
2399 " with USER [COMMAND…] Run shell or command with access to a home area\n"
2400 " firstboot Run first-boot home area creation wizard\n"
2401 "\n%4$sOptions:%5$s\n"
2402 " -h --help Show this help\n"
2403 " --version Show package version\n"
2404 " --no-pager Do not pipe output into a pager\n"
2405 " --no-legend Do not show the headers and footers\n"
2406 " --no-ask-password Do not ask for system passwords\n"
2407 " -H --host=[USER@]HOST Operate on remote host\n"
2408 " -M --machine=CONTAINER Operate on local container\n"
2409 " --identity=PATH Read JSON identity from file\n"
2410 " --json=FORMAT Output inspection data in JSON (takes one of\n"
2411 " pretty, short, off)\n"
2412 " -j Equivalent to --json=pretty (on TTY) or\n"
2413 " --json=short (otherwise)\n"
2414 " --export-format= Strip JSON inspection data (full, stripped,\n"
2416 " -E When specified once equals -j --export-format=\n"
2417 " stripped, when specified twice equals\n"
2418 " -j --export-format=minimal\n"
2419 " --prompt-new-user firstboot: Query user interactively for user\n"
2421 "\n%4$sGeneral User Record Properties:%5$s\n"
2422 " -c --real-name=REALNAME Real name for user\n"
2423 " --realm=REALM Realm to create user in\n"
2424 " --email-address=EMAIL Email address for user\n"
2425 " --location=LOCATION Set location of user on earth\n"
2426 " --icon-name=NAME Icon name for user\n"
2427 " -d --home-dir=PATH Home directory\n"
2428 " -u --uid=UID Numeric UID for user\n"
2429 " -G --member-of=GROUP Add user to group\n"
2430 " --capability-bounding-set=CAPS\n"
2431 " Bounding POSIX capability set\n"
2432 " --capability-ambient-set=CAPS\n"
2433 " Ambient POSIX capability set\n"
2434 " --skel=PATH Skeleton directory to use\n"
2435 " --shell=PATH Shell for account\n"
2436 " --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n"
2437 " --timezone=TIMEZONE Set a time-zone\n"
2438 " --language=LOCALE Set preferred language\n"
2439 " --ssh-authorized-keys=KEYS\n"
2440 " Specify SSH public keys\n"
2441 " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
2442 " private key and matching X.509 certificate\n"
2443 " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
2445 " --fido2-with-client-pin=BOOL\n"
2446 " Whether to require entering a PIN to unlock the\n"
2448 " --fido2-with-user-presence=BOOL\n"
2449 " Whether to require user presence to unlock the\n"
2451 " --fido2-with-user-verification=BOOL\n"
2452 " Whether to require user verification to unlock\n"
2454 " --recovery-key=BOOL Add a recovery key\n"
2455 "\n%4$sAccount Management User Record Properties:%5$s\n"
2456 " --locked=BOOL Set locked account state\n"
2457 " --not-before=TIMESTAMP Do not allow logins before\n"
2458 " --not-after=TIMESTAMP Do not allow logins after\n"
2459 " --rate-limit-interval=SECS\n"
2460 " Login rate-limit interval in seconds\n"
2461 " --rate-limit-burst=NUMBER\n"
2462 " Login rate-limit attempts per interval\n"
2463 "\n%4$sPassword Policy User Record Properties:%5$s\n"
2464 " --password-hint=HINT Set Password hint\n"
2465 " --enforce-password-policy=BOOL\n"
2466 " Control whether to enforce system's password\n"
2467 " policy for this user\n"
2468 " -P Same as --enforce-password-password=no\n"
2469 " --password-change-now=BOOL\n"
2470 " Require the password to be changed on next login\n"
2471 " --password-change-min=TIME\n"
2472 " Require minimum time between password changes\n"
2473 " --password-change-max=TIME\n"
2474 " Require maximum time between password changes\n"
2475 " --password-change-warn=TIME\n"
2476 " How much time to warn before password expiry\n"
2477 " --password-change-inactive=TIME\n"
2478 " How much time to block password after expiry\n"
2479 "\n%4$sResource Management User Record Properties:%5$s\n"
2480 " --disk-size=BYTES Size to assign the user on disk\n"
2481 " --access-mode=MODE User home directory access mode\n"
2482 " --umask=MODE Umask for user when logging in\n"
2483 " --nice=NICE Nice level for user\n"
2484 " --rlimit=LIMIT=VALUE[:VALUE]\n"
2485 " Set resource limits\n"
2486 " --tasks-max=MAX Set maximum number of per-user tasks\n"
2487 " --memory-high=BYTES Set high memory threshold in bytes\n"
2488 " --memory-max=BYTES Set maximum memory limit\n"
2489 " --cpu-weight=WEIGHT Set CPU weight\n"
2490 " --io-weight=WEIGHT Set IO weight\n"
2491 "\n%4$sStorage User Record Properties:%5$s\n"
2492 " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
2493 " subvolume, cifs)\n"
2494 " --image-path=PATH Path to image file/directory\n"
2495 " --drop-caches=BOOL Whether to automatically drop caches on logout\n"
2496 "\n%4$sLUKS Storage User Record Properties:%5$s\n"
2497 " --fs-type=TYPE File system type to use in case of luks\n"
2498 " storage (btrfs, ext4, xfs)\n"
2499 " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
2500 " when activated (mounted)\n"
2501 " --luks-offline-discard=BOOL\n"
2502 " Whether to trim file on logout\n"
2503 " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
2504 " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
2505 " --luks-volume-key-size=BITS\n"
2506 " Volume key size to use for LUKS encryption\n"
2507 " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
2508 " --luks-pbkdf-hash-algorithm=ALGORITHM\n"
2509 " PBKDF hash algorithm to use\n"
2510 " --luks-pbkdf-time-cost=SECS\n"
2511 " Time cost for PBKDF in seconds\n"
2512 " --luks-pbkdf-memory-cost=BYTES\n"
2513 " Memory cost for PBKDF in bytes\n"
2514 " --luks-pbkdf-parallel-threads=NUMBER\n"
2515 " Number of parallel threads for PKBDF\n"
2516 " --luks-sector-size=BYTES\n"
2517 " Sector size for LUKS encryption in bytes\n"
2518 " --luks-extra-mount-options=OPTIONS\n"
2519 " LUKS extra mount options\n"
2520 " --auto-resize-mode=MODE Automatically grow/shrink home on login/logout\n"
2521 " --rebalance-weight=WEIGHT Weight while rebalancing\n"
2522 "\n%4$sMounting User Record Properties:%5$s\n"
2523 " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
2524 " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
2525 " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
2526 "\n%4$sCIFS User Record Properties:%5$s\n"
2527 " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
2528 " --cifs-user-name=USER CIFS (Windows) user name\n"
2529 " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
2530 " --cifs-extra-mount-options=OPTIONS\n"
2531 " CIFS (Windows) extra mount options\n"
2532 "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
2533 " --stop-delay=SECS How long to leave user services running after\n"
2535 " --kill-processes=BOOL Whether to kill user processes when sessions\n"
2537 " --auto-login=BOOL Try to log this user in automatically\n"
2538 "\nSee the %6$s for details.\n",
2539 program_invocation_short_name
,
2549 static int parse_argv(int argc
, char *argv
[]) {
2552 ARG_VERSION
= 0x100,
2555 ARG_NO_ASK_PASSWORD
,
2565 ARG_LUKS_OFFLINE_DISCARD
,
2571 ARG_SSH_AUTHORIZED_KEYS
,
2580 ARG_LUKS_CIPHER_MODE
,
2581 ARG_LUKS_VOLUME_KEY_SIZE
,
2588 ARG_CIFS_EXTRA_MOUNT_OPTIONS
,
2594 ARG_LUKS_PBKDF_TYPE
,
2595 ARG_LUKS_PBKDF_HASH_ALGORITHM
,
2596 ARG_LUKS_PBKDF_FORCE_ITERATIONS
,
2597 ARG_LUKS_PBKDF_TIME_COST
,
2598 ARG_LUKS_PBKDF_MEMORY_COST
,
2599 ARG_LUKS_PBKDF_PARALLEL_THREADS
,
2600 ARG_LUKS_SECTOR_SIZE
,
2601 ARG_RATE_LIMIT_INTERVAL
,
2602 ARG_RATE_LIMIT_BURST
,
2605 ARG_ENFORCE_PASSWORD_POLICY
,
2606 ARG_PASSWORD_CHANGE_NOW
,
2607 ARG_PASSWORD_CHANGE_MIN
,
2608 ARG_PASSWORD_CHANGE_MAX
,
2609 ARG_PASSWORD_CHANGE_WARN
,
2610 ARG_PASSWORD_CHANGE_INACTIVE
,
2613 ARG_PKCS11_TOKEN_URI
,
2620 ARG_AND_CHANGE_PASSWORD
,
2622 ARG_LUKS_EXTRA_MOUNT_OPTIONS
,
2623 ARG_AUTO_RESIZE_MODE
,
2624 ARG_REBALANCE_WEIGHT
,
2626 ARG_CAPABILITY_BOUNDING_SET
,
2627 ARG_CAPABILITY_AMBIENT_SET
,
2628 ARG_PROMPT_NEW_USER
,
2631 static const struct option options
[] = {
2632 { "help", no_argument
, NULL
, 'h' },
2633 { "version", no_argument
, NULL
, ARG_VERSION
},
2634 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2635 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2636 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2637 { "host", required_argument
, NULL
, 'H' },
2638 { "machine", required_argument
, NULL
, 'M' },
2639 { "identity", required_argument
, NULL
, 'I' },
2640 { "real-name", required_argument
, NULL
, 'c' },
2641 { "comment", required_argument
, NULL
, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
2642 { "realm", required_argument
, NULL
, ARG_REALM
},
2643 { "email-address", required_argument
, NULL
, ARG_EMAIL_ADDRESS
},
2644 { "location", required_argument
, NULL
, ARG_LOCATION
},
2645 { "password-hint", required_argument
, NULL
, ARG_PASSWORD_HINT
},
2646 { "icon-name", required_argument
, NULL
, ARG_ICON_NAME
},
2647 { "home-dir", required_argument
, NULL
, 'd' }, /* Compatible with useradd(8) */
2648 { "uid", required_argument
, NULL
, 'u' }, /* Compatible with useradd(8) */
2649 { "member-of", required_argument
, NULL
, 'G' },
2650 { "groups", required_argument
, NULL
, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
2651 { "skel", required_argument
, NULL
, 'k' }, /* Compatible with useradd(8) */
2652 { "shell", required_argument
, NULL
, 's' }, /* Compatible with useradd(8) */
2653 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2654 { "timezone", required_argument
, NULL
, ARG_TIMEZONE
},
2655 { "language", required_argument
, NULL
, ARG_LANGUAGE
},
2656 { "locked", required_argument
, NULL
, ARG_LOCKED
},
2657 { "not-before", required_argument
, NULL
, ARG_NOT_BEFORE
},
2658 { "not-after", required_argument
, NULL
, ARG_NOT_AFTER
},
2659 { "expiredate", required_argument
, NULL
, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
2660 { "ssh-authorized-keys", required_argument
, NULL
, ARG_SSH_AUTHORIZED_KEYS
},
2661 { "disk-size", required_argument
, NULL
, ARG_DISK_SIZE
},
2662 { "access-mode", required_argument
, NULL
, ARG_ACCESS_MODE
},
2663 { "umask", required_argument
, NULL
, ARG_UMASK
},
2664 { "nice", required_argument
, NULL
, ARG_NICE
},
2665 { "rlimit", required_argument
, NULL
, ARG_RLIMIT
},
2666 { "tasks-max", required_argument
, NULL
, ARG_TASKS_MAX
},
2667 { "memory-high", required_argument
, NULL
, ARG_MEMORY_HIGH
},
2668 { "memory-max", required_argument
, NULL
, ARG_MEMORY_MAX
},
2669 { "cpu-weight", required_argument
, NULL
, ARG_CPU_WEIGHT
},
2670 { "io-weight", required_argument
, NULL
, ARG_IO_WEIGHT
},
2671 { "storage", required_argument
, NULL
, ARG_STORAGE
},
2672 { "image-path", required_argument
, NULL
, ARG_IMAGE_PATH
},
2673 { "fs-type", required_argument
, NULL
, ARG_FS_TYPE
},
2674 { "luks-discard", required_argument
, NULL
, ARG_LUKS_DISCARD
},
2675 { "luks-offline-discard", required_argument
, NULL
, ARG_LUKS_OFFLINE_DISCARD
},
2676 { "luks-cipher", required_argument
, NULL
, ARG_LUKS_CIPHER
},
2677 { "luks-cipher-mode", required_argument
, NULL
, ARG_LUKS_CIPHER_MODE
},
2678 { "luks-volume-key-size", required_argument
, NULL
, ARG_LUKS_VOLUME_KEY_SIZE
},
2679 { "luks-pbkdf-type", required_argument
, NULL
, ARG_LUKS_PBKDF_TYPE
},
2680 { "luks-pbkdf-hash-algorithm", required_argument
, NULL
, ARG_LUKS_PBKDF_HASH_ALGORITHM
},
2681 { "luks-pbkdf-force-iterations", required_argument
, NULL
, ARG_LUKS_PBKDF_FORCE_ITERATIONS
},
2682 { "luks-pbkdf-time-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_TIME_COST
},
2683 { "luks-pbkdf-memory-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_MEMORY_COST
},
2684 { "luks-pbkdf-parallel-threads", required_argument
, NULL
, ARG_LUKS_PBKDF_PARALLEL_THREADS
},
2685 { "luks-sector-size", required_argument
, NULL
, ARG_LUKS_SECTOR_SIZE
},
2686 { "nosuid", required_argument
, NULL
, ARG_NOSUID
},
2687 { "nodev", required_argument
, NULL
, ARG_NODEV
},
2688 { "noexec", required_argument
, NULL
, ARG_NOEXEC
},
2689 { "cifs-user-name", required_argument
, NULL
, ARG_CIFS_USER_NAME
},
2690 { "cifs-domain", required_argument
, NULL
, ARG_CIFS_DOMAIN
},
2691 { "cifs-service", required_argument
, NULL
, ARG_CIFS_SERVICE
},
2692 { "cifs-extra-mount-options", required_argument
, NULL
, ARG_CIFS_EXTRA_MOUNT_OPTIONS
},
2693 { "rate-limit-interval", required_argument
, NULL
, ARG_RATE_LIMIT_INTERVAL
},
2694 { "rate-limit-burst", required_argument
, NULL
, ARG_RATE_LIMIT_BURST
},
2695 { "stop-delay", required_argument
, NULL
, ARG_STOP_DELAY
},
2696 { "kill-processes", required_argument
, NULL
, ARG_KILL_PROCESSES
},
2697 { "enforce-password-policy", required_argument
, NULL
, ARG_ENFORCE_PASSWORD_POLICY
},
2698 { "password-change-now", required_argument
, NULL
, ARG_PASSWORD_CHANGE_NOW
},
2699 { "password-change-min", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MIN
},
2700 { "password-change-max", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MAX
},
2701 { "password-change-warn", required_argument
, NULL
, ARG_PASSWORD_CHANGE_WARN
},
2702 { "password-change-inactive", required_argument
, NULL
, ARG_PASSWORD_CHANGE_INACTIVE
},
2703 { "auto-login", required_argument
, NULL
, ARG_AUTO_LOGIN
},
2704 { "json", required_argument
, NULL
, ARG_JSON
},
2705 { "export-format", required_argument
, NULL
, ARG_EXPORT_FORMAT
},
2706 { "pkcs11-token-uri", required_argument
, NULL
, ARG_PKCS11_TOKEN_URI
},
2707 { "fido2-credential-algorithm", required_argument
, NULL
, ARG_FIDO2_CRED_ALG
},
2708 { "fido2-device", required_argument
, NULL
, ARG_FIDO2_DEVICE
},
2709 { "fido2-with-client-pin", required_argument
, NULL
, ARG_FIDO2_WITH_PIN
},
2710 { "fido2-with-user-presence", required_argument
, NULL
, ARG_FIDO2_WITH_UP
},
2711 { "fido2-with-user-verification",required_argument
, NULL
, ARG_FIDO2_WITH_UV
},
2712 { "recovery-key", required_argument
, NULL
, ARG_RECOVERY_KEY
},
2713 { "and-resize", required_argument
, NULL
, ARG_AND_RESIZE
},
2714 { "and-change-password", required_argument
, NULL
, ARG_AND_CHANGE_PASSWORD
},
2715 { "drop-caches", required_argument
, NULL
, ARG_DROP_CACHES
},
2716 { "luks-extra-mount-options", required_argument
, NULL
, ARG_LUKS_EXTRA_MOUNT_OPTIONS
},
2717 { "auto-resize-mode", required_argument
, NULL
, ARG_AUTO_RESIZE_MODE
},
2718 { "rebalance-weight", required_argument
, NULL
, ARG_REBALANCE_WEIGHT
},
2719 { "capability-bounding-set", required_argument
, NULL
, ARG_CAPABILITY_BOUNDING_SET
},
2720 { "capability-ambient-set", required_argument
, NULL
, ARG_CAPABILITY_AMBIENT_SET
},
2721 { "prompt-new-user", no_argument
, NULL
, ARG_PROMPT_NEW_USER
},
2733 c
= getopt_long(argc
, argv
, "hH:M:I:c:d:u:k:s:e:G:jPE", options
, NULL
);
2740 return help(0, NULL
, NULL
);
2746 arg_pager_flags
|= PAGER_DISABLE
;
2753 case ARG_NO_ASK_PASSWORD
:
2754 arg_ask_password
= false;
2758 arg_transport
= BUS_TRANSPORT_REMOTE
;
2763 arg_transport
= BUS_TRANSPORT_MACHINE
;
2768 arg_identity
= optarg
;
2772 if (isempty(optarg
)) {
2773 r
= drop_from_identity("realName");
2780 if (!valid_gecos(optarg
))
2781 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Real name '%s' not a valid GECOS field.", optarg
);
2783 r
= json_variant_set_field_string(&arg_identity_extra
, "realName", optarg
);
2785 return log_error_errno(r
, "Failed to set realName field: %m");
2790 _cleanup_free_
char *hd
= NULL
;
2792 if (isempty(optarg
)) {
2793 r
= drop_from_identity("homeDirectory");
2800 r
= parse_path_argument(optarg
, false, &hd
);
2804 if (!valid_home(hd
))
2805 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Home directory '%s' not valid.", hd
);
2807 r
= json_variant_set_field_string(&arg_identity_extra
, "homeDirectory", hd
);
2809 return log_error_errno(r
, "Failed to set homeDirectory field: %m");
2815 if (isempty(optarg
)) {
2816 r
= drop_from_identity("realm");
2823 r
= dns_name_is_valid(optarg
);
2825 return log_error_errno(r
, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg
);
2827 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Realm '%s' is not a valid DNS domain: %m", optarg
);
2829 r
= json_variant_set_field_string(&arg_identity_extra
, "realm", optarg
);
2831 return log_error_errno(r
, "Failed to set realm field: %m");
2834 case ARG_EMAIL_ADDRESS
:
2837 case ARG_CIFS_USER_NAME
:
2838 case ARG_CIFS_DOMAIN
:
2839 case ARG_CIFS_EXTRA_MOUNT_OPTIONS
:
2840 case ARG_LUKS_EXTRA_MOUNT_OPTIONS
: {
2843 c
== ARG_EMAIL_ADDRESS
? "emailAddress" :
2844 c
== ARG_LOCATION
? "location" :
2845 c
== ARG_ICON_NAME
? "iconName" :
2846 c
== ARG_CIFS_USER_NAME
? "cifsUserName" :
2847 c
== ARG_CIFS_DOMAIN
? "cifsDomain" :
2848 c
== ARG_CIFS_EXTRA_MOUNT_OPTIONS
? "cifsExtraMountOptions" :
2849 c
== ARG_LUKS_EXTRA_MOUNT_OPTIONS
? "luksExtraMountOptions" :
2854 if (isempty(optarg
)) {
2855 r
= drop_from_identity(field
);
2862 r
= json_variant_set_field_string(&arg_identity_extra
, field
, optarg
);
2864 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2869 case ARG_CIFS_SERVICE
:
2870 if (isempty(optarg
)) {
2871 r
= drop_from_identity("cifsService");
2878 r
= parse_cifs_service(optarg
, NULL
, NULL
, NULL
);
2880 return log_error_errno(r
, "Failed to validate CIFS service name: %s", optarg
);
2882 r
= json_variant_set_field_string(&arg_identity_extra
, "cifsService", optarg
);
2884 return log_error_errno(r
, "Failed to set cifsService field: %m");
2888 case ARG_PASSWORD_HINT
:
2889 if (isempty(optarg
)) {
2890 r
= drop_from_identity("passwordHint");
2897 r
= json_variant_set_field_string(&arg_identity_extra_privileged
, "passwordHint", optarg
);
2899 return log_error_errno(r
, "Failed to set passwordHint field: %m");
2901 string_erase(optarg
);
2907 if (isempty(optarg
)) {
2908 r
= drop_from_identity("niceLevel");
2914 r
= parse_nice(optarg
, &nc
);
2916 return log_error_errno(r
, "Failed to parse nice level: %s", optarg
);
2918 r
= json_variant_set_field_integer(&arg_identity_extra
, "niceLevel", nc
);
2920 return log_error_errno(r
, "Failed to set niceLevel field: %m");
2926 _cleanup_(json_variant_unrefp
) JsonVariant
*jcur
= NULL
, *jmax
= NULL
;
2927 _cleanup_free_
char *field
= NULL
, *t
= NULL
;
2932 if (isempty(optarg
)) {
2933 /* Remove all resource limits */
2935 r
= drop_from_identity("resourceLimits");
2939 arg_identity_filter_rlimits
= strv_free(arg_identity_filter_rlimits
);
2940 arg_identity_extra_rlimits
= json_variant_unref(arg_identity_extra_rlimits
);
2944 eq
= strchr(optarg
, '=');
2946 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Can't parse resource limit assignment: %s", optarg
);
2948 field
= strndup(optarg
, eq
- optarg
);
2952 l
= rlimit_from_string_harder(field
);
2954 return log_error_errno(l
, "Unknown resource limit type: %s", field
);
2956 if (isempty(eq
+ 1)) {
2957 /* Remove only the specific rlimit */
2959 r
= strv_extend(&arg_identity_filter_rlimits
, rlimit_to_string(l
));
2963 r
= json_variant_filter(&arg_identity_extra_rlimits
, STRV_MAKE(field
));
2965 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2970 r
= rlimit_parse(l
, eq
+ 1, &rl
);
2972 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to parse resource limit value: %s", eq
+ 1);
2974 r
= rl
.rlim_cur
== RLIM_INFINITY
? json_variant_new_null(&jcur
) : json_variant_new_unsigned(&jcur
, rl
.rlim_cur
);
2976 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to allocate current integer: %m");
2978 r
= rl
.rlim_max
== RLIM_INFINITY
? json_variant_new_null(&jmax
) : json_variant_new_unsigned(&jmax
, rl
.rlim_max
);
2980 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to allocate maximum integer: %m");
2982 t
= strjoin("RLIMIT_", rlimit_to_string(l
));
2986 r
= json_variant_set_fieldb(
2987 &arg_identity_extra_rlimits
, t
,
2989 JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur
)),
2990 JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax
))));
2992 return log_error_errno(r
, "Failed to set %s field: %m", rlimit_to_string(l
));
3000 if (isempty(optarg
)) {
3001 r
= drop_from_identity("uid");
3008 r
= parse_uid(optarg
, &uid
);
3010 return log_error_errno(r
, "Failed to parse UID '%s'.", optarg
);
3012 if (uid_is_system(uid
))
3013 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in system range, refusing.", uid
);
3014 if (uid_is_dynamic(uid
))
3015 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in dynamic range, refusing.", uid
);
3016 if (uid
== UID_NOBODY
)
3017 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is nobody UID, refusing.", uid
);
3019 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "uid", uid
);
3021 return log_error_errno(r
, "Failed to set realm field: %m");
3027 case ARG_IMAGE_PATH
: {
3028 const char *field
= c
== 'k' ? "skeletonDirectory" : "imagePath";
3029 _cleanup_free_
char *v
= NULL
;
3031 if (isempty(optarg
)) {
3032 r
= drop_from_identity(field
);
3039 r
= parse_path_argument(optarg
, false, &v
);
3043 r
= json_variant_set_field_string(&arg_identity_extra_this_machine
, field
, v
);
3045 return log_error_errno(r
, "Failed to set %s field: %m", v
);
3051 if (isempty(optarg
)) {
3052 r
= drop_from_identity("shell");
3059 if (!valid_shell(optarg
))
3060 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Shell '%s' not valid.", optarg
);
3062 r
= json_variant_set_field_string(&arg_identity_extra
, "shell", optarg
);
3064 return log_error_errno(r
, "Failed to set shell field: %m");
3069 _cleanup_free_
char **l
= NULL
;
3070 _cleanup_(json_variant_unrefp
) JsonVariant
*ne
= NULL
;
3073 if (isempty(optarg
)) {
3074 r
= drop_from_identity("environment");
3081 e
= json_variant_by_key(arg_identity_extra
, "environment");
3083 r
= json_variant_strv(e
, &l
);
3085 return log_error_errno(r
, "Failed to parse JSON environment field: %m");
3088 r
= strv_env_replace_strdup_passthrough(&l
, optarg
);
3090 return log_error_errno(r
, "Cannot assign environment variable %s: %m", optarg
);
3094 r
= json_variant_new_array_strv(&ne
, l
);
3096 return log_error_errno(r
, "Failed to allocate environment list JSON: %m");
3098 r
= json_variant_set_field(&arg_identity_extra
, "environment", ne
);
3100 return log_error_errno(r
, "Failed to set environment list: %m");
3107 if (isempty(optarg
)) {
3108 r
= drop_from_identity("timeZone");
3115 if (!timezone_is_valid(optarg
, LOG_DEBUG
))
3116 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Timezone '%s' is not valid.", optarg
);
3118 r
= json_variant_set_field_string(&arg_identity_extra
, "timeZone", optarg
);
3120 return log_error_errno(r
, "Failed to set timezone field: %m");
3125 if (isempty(optarg
)) {
3126 r
= drop_from_identity("language");
3133 if (!locale_is_valid(optarg
))
3134 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Locale '%s' is not valid.", optarg
);
3136 if (locale_is_installed(optarg
) <= 0)
3137 log_warning("Locale '%s' is not installed, accepting anyway.", optarg
);
3139 r
= json_variant_set_field_string(&arg_identity_extra
, "preferredLanguage", optarg
);
3141 return log_error_errno(r
, "Failed to set preferredLanguage field: %m");
3149 case ARG_KILL_PROCESSES
:
3150 case ARG_ENFORCE_PASSWORD_POLICY
:
3151 case ARG_AUTO_LOGIN
:
3152 case ARG_PASSWORD_CHANGE_NOW
: {
3154 c
== ARG_LOCKED
? "locked" :
3155 c
== ARG_NOSUID
? "mountNoSuid" :
3156 c
== ARG_NODEV
? "mountNoDevices" :
3157 c
== ARG_NOEXEC
? "mountNoExecute" :
3158 c
== ARG_KILL_PROCESSES
? "killProcesses" :
3159 c
== ARG_ENFORCE_PASSWORD_POLICY
? "enforcePasswordPolicy" :
3160 c
== ARG_AUTO_LOGIN
? "autoLogin" :
3161 c
== ARG_PASSWORD_CHANGE_NOW
? "passwordChangeNow" :
3166 if (isempty(optarg
)) {
3167 r
= drop_from_identity(field
);
3174 r
= parse_boolean(optarg
);
3176 return log_error_errno(r
, "Failed to parse %s boolean: %m", field
);
3178 r
= json_variant_set_field_boolean(&arg_identity_extra
, field
, r
> 0);
3180 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3186 r
= json_variant_set_field_boolean(&arg_identity_extra
, "enforcePasswordPolicy", false);
3188 return log_error_errno(r
, "Failed to set enforcePasswordPolicy field: %m");
3193 if (isempty(optarg
)) {
3194 FOREACH_STRING(prop
, "diskSize", "diskSizeRelative", "rebalanceWeight") {
3195 r
= drop_from_identity(prop
);
3200 arg_disk_size
= arg_disk_size_relative
= UINT64_MAX
;
3204 r
= parse_permyriad(optarg
);
3206 r
= parse_disk_size(optarg
, &arg_disk_size
);
3210 r
= drop_from_identity("diskSizeRelative");
3214 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "diskSize", arg_disk_size
);
3216 return log_error_errno(r
, "Failed to set diskSize field: %m");
3218 arg_disk_size_relative
= UINT64_MAX
;
3220 /* Normalize to UINT32_MAX == 100% */
3221 arg_disk_size_relative
= UINT32_SCALE_FROM_PERMYRIAD(r
);
3223 r
= drop_from_identity("diskSize");
3227 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "diskSizeRelative", arg_disk_size_relative
);
3229 return log_error_errno(r
, "Failed to set diskSizeRelative field: %m");
3231 arg_disk_size
= UINT64_MAX
;
3234 /* Automatically turn off the rebalance logic if user configured a size explicitly */
3235 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "rebalanceWeight", REBALANCE_WEIGHT_OFF
);
3237 return log_error_errno(r
, "Failed to set rebalanceWeight field: %m");
3241 case ARG_ACCESS_MODE
: {
3244 if (isempty(optarg
)) {
3245 r
= drop_from_identity("accessMode");
3252 r
= parse_mode(optarg
, &mode
);
3254 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Access mode '%s' not valid.", optarg
);
3256 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "accessMode", mode
);
3258 return log_error_errno(r
, "Failed to set access mode field: %m");
3263 case ARG_LUKS_DISCARD
:
3264 if (isempty(optarg
)) {
3265 r
= drop_from_identity("luksDiscard");
3272 r
= parse_boolean(optarg
);
3274 return log_error_errno(r
, "Failed to parse --luks-discard= parameter: %s", optarg
);
3276 r
= json_variant_set_field_boolean(&arg_identity_extra
, "luksDiscard", r
);
3278 return log_error_errno(r
, "Failed to set discard field: %m");
3282 case ARG_LUKS_OFFLINE_DISCARD
:
3283 if (isempty(optarg
)) {
3284 r
= drop_from_identity("luksOfflineDiscard");
3291 r
= parse_boolean(optarg
);
3293 return log_error_errno(r
, "Failed to parse --luks-offline-discard= parameter: %s", optarg
);
3295 r
= json_variant_set_field_boolean(&arg_identity_extra
, "luksOfflineDiscard", r
);
3297 return log_error_errno(r
, "Failed to set offline discard field: %m");
3301 case ARG_LUKS_VOLUME_KEY_SIZE
:
3302 case ARG_LUKS_PBKDF_FORCE_ITERATIONS
:
3303 case ARG_LUKS_PBKDF_PARALLEL_THREADS
:
3304 case ARG_RATE_LIMIT_BURST
: {
3306 c
== ARG_LUKS_VOLUME_KEY_SIZE
? "luksVolumeKeySize" :
3307 c
== ARG_LUKS_PBKDF_FORCE_ITERATIONS
? "luksPbkdfForceIterations" :
3308 c
== ARG_LUKS_PBKDF_PARALLEL_THREADS
? "luksPbkdfParallelThreads" :
3309 c
== ARG_RATE_LIMIT_BURST
? "rateLimitBurst" : NULL
;
3314 if (isempty(optarg
)) {
3315 r
= drop_from_identity(field
);
3320 r
= safe_atou(optarg
, &n
);
3322 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
3324 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3326 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3331 case ARG_LUKS_SECTOR_SIZE
: {
3334 if (isempty(optarg
)) {
3335 r
= drop_from_identity("luksSectorSize");
3342 r
= parse_sector_size(optarg
, &ss
);
3346 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "luksSectorSize", ss
);
3348 return log_error_errno(r
, "Failed to set sector size field: %m");
3356 if (isempty(optarg
)) {
3357 r
= drop_from_identity("umask");
3364 r
= parse_mode(optarg
, &m
);
3366 return log_error_errno(r
, "Failed to parse umask: %m");
3368 r
= json_variant_set_field_integer(&arg_identity_extra
, "umask", m
);
3370 return log_error_errno(r
, "Failed to set umask field: %m");
3375 case ARG_SSH_AUTHORIZED_KEYS
: {
3376 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
3377 _cleanup_strv_free_
char **l
= NULL
, **add
= NULL
;
3379 if (isempty(optarg
)) {
3380 r
= drop_from_identity("sshAuthorizedKeys");
3387 if (optarg
[0] == '@') {
3388 _cleanup_fclose_
FILE *f
= NULL
;
3390 /* If prefixed with '@' read from a file */
3392 f
= fopen(optarg
+1, "re");
3394 return log_error_errno(errno
, "Failed to open '%s': %m", optarg
+1);
3397 _cleanup_free_
char *line
= NULL
;
3399 r
= read_line(f
, LONG_LINE_MAX
, &line
);
3401 return log_error_errno(r
, "Failed to read from '%s': %m", optarg
+1);
3411 r
= strv_consume(&add
, TAKE_PTR(line
));
3416 /* Otherwise, assume it's a literal key. Let's do some superficial checks
3417 * before accept it though. */
3419 if (string_has_cc(optarg
, NULL
))
3420 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Authorized key contains control characters, refusing.");
3421 if (optarg
[0] == '#')
3422 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specified key is a comment?");
3424 add
= strv_new(optarg
);
3429 v
= json_variant_ref(json_variant_by_key(arg_identity_extra_privileged
, "sshAuthorizedKeys"));
3431 r
= json_variant_strv(v
, &l
);
3433 return log_error_errno(r
, "Failed to parse SSH authorized keys list: %m");
3436 r
= strv_extend_strv(&l
, add
, true);
3440 v
= json_variant_unref(v
);
3442 r
= json_variant_new_array_strv(&v
, l
);
3446 r
= json_variant_set_field(&arg_identity_extra_privileged
, "sshAuthorizedKeys", v
);
3448 return log_error_errno(r
, "Failed to set authorized keys: %m");
3453 case ARG_NOT_BEFORE
:
3459 field
= c
== ARG_NOT_BEFORE
? "notBeforeUSec" :
3460 IN_SET(c
, ARG_NOT_AFTER
, 'e') ? "notAfterUSec" : NULL
;
3464 if (isempty(optarg
)) {
3465 r
= drop_from_identity(field
);
3472 /* Note the minor discrepancy regarding -e parsing here: we support that for compat
3473 * reasons, and in the original useradd(8) implementation it accepts dates in the
3474 * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
3475 * with greater precision. */
3476 r
= parse_timestamp(optarg
, &n
);
3478 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
3480 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3482 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3486 case ARG_PASSWORD_CHANGE_MIN
:
3487 case ARG_PASSWORD_CHANGE_MAX
:
3488 case ARG_PASSWORD_CHANGE_WARN
:
3489 case ARG_PASSWORD_CHANGE_INACTIVE
: {
3493 field
= c
== ARG_PASSWORD_CHANGE_MIN
? "passwordChangeMinUSec" :
3494 c
== ARG_PASSWORD_CHANGE_MAX
? "passwordChangeMaxUSec" :
3495 c
== ARG_PASSWORD_CHANGE_WARN
? "passwordChangeWarnUSec" :
3496 c
== ARG_PASSWORD_CHANGE_INACTIVE
? "passwordChangeInactiveUSec" :
3501 if (isempty(optarg
)) {
3502 r
= drop_from_identity(field
);
3509 r
= parse_sec(optarg
, &n
);
3511 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
3513 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3515 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3521 case ARG_LUKS_CIPHER
:
3522 case ARG_LUKS_CIPHER_MODE
:
3523 case ARG_LUKS_PBKDF_TYPE
:
3524 case ARG_LUKS_PBKDF_HASH_ALGORITHM
: {
3527 c
== ARG_STORAGE
? "storage" :
3528 c
== ARG_FS_TYPE
? "fileSystemType" :
3529 c
== ARG_LUKS_CIPHER
? "luksCipher" :
3530 c
== ARG_LUKS_CIPHER_MODE
? "luksCipherMode" :
3531 c
== ARG_LUKS_PBKDF_TYPE
? "luksPbkdfType" :
3532 c
== ARG_LUKS_PBKDF_HASH_ALGORITHM
? "luksPbkdfHashAlgorithm" : NULL
;
3536 if (isempty(optarg
)) {
3537 r
= drop_from_identity(field
);
3544 if (!string_is_safe(optarg
))
3545 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Parameter for %s field not valid: %s", field
, optarg
);
3547 r
= json_variant_set_field_string(
3548 IN_SET(c
, ARG_STORAGE
, ARG_FS_TYPE
) ?
3549 &arg_identity_extra_this_machine
:
3550 &arg_identity_extra
, field
, optarg
);
3552 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3557 case ARG_LUKS_PBKDF_TIME_COST
:
3558 case ARG_RATE_LIMIT_INTERVAL
:
3559 case ARG_STOP_DELAY
: {
3561 c
== ARG_LUKS_PBKDF_TIME_COST
? "luksPbkdfTimeCostUSec" :
3562 c
== ARG_RATE_LIMIT_INTERVAL
? "rateLimitIntervalUSec" :
3563 c
== ARG_STOP_DELAY
? "stopDelayUSec" :
3569 if (isempty(optarg
)) {
3570 r
= drop_from_identity(field
);
3577 r
= parse_sec(optarg
, &t
);
3579 return log_error_errno(r
, "Failed to parse %s field: %s", field
, optarg
);
3581 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, t
);
3583 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3589 const char *p
= optarg
;
3592 r
= drop_from_identity("memberOf");
3600 _cleanup_(json_variant_unrefp
) JsonVariant
*mo
= NULL
;
3601 _cleanup_strv_free_
char **list
= NULL
;
3602 _cleanup_free_
char *word
= NULL
;
3604 r
= extract_first_word(&p
, &word
, ",", 0);
3606 return log_error_errno(r
, "Failed to parse group list: %m");
3610 if (!valid_user_group_name(word
, 0))
3611 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid group name %s.", word
);
3613 mo
= json_variant_ref(json_variant_by_key(arg_identity_extra
, "memberOf"));
3615 r
= json_variant_strv(mo
, &list
);
3617 return log_error_errno(r
, "Failed to parse group list: %m");
3619 r
= strv_extend(&list
, word
);
3626 mo
= json_variant_unref(mo
);
3627 r
= json_variant_new_array_strv(&mo
, list
);
3629 return log_error_errno(r
, "Failed to create group list JSON: %m");
3631 r
= json_variant_set_field(&arg_identity_extra
, "memberOf", mo
);
3633 return log_error_errno(r
, "Failed to update group list: %m");
3639 case ARG_TASKS_MAX
: {
3642 if (isempty(optarg
)) {
3643 r
= drop_from_identity("tasksMax");
3649 r
= safe_atou64(optarg
, &u
);
3651 return log_error_errno(r
, "Failed to parse --tasks-max= parameter: %s", optarg
);
3653 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "tasksMax", u
);
3655 return log_error_errno(r
, "Failed to set tasksMax field: %m");
3660 case ARG_MEMORY_MAX
:
3661 case ARG_MEMORY_HIGH
:
3662 case ARG_LUKS_PBKDF_MEMORY_COST
: {
3664 c
== ARG_MEMORY_MAX
? "memoryMax" :
3665 c
== ARG_MEMORY_HIGH
? "memoryHigh" :
3666 c
== ARG_LUKS_PBKDF_MEMORY_COST
? "luksPbkdfMemoryCost" : NULL
;
3672 if (isempty(optarg
)) {
3673 r
= drop_from_identity(field
);
3679 r
= parse_size(optarg
, 1024, &u
);
3681 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
3683 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, field
, u
);
3685 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3690 case ARG_CPU_WEIGHT
:
3691 case ARG_IO_WEIGHT
: {
3692 const char *field
= c
== ARG_CPU_WEIGHT
? "cpuWeight" :
3693 c
== ARG_IO_WEIGHT
? "ioWeight" : NULL
;
3698 if (isempty(optarg
)) {
3699 r
= drop_from_identity(field
);
3705 r
= safe_atou64(optarg
, &u
);
3707 return log_error_errno(r
, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg
);
3709 if (!CGROUP_WEIGHT_IS_OK(u
))
3710 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Weight %" PRIu64
" is out of valid weight range.", u
);
3712 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, u
);
3714 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3719 case ARG_PKCS11_TOKEN_URI
:
3720 if (streq(optarg
, "list"))
3721 return pkcs11_list_tokens();
3723 /* If --pkcs11-token-uri= is specified we always drop everything old */
3724 FOREACH_STRING(p
, "pkcs11TokenUri", "pkcs11EncryptedKey") {
3725 r
= drop_from_identity(p
);
3730 if (isempty(optarg
)) {
3731 arg_pkcs11_token_uri
= strv_free(arg_pkcs11_token_uri
);
3735 if (streq(optarg
, "auto")) {
3736 _cleanup_free_
char *found
= NULL
;
3738 r
= pkcs11_find_token_auto(&found
);
3741 r
= strv_consume(&arg_pkcs11_token_uri
, TAKE_PTR(found
));
3743 if (!pkcs11_uri_valid(optarg
))
3744 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Not a valid PKCS#11 URI: %s", optarg
);
3746 r
= strv_extend(&arg_pkcs11_token_uri
, optarg
);
3751 strv_uniq(arg_pkcs11_token_uri
);
3754 case ARG_FIDO2_CRED_ALG
:
3755 r
= parse_fido2_algorithm(optarg
, &arg_fido2_cred_alg
);
3757 return log_error_errno(r
, "Failed to parse COSE algorithm: %s", optarg
);
3760 case ARG_FIDO2_DEVICE
:
3761 if (streq(optarg
, "list"))
3762 return fido2_list_devices();
3764 FOREACH_STRING(p
, "fido2HmacCredential", "fido2HmacSalt") {
3765 r
= drop_from_identity(p
);
3770 if (isempty(optarg
)) {
3771 arg_fido2_device
= strv_free(arg_fido2_device
);
3775 if (streq(optarg
, "auto")) {
3776 _cleanup_free_
char *found
= NULL
;
3778 r
= fido2_find_device_auto(&found
);
3782 r
= strv_consume(&arg_fido2_device
, TAKE_PTR(found
));
3784 r
= strv_extend(&arg_fido2_device
, optarg
);
3788 strv_uniq(arg_fido2_device
);
3791 case ARG_FIDO2_WITH_PIN
:
3792 r
= parse_boolean_argument("--fido2-with-client-pin=", optarg
, NULL
);
3796 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_PIN
, r
);
3799 case ARG_FIDO2_WITH_UP
:
3800 r
= parse_boolean_argument("--fido2-with-user-presence=", optarg
, NULL
);
3804 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UP
, r
);
3807 case ARG_FIDO2_WITH_UV
:
3808 r
= parse_boolean_argument("--fido2-with-user-verification=", optarg
, NULL
);
3812 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UV
, r
);
3815 case ARG_RECOVERY_KEY
:
3816 r
= parse_boolean(optarg
);
3818 return log_error_errno(r
, "Failed to parse --recovery-key= argument: %s", optarg
);
3820 arg_recovery_key
= r
;
3822 FOREACH_STRING(p
, "recoveryKey", "recoveryKeyType") {
3823 r
= drop_from_identity(p
);
3830 case ARG_AUTO_RESIZE_MODE
:
3831 if (isempty(optarg
)) {
3832 r
= drop_from_identity("autoResizeMode");
3839 r
= auto_resize_mode_from_string(optarg
);
3841 return log_error_errno(r
, "Failed to parse --auto-resize-mode= argument: %s", optarg
);
3843 r
= json_variant_set_field_string(&arg_identity_extra
, "autoResizeMode", auto_resize_mode_to_string(r
));
3845 return log_error_errno(r
, "Failed to set autoResizeMode field: %m");
3849 case ARG_REBALANCE_WEIGHT
: {
3852 if (isempty(optarg
)) {
3853 r
= drop_from_identity("rebalanceWeight");
3859 if (streq(optarg
, "off"))
3860 u
= REBALANCE_WEIGHT_OFF
;
3862 r
= safe_atou64(optarg
, &u
);
3864 return log_error_errno(r
, "Failed to parse --rebalance-weight= argument: %s", optarg
);
3866 if (u
< REBALANCE_WEIGHT_MIN
|| u
> REBALANCE_WEIGHT_MAX
)
3867 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
), "Rebalancing weight out of valid range %" PRIu64
"%s%" PRIu64
": %s",
3868 REBALANCE_WEIGHT_MIN
, special_glyph(SPECIAL_GLYPH_ELLIPSIS
), REBALANCE_WEIGHT_MAX
, optarg
);
3871 /* Drop from per machine stuff and everywhere */
3872 r
= drop_from_identity("rebalanceWeight");
3876 /* Add to main identity */
3877 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "rebalanceWeight", u
);
3879 return log_error_errno(r
, "Failed to set rebalanceWeight field: %m");
3885 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
3889 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
3896 if (arg_export_format
== EXPORT_FORMAT_FULL
)
3897 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
3898 else if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
3899 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
3901 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specifying -E more than twice is not supported.");
3903 arg_json_format_flags
&= ~JSON_FORMAT_OFF
;
3904 if (arg_json_format_flags
== 0)
3905 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
3908 case ARG_EXPORT_FORMAT
:
3909 if (streq(optarg
, "full"))
3910 arg_export_format
= EXPORT_FORMAT_FULL
;
3911 else if (streq(optarg
, "stripped"))
3912 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
3913 else if (streq(optarg
, "minimal"))
3914 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
3915 else if (streq(optarg
, "help")) {
3924 case ARG_AND_RESIZE
:
3925 arg_and_resize
= true;
3928 case ARG_AND_CHANGE_PASSWORD
:
3929 arg_and_change_password
= true;
3932 case ARG_DROP_CACHES
: {
3933 if (isempty(optarg
)) {
3934 r
= drop_from_identity("dropCaches");
3940 r
= parse_boolean_argument("--drop-caches=", optarg
, NULL
);
3944 r
= json_variant_set_field_boolean(&arg_identity_extra
, "dropCaches", r
);
3946 return log_error_errno(r
, "Failed to set drop caches field: %m");
3951 case ARG_CAPABILITY_AMBIENT_SET
:
3952 case ARG_CAPABILITY_BOUNDING_SET
: {
3953 _cleanup_strv_free_
char **l
= NULL
;
3954 bool subtract
= false;
3955 uint64_t parsed
, *which
, updated
;
3956 const char *p
, *field
;
3958 if (c
== ARG_CAPABILITY_AMBIENT_SET
) {
3959 which
= &arg_capability_ambient_set
;
3960 field
= "capabilityAmbientSet";
3962 assert(c
== ARG_CAPABILITY_BOUNDING_SET
);
3963 which
= &arg_capability_bounding_set
;
3964 field
= "capabilityBoundingSet";
3967 if (isempty(optarg
)) {
3968 r
= drop_from_identity(field
);
3972 *which
= UINT64_MAX
;
3982 r
= capability_set_from_string(p
, &parsed
);
3984 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid capabilities in capability string '%s'.", p
);
3986 return log_error_errno(r
, "Failed to parse capability string '%s': %m", p
);
3988 if (*which
== UINT64_MAX
)
3989 updated
= subtract
? all_capabilities() & ~parsed
: parsed
;
3991 updated
= *which
& ~parsed
;
3993 updated
= *which
| parsed
;
3995 if (capability_set_to_strv(updated
, &l
) < 0)
3998 r
= json_variant_set_field_strv(&arg_identity_extra
, field
, l
);
4000 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4006 case ARG_PROMPT_NEW_USER
:
4007 arg_prompt_new_user
= true;
4014 assert_not_reached();
4018 if (!strv_isempty(arg_pkcs11_token_uri
) || !strv_isempty(arg_fido2_device
))
4019 arg_and_change_password
= true;
4021 if (arg_disk_size
!= UINT64_MAX
|| arg_disk_size_relative
!= UINT64_MAX
)
4022 arg_and_resize
= true;
4027 static int redirect_bus_mgr(void) {
4030 /* Talk to a different service if that's requested. (The same env var is also understood by homed, so
4031 * that it is relatively easily possible to invoke a second instance of homed for debug purposes and
4032 * have homectl talk to it, without colliding with the host version. This is handy when operating
4033 * from a homed-managed account.) */
4035 suffix
= getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
4037 static BusLocator locator
= {
4038 .path
= "/org/freedesktop/home1",
4039 .interface
= "org.freedesktop.home1.Manager",
4042 /* Yes, we leak this memory, but there's little point to collect this, given that we only do
4043 * this in a debug environment, do it only once, and the string shall live for out entire
4044 * process runtime. */
4046 locator
.destination
= strjoin("org.freedesktop.home1.", suffix
);
4047 if (!locator
.destination
)
4052 bus_mgr
= bus_home_mgr
;
4057 static int run(int argc
, char *argv
[]) {
4058 static const Verb verbs
[] = {
4059 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
4060 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_homes
},
4061 { "activate", 2, VERB_ANY
, 0, activate_home
},
4062 { "deactivate", 2, VERB_ANY
, 0, deactivate_home
},
4063 { "inspect", VERB_ANY
, VERB_ANY
, 0, inspect_home
},
4064 { "authenticate", VERB_ANY
, VERB_ANY
, 0, authenticate_home
},
4065 { "create", VERB_ANY
, 2, 0, create_home
},
4066 { "remove", 2, VERB_ANY
, 0, remove_home
},
4067 { "update", VERB_ANY
, 2, 0, update_home
},
4068 { "passwd", VERB_ANY
, 2, 0, passwd_home
},
4069 { "resize", 2, 3, 0, resize_home
},
4070 { "lock", 2, VERB_ANY
, 0, lock_home
},
4071 { "unlock", 2, VERB_ANY
, 0, unlock_home
},
4072 { "with", 2, VERB_ANY
, 0, with_home
},
4073 { "lock-all", VERB_ANY
, 1, 0, lock_all_homes
},
4074 { "deactivate-all", VERB_ANY
, 1, 0, deactivate_all_homes
},
4075 { "rebalance", VERB_ANY
, 1, 0, rebalance
},
4076 { "firstboot", VERB_ANY
, 1, 0, verb_firstboot
},
4084 r
= redirect_bus_mgr();
4088 r
= parse_argv(argc
, argv
);
4092 return dispatch_verb(argc
, argv
, verbs
, NULL
);
4095 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);