1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "ask-password-api.h"
8 #include "bus-common-errors.h"
10 #include "bus-locator.h"
11 #include "cgroup-util.h"
12 #include "dns-domain.h"
16 #include "format-table.h"
18 #include "glyph-util.h"
19 #include "home-util.h"
20 #include "homectl-fido2.h"
21 #include "homectl-pkcs11.h"
22 #include "homectl-recovery-key.h"
23 #include "libfido2-util.h"
24 #include "locale-util.h"
25 #include "main-func.h"
26 #include "memory-util.h"
28 #include "parse-argument.h"
29 #include "parse-util.h"
30 #include "path-util.h"
31 #include "percent-util.h"
32 #include "pkcs11-util.h"
33 #include "pretty-print.h"
34 #include "process-util.h"
35 #include "pwquality-util.h"
36 #include "rlimit-util.h"
37 #include "spawn-polkit-agent.h"
38 #include "terminal-util.h"
39 #include "uid-alloc-range.h"
40 #include "user-record-pwquality.h"
41 #include "user-record-show.h"
42 #include "user-record-util.h"
43 #include "user-record.h"
44 #include "user-util.h"
47 static PagerFlags arg_pager_flags
= 0;
48 static bool arg_legend
= true;
49 static bool arg_ask_password
= true;
50 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
51 static const char *arg_host
= NULL
;
52 static const char *arg_identity
= NULL
;
53 static JsonVariant
*arg_identity_extra
= NULL
;
54 static JsonVariant
*arg_identity_extra_privileged
= NULL
;
55 static JsonVariant
*arg_identity_extra_this_machine
= NULL
;
56 static JsonVariant
*arg_identity_extra_rlimits
= NULL
;
57 static char **arg_identity_filter
= NULL
; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
58 static char **arg_identity_filter_rlimits
= NULL
;
59 static uint64_t arg_disk_size
= UINT64_MAX
;
60 static uint64_t arg_disk_size_relative
= UINT64_MAX
;
61 static char **arg_pkcs11_token_uri
= NULL
;
62 static char **arg_fido2_device
= NULL
;
63 static Fido2EnrollFlags arg_fido2_lock_with
= FIDO2ENROLL_PIN
| FIDO2ENROLL_UP
;
64 static bool arg_recovery_key
= false;
65 static JsonFormatFlags arg_json_format_flags
= JSON_FORMAT_OFF
;
66 static bool arg_and_resize
= false;
67 static bool arg_and_change_password
= false;
69 EXPORT_FORMAT_FULL
, /* export the full record */
70 EXPORT_FORMAT_STRIPPED
, /* strip "state" + "binding", but leave signature in place */
71 EXPORT_FORMAT_MINIMAL
, /* also strip signature */
72 } arg_export_format
= EXPORT_FORMAT_FULL
;
74 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra
, json_variant_unrefp
);
75 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine
, json_variant_unrefp
);
76 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged
, json_variant_unrefp
);
77 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits
, json_variant_unrefp
);
78 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter
, strv_freep
);
79 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits
, strv_freep
);
80 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri
, strv_freep
);
81 STATIC_DESTRUCTOR_REGISTER(arg_fido2_device
, strv_freep
);
83 static const BusLocator
*bus_mgr
;
85 static bool identity_properties_specified(void) {
88 !json_variant_is_blank_object(arg_identity_extra
) ||
89 !json_variant_is_blank_object(arg_identity_extra_privileged
) ||
90 !json_variant_is_blank_object(arg_identity_extra_this_machine
) ||
91 !json_variant_is_blank_object(arg_identity_extra_rlimits
) ||
92 !strv_isempty(arg_identity_filter
) ||
93 !strv_isempty(arg_identity_filter_rlimits
) ||
94 !strv_isempty(arg_pkcs11_token_uri
) ||
95 !strv_isempty(arg_fido2_device
);
98 static int acquire_bus(sd_bus
**bus
) {
106 r
= bus_connect_transport(arg_transport
, arg_host
, false, bus
);
108 return bus_log_connect_error(r
, arg_transport
);
110 (void) sd_bus_set_allow_interactive_authorization(*bus
, arg_ask_password
);
115 static int list_homes(int argc
, char *argv
[], void *userdata
) {
116 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
117 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
118 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
119 _cleanup_(table_unrefp
) Table
*table
= NULL
;
122 r
= acquire_bus(&bus
);
126 r
= bus_call_method(bus
, bus_mgr
, "ListHomes", &error
, &reply
, NULL
);
128 return log_error_errno(r
, "Failed to list homes: %s", bus_error_message(&error
, r
));
130 table
= table_new("name", "uid", "gid", "state", "realname", "home", "shell");
134 r
= sd_bus_message_enter_container(reply
, 'a', "(susussso)");
136 return bus_log_parse_error(r
);
139 const char *name
, *state
, *realname
, *home
, *shell
, *color
;
143 r
= sd_bus_message_read(reply
, "(susussso)", &name
, &uid
, &state
, &gid
, &realname
, &home
, &shell
, NULL
);
145 return bus_log_parse_error(r
);
149 r
= table_add_many(table
,
154 return table_log_add_error(r
);
157 r
= table_add_cell(table
, &cell
, TABLE_STRING
, state
);
159 return table_log_add_error(r
);
161 color
= user_record_state_color(state
);
163 (void) table_set_color(table
, cell
, color
);
165 r
= table_add_many(table
,
166 TABLE_STRING
, strna(empty_to_null(realname
)),
168 TABLE_STRING
, strna(empty_to_null(shell
)));
170 return table_log_add_error(r
);
173 r
= sd_bus_message_exit_container(reply
);
175 return bus_log_parse_error(r
);
177 if (table_get_rows(table
) > 1 || !FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
178 r
= table_set_sort(table
, (size_t) 0);
180 return table_log_sort_error(r
);
182 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, arg_legend
);
187 if (arg_legend
&& (arg_json_format_flags
& JSON_FORMAT_OFF
)) {
188 if (table_get_rows(table
) > 1)
189 printf("\n%zu home areas listed.\n", table_get_rows(table
) - 1);
191 printf("No home areas.\n");
197 static int acquire_existing_password(
198 const char *user_name
,
200 bool emphasize_current
,
201 AskPasswordFlags flags
) {
203 _cleanup_(strv_free_erasep
) char **password
= NULL
;
204 _cleanup_free_
char *question
= NULL
;
211 e
= getenv("PASSWORD");
213 /* People really shouldn't use environment variables for passing passwords. We support this
214 * only for testing purposes, and do not document the behaviour, so that people won't
215 * actually use this outside of testing. */
217 r
= user_record_set_password(hr
, STRV_MAKE(e
), true);
219 return log_error_errno(r
, "Failed to store password: %m");
221 assert_se(unsetenv_erase("PASSWORD") >= 0);
225 /* If this is not our own user, then don't use the password cache */
226 if (is_this_me(user_name
) <= 0)
227 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
229 if (asprintf(&question
, emphasize_current
?
230 "Please enter current password for user %s:" :
231 "Please enter password for user %s:",
235 r
= ask_password_auto(question
,
236 /* icon= */ "user-home",
238 /* key_name= */ "home-password",
239 /* credential_name= */ "home.password",
243 if (r
== -EUNATCH
) { /* EUNATCH is returned if no password was found and asking interactively was
244 * disabled via the flags. Not an error for us. */
245 log_debug_errno(r
, "No passwords acquired.");
249 return log_error_errno(r
, "Failed to acquire password: %m");
251 r
= user_record_set_password(hr
, password
, true);
253 return log_error_errno(r
, "Failed to store password: %m");
258 static int acquire_recovery_key(
259 const char *user_name
,
261 AskPasswordFlags flags
) {
263 _cleanup_(strv_free_erasep
) char **recovery_key
= NULL
;
264 _cleanup_free_
char *question
= NULL
;
271 e
= getenv("RECOVERY_KEY");
273 /* People really shouldn't use environment variables for passing secrets. We support this
274 * only for testing purposes, and do not document the behaviour, so that people won't
275 * actually use this outside of testing. */
277 r
= user_record_set_password(hr
, STRV_MAKE(e
), true); /* recovery keys are stored in the record exactly like regular passwords! */
279 return log_error_errno(r
, "Failed to store recovery key: %m");
281 assert_se(unsetenv_erase("RECOVERY_KEY") >= 0);
285 /* If this is not our own user, then don't use the password cache */
286 if (is_this_me(user_name
) <= 0)
287 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
289 if (asprintf(&question
, "Please enter recovery key for user %s:", user_name
) < 0)
292 r
= ask_password_auto(question
,
293 /* icon= */ "user-home",
295 /* key_name= */ "home-recovery-key",
296 /* credential_name= */ "home.recovery-key",
300 if (r
== -EUNATCH
) { /* EUNATCH is returned if no recovery key was found and asking interactively was
301 * disabled via the flags. Not an error for us. */
302 log_debug_errno(r
, "No recovery keys acquired.");
306 return log_error_errno(r
, "Failed to acquire recovery keys: %m");
308 r
= user_record_set_password(hr
, recovery_key
, true);
310 return log_error_errno(r
, "Failed to store recovery keys: %m");
315 static int acquire_token_pin(
316 const char *user_name
,
318 AskPasswordFlags flags
) {
320 _cleanup_(strv_free_erasep
) char **pin
= NULL
;
321 _cleanup_free_
char *question
= NULL
;
330 r
= user_record_set_token_pin(hr
, STRV_MAKE(e
), false);
332 return log_error_errno(r
, "Failed to store token PIN: %m");
334 assert_se(unsetenv_erase("PIN") >= 0);
338 /* If this is not our own user, then don't use the password cache */
339 if (is_this_me(user_name
) <= 0)
340 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
342 if (asprintf(&question
, "Please enter security token PIN for user %s:", user_name
) < 0)
345 r
= ask_password_auto(
347 /* icon= */ "user-home",
349 /* key_name= */ "token-pin",
350 /* credential_name= */ "home.token-pin",
354 if (r
== -EUNATCH
) { /* EUNATCH is returned if no PIN was found and asking interactively was disabled
355 * via the flags. Not an error for us. */
356 log_debug_errno(r
, "No security token PINs acquired.");
360 return log_error_errno(r
, "Failed to acquire security token PIN: %m");
362 r
= user_record_set_token_pin(hr
, pin
, false);
364 return log_error_errno(r
, "Failed to store security token PIN: %m");
369 static int handle_generic_user_record_error(
370 const char *user_name
,
372 const sd_bus_error
*error
,
374 bool emphasize_current_password
) {
380 if (sd_bus_error_has_name(error
, BUS_ERROR_HOME_ABSENT
))
381 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE
),
382 "Home of user %s is currently absent, please plug in the necessary storage device or backing file system.", user_name
);
384 else if (sd_bus_error_has_name(error
, BUS_ERROR_AUTHENTICATION_LIMIT_HIT
))
385 return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS
),
386 "Too frequent unsuccessful login attempts for user %s, try again later.", user_name
);
388 else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD
)) {
390 if (!strv_isempty(hr
->password
))
391 log_notice("Password incorrect or not sufficient, please try again.");
393 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
394 * let's push what we acquire here into the cache */
395 r
= acquire_existing_password(
398 emphasize_current_password
,
399 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
403 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_RECOVERY_KEY
)) {
405 if (!strv_isempty(hr
->password
))
406 log_notice("Recovery key incorrect or not sufficient, please try again.");
408 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
409 * let's push what we acquire here into the cache */
410 r
= acquire_recovery_key(
413 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
417 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
)) {
419 if (strv_isempty(hr
->password
))
420 log_notice("Security token not inserted, please enter password.");
422 log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
424 r
= acquire_existing_password(
427 emphasize_current_password
,
428 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
432 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_NEEDED
)) {
434 /* First time the PIN is requested, let's accept cached data, and allow using credential store */
435 r
= acquire_token_pin(
438 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_PUSH_CACHE
);
442 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED
)) {
444 log_notice("%s%sPlease authenticate physically on security token.",
445 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
446 emoji_enabled() ? " " : "");
448 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, true);
450 return log_error_errno(r
, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
452 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED
)) {
454 log_notice("%s%sPlease confirm presence on security token.",
455 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
456 emoji_enabled() ? " " : "");
458 r
= user_record_set_fido2_user_presence_permitted(hr
, true);
460 return log_error_errno(r
, "Failed to set FIDO2 user presence permitted flag: %m");
462 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED
)) {
464 log_notice("%s%sPlease verify user on security token.",
465 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
466 emoji_enabled() ? " " : "");
468 r
= user_record_set_fido2_user_verification_permitted(hr
, true);
470 return log_error_errno(r
, "Failed to set FIDO2 user verification permitted flag: %m");
472 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_LOCKED
))
473 return log_error_errno(SYNTHETIC_ERRNO(EPERM
), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
475 else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN
)) {
477 log_notice("Security token PIN incorrect, please try again.");
479 /* If the previous PIN was wrong don't accept cached info anymore, but add to cache. Also, don't use the credential data */
480 r
= acquire_token_pin(
483 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
487 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT
)) {
489 log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
491 r
= acquire_token_pin(
494 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
498 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT
)) {
500 log_notice("Security token PIN incorrect, please try again (only one try left!).");
502 r
= acquire_token_pin(
505 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
509 return log_error_errno(ret
, "Operation on home %s failed: %s", user_name
, bus_error_message(error
, ret
));
514 static int acquire_passed_secrets(const char *user_name
, UserRecord
**ret
) {
515 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
520 /* Generates an initial secret objects that contains passwords supplied via $PASSWORD, the password
521 * cache or the credentials subsystem, but excluding any interactive stuff. If nothing is passed,
522 * returns an empty secret object. */
524 secret
= user_record_new();
528 r
= acquire_existing_password(
531 /* emphasize_current_password = */ false,
532 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
536 r
= acquire_token_pin(
539 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
543 r
= acquire_recovery_key(
546 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
550 *ret
= TAKE_PTR(secret
);
554 static int activate_home(int argc
, char *argv
[], void *userdata
) {
555 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
559 r
= acquire_bus(&bus
);
563 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
564 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
566 r
= acquire_passed_secrets(*i
, &secret
);
571 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
572 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
574 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ActivateHome");
576 return bus_log_create_error(r
);
578 r
= sd_bus_message_append(m
, "s", *i
);
580 return bus_log_create_error(r
);
582 r
= bus_message_append_secret(m
, secret
);
584 return bus_log_create_error(r
);
586 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
588 r
= handle_generic_user_record_error(*i
, secret
, &error
, r
, /* emphasize_current_password= */ false);
603 static int deactivate_home(int argc
, char *argv
[], void *userdata
) {
604 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
608 r
= acquire_bus(&bus
);
612 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
613 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
614 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
616 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "DeactivateHome");
618 return bus_log_create_error(r
);
620 r
= sd_bus_message_append(m
, "s", *i
);
622 return bus_log_create_error(r
);
624 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
626 log_error_errno(r
, "Failed to deactivate user home: %s", bus_error_message(&error
, r
));
635 static void dump_home_record(UserRecord
*hr
) {
640 if (hr
->incomplete
) {
642 log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr
->user_name
);
645 if (arg_json_format_flags
& JSON_FORMAT_OFF
)
646 user_record_show(hr
, true);
648 _cleanup_(user_record_unrefp
) UserRecord
*stripped
= NULL
;
650 if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
651 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_EMBEDDED
|USER_RECORD_PERMISSIVE
, &stripped
);
652 else if (arg_export_format
== EXPORT_FORMAT_MINIMAL
)
653 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_SIGNABLE
|USER_RECORD_PERMISSIVE
, &stripped
);
657 log_warning_errno(r
, "Failed to strip user record, ignoring: %m");
661 json_variant_dump(hr
->json
, arg_json_format_flags
, stdout
, NULL
);
665 static char **mangle_user_list(char **list
, char ***ret_allocated
) {
666 _cleanup_free_
char *myself
= NULL
;
669 if (!strv_isempty(list
)) {
670 *ret_allocated
= NULL
;
674 myself
= getusername_malloc();
682 l
[0] = TAKE_PTR(myself
);
689 static int inspect_home(int argc
, char *argv
[], void *userdata
) {
690 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
691 _cleanup_(strv_freep
) char **mangled_list
= NULL
;
695 pager_open(arg_pager_flags
);
697 r
= acquire_bus(&bus
);
701 items
= mangle_user_list(strv_skip(argv
, 1), &mangled_list
);
705 STRV_FOREACH(i
, items
) {
706 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
707 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
708 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
709 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
714 r
= parse_uid(*i
, &uid
);
716 if (!valid_user_group_name(*i
, 0)) {
717 log_error("Invalid user name '%s'.", *i
);
724 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByName", &error
, &reply
, "s", *i
);
726 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByUID", &error
, &reply
, "u", (uint32_t) uid
);
729 log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
736 r
= sd_bus_message_read(reply
, "sbo", &json
, &incomplete
, NULL
);
738 bus_log_parse_error(r
);
745 r
= json_parse(json
, JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
747 log_error_errno(r
, "Failed to parse JSON identity: %m");
754 hr
= user_record_new();
758 r
= user_record_load(hr
, v
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_LOG
|USER_RECORD_PERMISSIVE
);
766 hr
->incomplete
= incomplete
;
767 dump_home_record(hr
);
773 static int authenticate_home(int argc
, char *argv
[], void *userdata
) {
774 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
775 _cleanup_(strv_freep
) char **mangled_list
= NULL
;
779 items
= mangle_user_list(strv_skip(argv
, 1), &mangled_list
);
783 r
= acquire_bus(&bus
);
787 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
789 STRV_FOREACH(i
, items
) {
790 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
792 r
= acquire_passed_secrets(*i
, &secret
);
797 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
798 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
800 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "AuthenticateHome");
802 return bus_log_create_error(r
);
804 r
= sd_bus_message_append(m
, "s", *i
);
806 return bus_log_create_error(r
);
808 r
= bus_message_append_secret(m
, secret
);
810 return bus_log_create_error(r
);
812 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
814 r
= handle_generic_user_record_error(*i
, secret
, &error
, r
, false);
829 static int update_last_change(JsonVariant
**v
, bool with_password
, bool override
) {
836 n
= now(CLOCK_REALTIME
);
838 c
= json_variant_by_key(*v
, "lastChangeUSec");
843 goto update_password
;
845 if (!json_variant_is_unsigned(c
))
846 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec field is not an unsigned integer, refusing.");
848 u
= json_variant_unsigned(c
);
850 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec is from the future, can't update.");
853 r
= json_variant_set_field_unsigned(v
, "lastChangeUSec", n
);
855 return log_error_errno(r
, "Failed to update lastChangeUSec: %m");
861 c
= json_variant_by_key(*v
, "lastPasswordChangeUSec");
868 if (!json_variant_is_unsigned(c
))
869 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec field is not an unsigned integer, refusing.");
871 u
= json_variant_unsigned(c
);
873 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec is from the future, can't update.");
876 r
= json_variant_set_field_unsigned(v
, "lastPasswordChangeUSec", n
);
878 return log_error_errno(r
, "Failed to update lastPasswordChangeUSec: %m");
883 static int apply_identity_changes(JsonVariant
**_v
) {
884 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
889 v
= json_variant_ref(*_v
);
891 r
= json_variant_filter(&v
, arg_identity_filter
);
893 return log_error_errno(r
, "Failed to filter identity: %m");
895 r
= json_variant_merge(&v
, arg_identity_extra
);
897 return log_error_errno(r
, "Failed to merge identities: %m");
899 if (arg_identity_extra_this_machine
|| !strv_isempty(arg_identity_filter
)) {
900 _cleanup_(json_variant_unrefp
) JsonVariant
*per_machine
= NULL
, *mmid
= NULL
;
903 r
= sd_id128_get_machine(&mid
);
905 return log_error_errno(r
, "Failed to acquire machine ID: %m");
907 r
= json_variant_new_string(&mmid
, SD_ID128_TO_STRING(mid
));
909 return log_error_errno(r
, "Failed to allocate matchMachineId object: %m");
911 per_machine
= json_variant_ref(json_variant_by_key(v
, "perMachine"));
913 _cleanup_(json_variant_unrefp
) JsonVariant
*npm
= NULL
, *add
= NULL
;
914 _cleanup_free_ JsonVariant
**array
= NULL
;
918 if (!json_variant_is_array(per_machine
))
919 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine field is not an array, refusing.");
921 array
= new(JsonVariant
*, json_variant_elements(per_machine
) + 1);
925 JSON_VARIANT_ARRAY_FOREACH(z
, per_machine
) {
928 if (!json_variant_is_object(z
))
929 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine entry is not an object, refusing.");
933 u
= json_variant_by_key(z
, "matchMachineId");
937 if (!json_variant_equal(u
, mmid
))
940 r
= json_variant_merge(&add
, z
);
942 return log_error_errno(r
, "Failed to merge perMachine entry: %m");
947 r
= json_variant_filter(&add
, arg_identity_filter
);
949 return log_error_errno(r
, "Failed to filter perMachine: %m");
951 r
= json_variant_merge(&add
, arg_identity_extra_this_machine
);
953 return log_error_errno(r
, "Failed to merge in perMachine fields: %m");
955 if (arg_identity_filter_rlimits
|| arg_identity_extra_rlimits
) {
956 _cleanup_(json_variant_unrefp
) JsonVariant
*rlv
= NULL
;
958 rlv
= json_variant_ref(json_variant_by_key(add
, "resourceLimits"));
960 r
= json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
962 return log_error_errno(r
, "Failed to filter resource limits: %m");
964 r
= json_variant_merge(&rlv
, arg_identity_extra_rlimits
);
966 return log_error_errno(r
, "Failed to set resource limits: %m");
968 if (json_variant_is_blank_object(rlv
)) {
969 r
= json_variant_filter(&add
, STRV_MAKE("resourceLimits"));
971 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
973 r
= json_variant_set_field(&add
, "resourceLimits", rlv
);
975 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
979 if (!json_variant_is_blank_object(add
)) {
980 r
= json_variant_set_field(&add
, "matchMachineId", mmid
);
982 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
987 r
= json_variant_new_array(&npm
, array
, i
);
989 return log_error_errno(r
, "Failed to allocate new perMachine array: %m");
991 json_variant_unref(per_machine
);
992 per_machine
= TAKE_PTR(npm
);
994 _cleanup_(json_variant_unrefp
) JsonVariant
*item
= json_variant_ref(arg_identity_extra_this_machine
);
996 if (arg_identity_extra_rlimits
) {
997 r
= json_variant_set_field(&item
, "resourceLimits", arg_identity_extra_rlimits
);
999 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
1002 r
= json_variant_set_field(&item
, "matchMachineId", mmid
);
1004 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
1006 r
= json_variant_append_array(&per_machine
, item
);
1008 return log_error_errno(r
, "Failed to append to perMachine array: %m");
1011 r
= json_variant_set_field(&v
, "perMachine", per_machine
);
1013 return log_error_errno(r
, "Failed to update per machine record: %m");
1016 if (arg_identity_extra_privileged
|| arg_identity_filter
) {
1017 _cleanup_(json_variant_unrefp
) JsonVariant
*privileged
= NULL
;
1019 privileged
= json_variant_ref(json_variant_by_key(v
, "privileged"));
1021 r
= json_variant_filter(&privileged
, arg_identity_filter
);
1023 return log_error_errno(r
, "Failed to filter identity (privileged part): %m");
1025 r
= json_variant_merge(&privileged
, arg_identity_extra_privileged
);
1027 return log_error_errno(r
, "Failed to merge identities (privileged part): %m");
1029 if (json_variant_is_blank_object(privileged
)) {
1030 r
= json_variant_filter(&v
, STRV_MAKE("privileged"));
1032 return log_error_errno(r
, "Failed to drop privileged part from identity: %m");
1034 r
= json_variant_set_field(&v
, "privileged", privileged
);
1036 return log_error_errno(r
, "Failed to update privileged part of identity: %m");
1040 if (arg_identity_filter_rlimits
) {
1041 _cleanup_(json_variant_unrefp
) JsonVariant
*rlv
= NULL
;
1043 rlv
= json_variant_ref(json_variant_by_key(v
, "resourceLimits"));
1045 r
= json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
1047 return log_error_errno(r
, "Failed to filter resource limits: %m");
1049 /* Note that we only filter resource limits here, but don't apply them. We do that in the perMachine section */
1051 if (json_variant_is_blank_object(rlv
)) {
1052 r
= json_variant_filter(&v
, STRV_MAKE("resourceLimits"));
1054 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
1056 r
= json_variant_set_field(&v
, "resourceLimits", rlv
);
1058 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
1062 json_variant_unref(*_v
);
1068 static int add_disposition(JsonVariant
**v
) {
1073 if (json_variant_by_key(*v
, "disposition"))
1076 /* Set the disposition to regular, if not configured explicitly */
1077 r
= json_variant_set_field_string(v
, "disposition", "regular");
1079 return log_error_errno(r
, "Failed to set disposition field: %m");
1084 static int acquire_new_home_record(UserRecord
**ret
) {
1085 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
1086 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1093 unsigned line
, column
;
1095 r
= json_parse_file(
1096 streq(arg_identity
, "-") ? stdin
: NULL
,
1097 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, JSON_PARSE_SENSITIVE
, &v
, &line
, &column
);
1099 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
1102 r
= apply_identity_changes(&v
);
1106 r
= add_disposition(&v
);
1110 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
1111 r
= identity_add_pkcs11_key_data(&v
, *i
);
1116 STRV_FOREACH(i
, arg_fido2_device
) {
1117 r
= identity_add_fido2_parameters(&v
, *i
, arg_fido2_lock_with
);
1122 if (arg_recovery_key
) {
1123 r
= identity_add_recovery_key(&v
);
1128 r
= update_last_change(&v
, true, false);
1133 json_variant_dump(v
, JSON_FORMAT_PRETTY
, NULL
, NULL
);
1135 hr
= user_record_new();
1139 r
= user_record_load(hr
, v
, USER_RECORD_REQUIRE_REGULAR
|USER_RECORD_ALLOW_SECRET
|USER_RECORD_ALLOW_PRIVILEGED
|USER_RECORD_ALLOW_PER_MACHINE
|USER_RECORD_ALLOW_SIGNATURE
|USER_RECORD_LOG
|USER_RECORD_PERMISSIVE
);
1143 *ret
= TAKE_PTR(hr
);
1147 static int acquire_new_password(
1148 const char *user_name
,
1160 e
= getenv("NEWPASSWORD");
1162 _cleanup_(erase_and_freep
) char *copy
= NULL
;
1164 /* As above, this is not for use, just for testing */
1172 r
= user_record_set_password(hr
, STRV_MAKE(e
), /* prepend = */ true);
1174 return log_error_errno(r
, "Failed to store password: %m");
1176 assert_se(unsetenv_erase("NEWPASSWORD") >= 0);
1179 *ret
= TAKE_PTR(copy
);
1185 (void) suggest_passwords();
1188 _cleanup_(strv_free_erasep
) char **first
= NULL
, **second
= NULL
;
1189 _cleanup_free_
char *question
= NULL
;
1192 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY
), "Too many attempts, giving up:");
1194 if (asprintf(&question
, "Please enter new password for user %s:", user_name
) < 0)
1197 r
= ask_password_auto(
1199 /* icon= */ "user-home",
1201 /* key_name= */ "home-password",
1202 /* credential_name= */ "home.new-password",
1204 0, /* no caching, we want to collect a new password here after all */
1207 return log_error_errno(r
, "Failed to acquire password: %m");
1209 question
= mfree(question
);
1210 if (asprintf(&question
, "Please enter new password for user %s (repeat):", user_name
) < 0)
1213 r
= ask_password_auto(
1215 /* icon= */ "user-home",
1217 /* key_name= */ "home-password",
1218 /* credential_name= */ "home.new-password",
1223 return log_error_errno(r
, "Failed to acquire password: %m");
1225 if (strv_equal(first
, second
)) {
1226 _cleanup_(erase_and_freep
) char *copy
= NULL
;
1229 copy
= strdup(first
[0]);
1234 r
= user_record_set_password(hr
, first
, /* prepend = */ true);
1236 return log_error_errno(r
, "Failed to store password: %m");
1239 *ret
= TAKE_PTR(copy
);
1244 log_error("Password didn't match, try again.");
1248 static int create_home(int argc
, char *argv
[], void *userdata
) {
1249 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1250 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1253 r
= acquire_bus(&bus
);
1257 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1260 /* If a username was specified, use it */
1262 if (valid_user_group_name(argv
[1], 0))
1263 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", argv
[1]);
1265 _cleanup_free_
char *un
= NULL
, *rr
= NULL
;
1267 /* Before we consider the user name invalid, let's check if we can split it? */
1268 r
= split_user_name_realm(argv
[1], &un
, &rr
);
1270 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name '%s' is not valid: %m", argv
[1]);
1273 r
= json_variant_set_field_string(&arg_identity_extra
, "realm", rr
);
1275 return log_error_errno(r
, "Failed to set realm field: %m");
1278 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", un
);
1281 return log_error_errno(r
, "Failed to set userName field: %m");
1283 /* If neither a username nor an identity have been specified we cannot operate. */
1285 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name required.");
1288 r
= acquire_new_home_record(&hr
);
1292 /* If the JSON record carries no plain text password (besides the recovery key), then let's query it
1294 if (strv_length(hr
->password
) <= arg_recovery_key
) {
1296 if (strv_isempty(hr
->hashed_password
)) {
1297 _cleanup_(erase_and_freep
) char *new_password
= NULL
;
1299 /* No regular (i.e. non-PKCS#11) hashed passwords set in the record, let's fix that. */
1300 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ true, &new_password
);
1304 r
= user_record_make_hashed_password(hr
, STRV_MAKE(new_password
), /* extend = */ false);
1306 return log_error_errno(r
, "Failed to hash password: %m");
1308 /* There's a hash password set in the record, acquire the unhashed version of it. */
1309 r
= acquire_existing_password(
1312 /* emphasize_current= */ false,
1313 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_PUSH_CACHE
);
1319 if (hr
->enforce_password_policy
== 0) {
1320 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1322 /* If password quality enforcement is disabled, let's at least warn client side */
1324 r
= user_record_quality_check_password(hr
, hr
, &error
);
1326 log_warning_errno(r
, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error
, r
));
1330 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1331 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1332 _cleanup_(erase_and_freep
) char *formatted
= NULL
;
1334 r
= json_variant_format(hr
->json
, 0, &formatted
);
1336 return log_error_errno(r
, "Failed to format user record: %m");
1338 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "CreateHome");
1340 return bus_log_create_error(r
);
1342 (void) sd_bus_message_sensitive(m
);
1344 r
= sd_bus_message_append(m
, "s", formatted
);
1346 return bus_log_create_error(r
);
1348 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1350 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
1351 _cleanup_(erase_and_freep
) char *new_password
= NULL
;
1353 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
1354 log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
1356 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ false, &new_password
);
1360 r
= user_record_make_hashed_password(hr
, STRV_MAKE(new_password
), /* extend = */ false);
1362 return log_error_errno(r
, "Failed to hash passwords: %m");
1364 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1375 static int remove_home(int argc
, char *argv
[], void *userdata
) {
1376 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1380 r
= acquire_bus(&bus
);
1384 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1386 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1387 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1388 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1390 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "RemoveHome");
1392 return bus_log_create_error(r
);
1394 r
= sd_bus_message_append(m
, "s", *i
);
1396 return bus_log_create_error(r
);
1398 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1400 log_error_errno(r
, "Failed to remove home: %s", bus_error_message(&error
, r
));
1409 static int acquire_updated_home_record(
1411 const char *username
,
1414 _cleanup_(json_variant_unrefp
) JsonVariant
*json
= NULL
;
1415 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1422 unsigned line
, column
;
1425 r
= json_parse_file(
1426 streq(arg_identity
, "-") ? stdin
: NULL
,
1427 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, JSON_PARSE_SENSITIVE
, &json
, &line
, &column
);
1429 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
1431 un
= json_variant_by_key(json
, "userName");
1433 if (!json_variant_is_string(un
) || (username
&& !streq(json_variant_string(un
), username
)))
1434 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name specified on command line and in JSON record do not match.");
1437 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No username specified.");
1439 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", username
);
1441 return log_error_errno(r
, "Failed to set userName field: %m");
1445 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1446 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1450 if (!identity_properties_specified())
1451 return log_error_errno(SYNTHETIC_ERRNO(EALREADY
), "No field to change specified.");
1453 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByName", &error
, &reply
, "s", username
);
1455 return log_error_errno(r
, "Failed to acquire user home record: %s", bus_error_message(&error
, r
));
1457 r
= sd_bus_message_read(reply
, "sbo", &text
, &incomplete
, NULL
);
1459 return bus_log_parse_error(r
);
1462 return log_error_errno(SYNTHETIC_ERRNO(EACCES
), "Lacking rights to acquire user record including privileged metadata, can't update record.");
1464 r
= json_parse(text
, JSON_PARSE_SENSITIVE
, &json
, NULL
, NULL
);
1466 return log_error_errno(r
, "Failed to parse JSON identity: %m");
1468 reply
= sd_bus_message_unref(reply
);
1470 r
= json_variant_filter(&json
, STRV_MAKE("binding", "status", "signature"));
1472 return log_error_errno(r
, "Failed to strip binding and status from record to update: %m");
1475 r
= apply_identity_changes(&json
);
1479 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
1480 r
= identity_add_pkcs11_key_data(&json
, *i
);
1485 STRV_FOREACH(i
, arg_fido2_device
) {
1486 r
= identity_add_fido2_parameters(&json
, *i
, arg_fido2_lock_with
);
1491 /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
1493 r
= update_last_change(&json
, arg_pkcs11_token_uri
|| arg_fido2_device
, !arg_identity
);
1498 json_variant_dump(json
, JSON_FORMAT_PRETTY
, NULL
, NULL
);
1500 hr
= user_record_new();
1504 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
);
1508 *ret
= TAKE_PTR(hr
);
1512 static int home_record_reset_human_interaction_permission(UserRecord
*hr
) {
1517 /* When we execute multiple operations one after the other, let's reset the permission to ask the
1518 * user each time, so that if interaction is necessary we will be told so again and thus can print a
1519 * nice message to the user, telling the user so. */
1521 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, -1);
1523 return log_error_errno(r
, "Failed to reset PKCS#11 protected authentication path permission flag: %m");
1525 r
= user_record_set_fido2_user_presence_permitted(hr
, -1);
1527 return log_error_errno(r
, "Failed to reset FIDO2 user presence permission flag: %m");
1529 r
= user_record_set_fido2_user_verification_permitted(hr
, -1);
1531 return log_error_errno(r
, "Failed to reset FIDO2 user verification permission flag: %m");
1536 static int update_home(int argc
, char *argv
[], void *userdata
) {
1537 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1538 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
, *secret
= NULL
;
1539 _cleanup_free_
char *buffer
= NULL
;
1540 const char *username
;
1545 else if (!arg_identity
) {
1546 buffer
= getusername_malloc();
1554 r
= acquire_bus(&bus
);
1558 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1560 r
= acquire_updated_home_record(bus
, username
, &hr
);
1564 /* Add in all secrets we can acquire cheaply */
1565 r
= acquire_passed_secrets(username
, &secret
);
1569 r
= user_record_merge_secret(hr
, secret
);
1573 /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
1574 * authentication might be confusing. */
1576 if (arg_and_resize
|| arg_and_change_password
)
1577 log_info("Updating home directory.");
1580 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1581 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1582 _cleanup_free_
char *formatted
= NULL
;
1584 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "UpdateHome");
1586 return bus_log_create_error(r
);
1588 r
= json_variant_format(hr
->json
, 0, &formatted
);
1590 return log_error_errno(r
, "Failed to format user record: %m");
1592 (void) sd_bus_message_sensitive(m
);
1594 r
= sd_bus_message_append(m
, "s", formatted
);
1596 return bus_log_create_error(r
);
1598 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1600 if (arg_and_change_password
&&
1601 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1602 /* In the generic handler we'd ask for a password in this case, but when
1603 * changing passwords that's not sufficient, as we need to acquire all keys
1605 return log_error_errno(r
, "Security token not inserted, refusing.");
1607 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1615 log_info("Resizing home.");
1617 (void) home_record_reset_human_interaction_permission(hr
);
1619 /* Also sync down disk size to underlying LUKS/fscrypt/quota */
1620 while (arg_and_resize
) {
1621 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1622 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1624 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ResizeHome");
1626 return bus_log_create_error(r
);
1628 /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
1629 r
= sd_bus_message_append(m
, "st", hr
->user_name
, UINT64_MAX
);
1631 return bus_log_create_error(r
);
1633 r
= bus_message_append_secret(m
, hr
);
1635 return bus_log_create_error(r
);
1637 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1639 if (arg_and_change_password
&&
1640 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1641 return log_error_errno(r
, "Security token not inserted, refusing.");
1643 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1650 if (arg_and_change_password
)
1651 log_info("Synchronizing passwords and encryption keys.");
1653 (void) home_record_reset_human_interaction_permission(hr
);
1655 /* Also sync down passwords to underlying LUKS/fscrypt */
1656 while (arg_and_change_password
) {
1657 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1658 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1660 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ChangePasswordHome");
1662 return bus_log_create_error(r
);
1664 /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
1665 r
= sd_bus_message_append(m
, "ss", hr
->user_name
, "{}");
1667 return bus_log_create_error(r
);
1669 r
= bus_message_append_secret(m
, hr
);
1671 return bus_log_create_error(r
);
1673 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1675 if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1676 return log_error_errno(r
, "Security token not inserted, refusing.");
1678 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1688 static int passwd_home(int argc
, char *argv
[], void *userdata
) {
1689 _cleanup_(user_record_unrefp
) UserRecord
*old_secret
= NULL
, *new_secret
= NULL
;
1690 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1691 _cleanup_free_
char *buffer
= NULL
;
1692 const char *username
;
1695 if (arg_pkcs11_token_uri
)
1696 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=…'.");
1697 if (arg_fido2_device
)
1698 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "To change the FIDO2 security token use 'homectl update --fido2-device=…'.");
1699 if (identity_properties_specified())
1700 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "The 'passwd' verb does not permit changing other record properties at the same time.");
1705 buffer
= getusername_malloc();
1712 r
= acquire_bus(&bus
);
1716 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1718 r
= acquire_passed_secrets(username
, &old_secret
);
1722 new_secret
= user_record_new();
1726 r
= acquire_new_password(username
, new_secret
, /* suggest = */ true, NULL
);
1731 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1732 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1734 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ChangePasswordHome");
1736 return bus_log_create_error(r
);
1738 r
= sd_bus_message_append(m
, "s", username
);
1740 return bus_log_create_error(r
);
1742 r
= bus_message_append_secret(m
, new_secret
);
1744 return bus_log_create_error(r
);
1746 r
= bus_message_append_secret(m
, old_secret
);
1748 return bus_log_create_error(r
);
1750 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1752 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
1754 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
1756 r
= acquire_new_password(username
, new_secret
, /* suggest = */ false, NULL
);
1758 } else if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1760 /* In the generic handler we'd ask for a password in this case, but when
1761 * changing passwords that's not sufficeint, as we need to acquire all keys
1763 return log_error_errno(r
, "Security token not inserted, refusing.");
1765 r
= handle_generic_user_record_error(username
, old_secret
, &error
, r
, true);
1775 static int parse_disk_size(const char *t
, uint64_t *ret
) {
1781 if (streq(t
, "min"))
1783 else if (streq(t
, "max"))
1784 *ret
= UINT64_MAX
-1; /* Largest size that isn't UINT64_MAX special marker */
1788 r
= parse_size(t
, 1024, &ds
);
1790 return log_error_errno(r
, "Failed to parse disk size parameter: %s", t
);
1792 if (ds
>= UINT64_MAX
) /* UINT64_MAX has special meaning for us ("dont change"), refuse */
1793 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
), "Disk size out of range: %s", t
);
1801 static int resize_home(int argc
, char *argv
[], void *userdata
) {
1802 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1803 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
1804 uint64_t ds
= UINT64_MAX
;
1807 r
= acquire_bus(&bus
);
1811 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1813 if (arg_disk_size_relative
!= UINT64_MAX
||
1814 (argc
> 2 && parse_permyriad(argv
[2]) >= 0))
1815 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1816 "Relative disk size specification currently not supported when resizing.");
1819 r
= parse_disk_size(argv
[2], &ds
);
1824 if (arg_disk_size
!= UINT64_MAX
) {
1825 if (ds
!= UINT64_MAX
&& ds
!= arg_disk_size
)
1826 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Disk size specified twice and doesn't match, refusing.");
1831 r
= acquire_passed_secrets(argv
[1], &secret
);
1836 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1837 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1839 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ResizeHome");
1841 return bus_log_create_error(r
);
1843 r
= sd_bus_message_append(m
, "st", argv
[1], ds
);
1845 return bus_log_create_error(r
);
1847 r
= bus_message_append_secret(m
, secret
);
1849 return bus_log_create_error(r
);
1851 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1853 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
1863 static int lock_home(int argc
, char *argv
[], void *userdata
) {
1864 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1868 r
= acquire_bus(&bus
);
1872 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1873 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1874 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1876 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "LockHome");
1878 return bus_log_create_error(r
);
1880 r
= sd_bus_message_append(m
, "s", *i
);
1882 return bus_log_create_error(r
);
1884 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1886 log_error_errno(r
, "Failed to lock home: %s", bus_error_message(&error
, r
));
1895 static int unlock_home(int argc
, char *argv
[], void *userdata
) {
1896 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1900 r
= acquire_bus(&bus
);
1904 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1905 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
1907 r
= acquire_passed_secrets(*i
, &secret
);
1912 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1913 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1915 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "UnlockHome");
1917 return bus_log_create_error(r
);
1919 r
= sd_bus_message_append(m
, "s", *i
);
1921 return bus_log_create_error(r
);
1923 r
= bus_message_append_secret(m
, secret
);
1925 return bus_log_create_error(r
);
1927 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1929 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
1944 static int with_home(int argc
, char *argv
[], void *userdata
) {
1945 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1946 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
1947 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1948 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
1949 _cleanup_close_
int acquired_fd
= -1;
1950 _cleanup_strv_free_
char **cmdline
= NULL
;
1955 r
= acquire_bus(&bus
);
1960 _cleanup_free_
char *shell
= NULL
;
1962 /* If no command is specified, spawn a shell */
1963 r
= get_shell(&shell
);
1965 return log_error_errno(r
, "Failed to acquire shell: %m");
1967 cmdline
= strv_new(shell
);
1969 cmdline
= strv_copy(argv
+ 2);
1973 r
= acquire_passed_secrets(argv
[1], &secret
);
1978 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "AcquireHome");
1980 return bus_log_create_error(r
);
1982 r
= sd_bus_message_append(m
, "s", argv
[1]);
1984 return bus_log_create_error(r
);
1986 r
= bus_message_append_secret(m
, secret
);
1988 return bus_log_create_error(r
);
1990 r
= sd_bus_message_append(m
, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
1992 return bus_log_create_error(r
);
1994 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
1995 m
= sd_bus_message_unref(m
);
1997 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2001 sd_bus_error_free(&error
);
2005 r
= sd_bus_message_read(reply
, "h", &fd
);
2007 return bus_log_parse_error(r
);
2009 acquired_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
2010 if (acquired_fd
< 0)
2011 return log_error_errno(errno
, "Failed to duplicate acquired fd: %m");
2013 reply
= sd_bus_message_unref(reply
);
2018 r
= bus_call_method(bus
, bus_mgr
, "GetHomeByName", &error
, &reply
, "s", argv
[1]);
2020 return log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
2022 r
= sd_bus_message_read(reply
, "usussso", NULL
, NULL
, NULL
, NULL
, &home
, NULL
, NULL
);
2024 return bus_log_parse_error(r
);
2026 r
= safe_fork("(with)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_RLIMIT_NOFILE_SAFE
|FORK_REOPEN_LOG
, &pid
);
2030 if (chdir(home
) < 0) {
2031 log_error_errno(errno
, "Failed to change to directory %s: %m", home
);
2035 execvp(cmdline
[0], cmdline
);
2036 log_error_errno(errno
, "Failed to execute %s: %m", cmdline
[0]);
2040 ret
= wait_for_terminate_and_check(cmdline
[0], pid
, WAIT_LOG_ABNORMAL
);
2042 /* Close the fd that pings the home now. */
2043 acquired_fd
= safe_close(acquired_fd
);
2045 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ReleaseHome");
2047 return bus_log_create_error(r
);
2049 r
= sd_bus_message_append(m
, "s", argv
[1]);
2051 return bus_log_create_error(r
);
2053 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2055 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_BUSY
))
2056 log_notice("Not deactivating home directory of %s, as it is still used.", argv
[1]);
2058 return log_error_errno(r
, "Failed to release user home: %s", bus_error_message(&error
, r
));
2064 static int lock_all_homes(int argc
, char *argv
[], void *userdata
) {
2065 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2066 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2067 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2070 r
= acquire_bus(&bus
);
2074 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "LockAllHomes");
2076 return bus_log_create_error(r
);
2078 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2080 return log_error_errno(r
, "Failed to lock all homes: %s", bus_error_message(&error
, r
));
2085 static int deactivate_all_homes(int argc
, char *argv
[], void *userdata
) {
2086 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2087 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2088 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2091 r
= acquire_bus(&bus
);
2095 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "DeactivateAllHomes");
2097 return bus_log_create_error(r
);
2099 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2101 return log_error_errno(r
, "Failed to deactivate all homes: %s", bus_error_message(&error
, r
));
2106 static int rebalance(int argc
, char *argv
[], void *userdata
) {
2107 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2108 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2109 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2112 r
= acquire_bus(&bus
);
2116 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "Rebalance");
2118 return bus_log_create_error(r
);
2120 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2122 if (sd_bus_error_has_name(&error
, BUS_ERROR_REBALANCE_NOT_NEEDED
))
2123 log_info("No homes needed rebalancing.");
2125 return log_error_errno(r
, "Failed to rebalance: %s", bus_error_message(&error
, r
));
2127 log_info("Completed rebalancing.");
2132 static int drop_from_identity(const char *field
) {
2137 /* If we are called to update an identity record and drop some field, let's keep track of what to
2138 * remove from the old record */
2139 r
= strv_extend(&arg_identity_filter
, field
);
2143 /* Let's also drop the field if it was previously set to a new value on the same command line */
2144 r
= json_variant_filter(&arg_identity_extra
, STRV_MAKE(field
));
2146 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2148 r
= json_variant_filter(&arg_identity_extra_this_machine
, STRV_MAKE(field
));
2150 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2152 r
= json_variant_filter(&arg_identity_extra_privileged
, STRV_MAKE(field
));
2154 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2159 static int help(int argc
, char *argv
[], void *userdata
) {
2160 _cleanup_free_
char *link
= NULL
;
2163 pager_open(arg_pager_flags
);
2165 r
= terminal_urlify_man("homectl", "1", &link
);
2169 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
2170 "%2$sCreate, manipulate or inspect home directories.%3$s\n"
2171 "\n%4$sCommands:%5$s\n"
2172 " list List home areas\n"
2173 " activate USER… Activate a home area\n"
2174 " deactivate USER… Deactivate a home area\n"
2175 " inspect USER… Inspect a home area\n"
2176 " authenticate USER… Authenticate a home area\n"
2177 " create USER Create a home area\n"
2178 " remove USER… Remove a home area\n"
2179 " update USER Update a home area\n"
2180 " passwd USER Change password of a home area\n"
2181 " resize USER SIZE Resize a home area\n"
2182 " lock USER… Temporarily lock an active home area\n"
2183 " unlock USER… Unlock a temporarily locked home area\n"
2184 " lock-all Lock all suitable home areas\n"
2185 " deactivate-all Deactivate all active home areas\n"
2186 " rebalance Rebalance free space between home areas\n"
2187 " with USER [COMMAND…] Run shell or command with access to a home area\n"
2188 "\n%4$sOptions:%5$s\n"
2189 " -h --help Show this help\n"
2190 " --version Show package version\n"
2191 " --no-pager Do not pipe output into a pager\n"
2192 " --no-legend Do not show the headers and footers\n"
2193 " --no-ask-password Do not ask for system passwords\n"
2194 " -H --host=[USER@]HOST Operate on remote host\n"
2195 " -M --machine=CONTAINER Operate on local container\n"
2196 " --identity=PATH Read JSON identity from file\n"
2197 " --json=FORMAT Output inspection data in JSON (takes one of\n"
2198 " pretty, short, off)\n"
2199 " -j Equivalent to --json=pretty (on TTY) or\n"
2200 " --json=short (otherwise)\n"
2201 " --export-format= Strip JSON inspection data (full, stripped,\n"
2203 " -E When specified once equals -j --export-format=\n"
2204 " stripped, when specified twice equals\n"
2205 " -j --export-format=minimal\n"
2206 "\n%4$sGeneral User Record Properties:%5$s\n"
2207 " -c --real-name=REALNAME Real name for user\n"
2208 " --realm=REALM Realm to create user in\n"
2209 " --email-address=EMAIL Email address for user\n"
2210 " --location=LOCATION Set location of user on earth\n"
2211 " --icon-name=NAME Icon name for user\n"
2212 " -d --home-dir=PATH Home directory\n"
2213 " -u --uid=UID Numeric UID for user\n"
2214 " -G --member-of=GROUP Add user to group\n"
2215 " --skel=PATH Skeleton directory to use\n"
2216 " --shell=PATH Shell for account\n"
2217 " --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n"
2218 " --timezone=TIMEZONE Set a time-zone\n"
2219 " --language=LOCALE Set preferred language\n"
2220 " --ssh-authorized-keys=KEYS\n"
2221 " Specify SSH public keys\n"
2222 " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
2223 " private key and matching X.509 certificate\n"
2224 " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
2226 " --fido2-with-client-pin=BOOL\n"
2227 " Whether to require entering a PIN to unlock the\n"
2229 " --fido2-with-user-presence=BOOL\n"
2230 " Whether to require user presence to unlock the\n"
2232 " --fido2-with-user-verification=BOOL\n"
2233 " Whether to require user verification to unlock\n"
2235 " --recovery-key=BOOL Add a recovery key\n"
2236 "\n%4$sAccount Management User Record Properties:%5$s\n"
2237 " --locked=BOOL Set locked account state\n"
2238 " --not-before=TIMESTAMP Do not allow logins before\n"
2239 " --not-after=TIMESTAMP Do not allow logins after\n"
2240 " --rate-limit-interval=SECS\n"
2241 " Login rate-limit interval in seconds\n"
2242 " --rate-limit-burst=NUMBER\n"
2243 " Login rate-limit attempts per interval\n"
2244 "\n%4$sPassword Policy User Record Properties:%5$s\n"
2245 " --password-hint=HINT Set Password hint\n"
2246 " --enforce-password-policy=BOOL\n"
2247 " Control whether to enforce system's password\n"
2248 " policy for this user\n"
2249 " -P Same as --enforce-password-password=no\n"
2250 " --password-change-now=BOOL\n"
2251 " Require the password to be changed on next login\n"
2252 " --password-change-min=TIME\n"
2253 " Require minimum time between password changes\n"
2254 " --password-change-max=TIME\n"
2255 " Require maximum time between password changes\n"
2256 " --password-change-warn=TIME\n"
2257 " How much time to warn before password expiry\n"
2258 " --password-change-inactive=TIME\n"
2259 " How much time to block password after expiry\n"
2260 "\n%4$sResource Management User Record Properties:%5$s\n"
2261 " --disk-size=BYTES Size to assign the user on disk\n"
2262 " --access-mode=MODE User home directory access mode\n"
2263 " --umask=MODE Umask for user when logging in\n"
2264 " --nice=NICE Nice level for user\n"
2265 " --rlimit=LIMIT=VALUE[:VALUE]\n"
2266 " Set resource limits\n"
2267 " --tasks-max=MAX Set maximum number of per-user tasks\n"
2268 " --memory-high=BYTES Set high memory threshold in bytes\n"
2269 " --memory-max=BYTES Set maximum memory limit\n"
2270 " --cpu-weight=WEIGHT Set CPU weight\n"
2271 " --io-weight=WEIGHT Set IO weight\n"
2272 "\n%4$sStorage User Record Properties:%5$s\n"
2273 " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
2274 " subvolume, cifs)\n"
2275 " --image-path=PATH Path to image file/directory\n"
2276 " --drop-caches=BOOL Whether to automatically drop caches on logout\n"
2277 "\n%4$sLUKS Storage User Record Properties:%5$s\n"
2278 " --fs-type=TYPE File system type to use in case of luks\n"
2279 " storage (btrfs, ext4, xfs)\n"
2280 " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
2281 " when activated (mounted)\n"
2282 " --luks-offline-discard=BOOL\n"
2283 " Whether to trim file on logout\n"
2284 " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
2285 " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
2286 " --luks-volume-key-size=BITS\n"
2287 " Volume key size to use for LUKS encryption\n"
2288 " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
2289 " --luks-pbkdf-hash-algorithm=ALGORITHM\n"
2290 " PBKDF hash algorithm to use\n"
2291 " --luks-pbkdf-time-cost=SECS\n"
2292 " Time cost for PBKDF in seconds\n"
2293 " --luks-pbkdf-memory-cost=BYTES\n"
2294 " Memory cost for PBKDF in bytes\n"
2295 " --luks-pbkdf-parallel-threads=NUMBER\n"
2296 " Number of parallel threads for PKBDF\n"
2297 " --luks-extra-mount-options=OPTIONS\n"
2298 " LUKS extra mount options\n"
2299 " --auto-resize-mode=MODE Automatically grow/shrink home on login/logout\n"
2300 " --rebalance-weight=WEIGHT Weight while rebalancing\n"
2301 "\n%4$sMounting User Record Properties:%5$s\n"
2302 " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
2303 " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
2304 " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
2305 "\n%4$sCIFS User Record Properties:%5$s\n"
2306 " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
2307 " --cifs-user-name=USER CIFS (Windows) user name\n"
2308 " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
2309 " --cifs-extra-mount-options=OPTIONS\n"
2310 " CIFS (Windows) extra mount options\n"
2311 "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
2312 " --stop-delay=SECS How long to leave user services running after\n"
2314 " --kill-processes=BOOL Whether to kill user processes when sessions\n"
2316 " --auto-login=BOOL Try to log this user in automatically\n"
2317 "\nSee the %6$s for details.\n",
2318 program_invocation_short_name
,
2328 static int parse_argv(int argc
, char *argv
[]) {
2331 ARG_VERSION
= 0x100,
2334 ARG_NO_ASK_PASSWORD
,
2344 ARG_LUKS_OFFLINE_DISCARD
,
2350 ARG_SSH_AUTHORIZED_KEYS
,
2359 ARG_LUKS_CIPHER_MODE
,
2360 ARG_LUKS_VOLUME_KEY_SIZE
,
2367 ARG_CIFS_EXTRA_MOUNT_OPTIONS
,
2373 ARG_LUKS_PBKDF_TYPE
,
2374 ARG_LUKS_PBKDF_HASH_ALGORITHM
,
2375 ARG_LUKS_PBKDF_TIME_COST
,
2376 ARG_LUKS_PBKDF_MEMORY_COST
,
2377 ARG_LUKS_PBKDF_PARALLEL_THREADS
,
2378 ARG_RATE_LIMIT_INTERVAL
,
2379 ARG_RATE_LIMIT_BURST
,
2382 ARG_ENFORCE_PASSWORD_POLICY
,
2383 ARG_PASSWORD_CHANGE_NOW
,
2384 ARG_PASSWORD_CHANGE_MIN
,
2385 ARG_PASSWORD_CHANGE_MAX
,
2386 ARG_PASSWORD_CHANGE_WARN
,
2387 ARG_PASSWORD_CHANGE_INACTIVE
,
2390 ARG_PKCS11_TOKEN_URI
,
2397 ARG_AND_CHANGE_PASSWORD
,
2399 ARG_LUKS_EXTRA_MOUNT_OPTIONS
,
2400 ARG_AUTO_RESIZE_MODE
,
2401 ARG_REBALANCE_WEIGHT
,
2404 static const struct option options
[] = {
2405 { "help", no_argument
, NULL
, 'h' },
2406 { "version", no_argument
, NULL
, ARG_VERSION
},
2407 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2408 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2409 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2410 { "host", required_argument
, NULL
, 'H' },
2411 { "machine", required_argument
, NULL
, 'M' },
2412 { "identity", required_argument
, NULL
, 'I' },
2413 { "real-name", required_argument
, NULL
, 'c' },
2414 { "comment", required_argument
, NULL
, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
2415 { "realm", required_argument
, NULL
, ARG_REALM
},
2416 { "email-address", required_argument
, NULL
, ARG_EMAIL_ADDRESS
},
2417 { "location", required_argument
, NULL
, ARG_LOCATION
},
2418 { "password-hint", required_argument
, NULL
, ARG_PASSWORD_HINT
},
2419 { "icon-name", required_argument
, NULL
, ARG_ICON_NAME
},
2420 { "home-dir", required_argument
, NULL
, 'd' }, /* Compatible with useradd(8) */
2421 { "uid", required_argument
, NULL
, 'u' }, /* Compatible with useradd(8) */
2422 { "member-of", required_argument
, NULL
, 'G' },
2423 { "groups", required_argument
, NULL
, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
2424 { "skel", required_argument
, NULL
, 'k' }, /* Compatible with useradd(8) */
2425 { "shell", required_argument
, NULL
, 's' }, /* Compatible with useradd(8) */
2426 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2427 { "timezone", required_argument
, NULL
, ARG_TIMEZONE
},
2428 { "language", required_argument
, NULL
, ARG_LANGUAGE
},
2429 { "locked", required_argument
, NULL
, ARG_LOCKED
},
2430 { "not-before", required_argument
, NULL
, ARG_NOT_BEFORE
},
2431 { "not-after", required_argument
, NULL
, ARG_NOT_AFTER
},
2432 { "expiredate", required_argument
, NULL
, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
2433 { "ssh-authorized-keys", required_argument
, NULL
, ARG_SSH_AUTHORIZED_KEYS
},
2434 { "disk-size", required_argument
, NULL
, ARG_DISK_SIZE
},
2435 { "access-mode", required_argument
, NULL
, ARG_ACCESS_MODE
},
2436 { "umask", required_argument
, NULL
, ARG_UMASK
},
2437 { "nice", required_argument
, NULL
, ARG_NICE
},
2438 { "rlimit", required_argument
, NULL
, ARG_RLIMIT
},
2439 { "tasks-max", required_argument
, NULL
, ARG_TASKS_MAX
},
2440 { "memory-high", required_argument
, NULL
, ARG_MEMORY_HIGH
},
2441 { "memory-max", required_argument
, NULL
, ARG_MEMORY_MAX
},
2442 { "cpu-weight", required_argument
, NULL
, ARG_CPU_WEIGHT
},
2443 { "io-weight", required_argument
, NULL
, ARG_IO_WEIGHT
},
2444 { "storage", required_argument
, NULL
, ARG_STORAGE
},
2445 { "image-path", required_argument
, NULL
, ARG_IMAGE_PATH
},
2446 { "fs-type", required_argument
, NULL
, ARG_FS_TYPE
},
2447 { "luks-discard", required_argument
, NULL
, ARG_LUKS_DISCARD
},
2448 { "luks-offline-discard", required_argument
, NULL
, ARG_LUKS_OFFLINE_DISCARD
},
2449 { "luks-cipher", required_argument
, NULL
, ARG_LUKS_CIPHER
},
2450 { "luks-cipher-mode", required_argument
, NULL
, ARG_LUKS_CIPHER_MODE
},
2451 { "luks-volume-key-size", required_argument
, NULL
, ARG_LUKS_VOLUME_KEY_SIZE
},
2452 { "luks-pbkdf-type", required_argument
, NULL
, ARG_LUKS_PBKDF_TYPE
},
2453 { "luks-pbkdf-hash-algorithm", required_argument
, NULL
, ARG_LUKS_PBKDF_HASH_ALGORITHM
},
2454 { "luks-pbkdf-time-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_TIME_COST
},
2455 { "luks-pbkdf-memory-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_MEMORY_COST
},
2456 { "luks-pbkdf-parallel-threads", required_argument
, NULL
, ARG_LUKS_PBKDF_PARALLEL_THREADS
},
2457 { "nosuid", required_argument
, NULL
, ARG_NOSUID
},
2458 { "nodev", required_argument
, NULL
, ARG_NODEV
},
2459 { "noexec", required_argument
, NULL
, ARG_NOEXEC
},
2460 { "cifs-user-name", required_argument
, NULL
, ARG_CIFS_USER_NAME
},
2461 { "cifs-domain", required_argument
, NULL
, ARG_CIFS_DOMAIN
},
2462 { "cifs-service", required_argument
, NULL
, ARG_CIFS_SERVICE
},
2463 { "cifs-extra-mount-options", required_argument
, NULL
, ARG_CIFS_EXTRA_MOUNT_OPTIONS
},
2464 { "rate-limit-interval", required_argument
, NULL
, ARG_RATE_LIMIT_INTERVAL
},
2465 { "rate-limit-burst", required_argument
, NULL
, ARG_RATE_LIMIT_BURST
},
2466 { "stop-delay", required_argument
, NULL
, ARG_STOP_DELAY
},
2467 { "kill-processes", required_argument
, NULL
, ARG_KILL_PROCESSES
},
2468 { "enforce-password-policy", required_argument
, NULL
, ARG_ENFORCE_PASSWORD_POLICY
},
2469 { "password-change-now", required_argument
, NULL
, ARG_PASSWORD_CHANGE_NOW
},
2470 { "password-change-min", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MIN
},
2471 { "password-change-max", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MAX
},
2472 { "password-change-warn", required_argument
, NULL
, ARG_PASSWORD_CHANGE_WARN
},
2473 { "password-change-inactive", required_argument
, NULL
, ARG_PASSWORD_CHANGE_INACTIVE
},
2474 { "auto-login", required_argument
, NULL
, ARG_AUTO_LOGIN
},
2475 { "json", required_argument
, NULL
, ARG_JSON
},
2476 { "export-format", required_argument
, NULL
, ARG_EXPORT_FORMAT
},
2477 { "pkcs11-token-uri", required_argument
, NULL
, ARG_PKCS11_TOKEN_URI
},
2478 { "fido2-device", required_argument
, NULL
, ARG_FIDO2_DEVICE
},
2479 { "fido2-with-client-pin", required_argument
, NULL
, ARG_FIDO2_WITH_PIN
},
2480 { "fido2-with-user-presence", required_argument
, NULL
, ARG_FIDO2_WITH_UP
},
2481 { "fido2-with-user-verification",required_argument
, NULL
, ARG_FIDO2_WITH_UV
},
2482 { "recovery-key", required_argument
, NULL
, ARG_RECOVERY_KEY
},
2483 { "and-resize", required_argument
, NULL
, ARG_AND_RESIZE
},
2484 { "and-change-password", required_argument
, NULL
, ARG_AND_CHANGE_PASSWORD
},
2485 { "drop-caches", required_argument
, NULL
, ARG_DROP_CACHES
},
2486 { "luks-extra-mount-options", required_argument
, NULL
, ARG_LUKS_EXTRA_MOUNT_OPTIONS
},
2487 { "auto-resize-mode", required_argument
, NULL
, ARG_AUTO_RESIZE_MODE
},
2488 { "rebalance-weight", required_argument
, NULL
, ARG_REBALANCE_WEIGHT
},
2500 c
= getopt_long(argc
, argv
, "hH:M:I:c:d:u:k:s:e:G:jPE", options
, NULL
);
2507 return help(0, NULL
, NULL
);
2513 arg_pager_flags
|= PAGER_DISABLE
;
2520 case ARG_NO_ASK_PASSWORD
:
2521 arg_ask_password
= false;
2525 arg_transport
= BUS_TRANSPORT_REMOTE
;
2530 arg_transport
= BUS_TRANSPORT_MACHINE
;
2535 arg_identity
= optarg
;
2539 if (isempty(optarg
)) {
2540 r
= drop_from_identity("realName");
2547 if (!valid_gecos(optarg
))
2548 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Real name '%s' not a valid GECOS field.", optarg
);
2550 r
= json_variant_set_field_string(&arg_identity_extra
, "realName", optarg
);
2552 return log_error_errno(r
, "Failed to set realName field: %m");
2557 _cleanup_free_
char *hd
= NULL
;
2559 if (isempty(optarg
)) {
2560 r
= drop_from_identity("homeDirectory");
2567 r
= parse_path_argument(optarg
, false, &hd
);
2571 if (!valid_home(hd
))
2572 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Home directory '%s' not valid.", hd
);
2574 r
= json_variant_set_field_string(&arg_identity_extra
, "homeDirectory", hd
);
2576 return log_error_errno(r
, "Failed to set homeDirectory field: %m");
2582 if (isempty(optarg
)) {
2583 r
= drop_from_identity("realm");
2590 r
= dns_name_is_valid(optarg
);
2592 return log_error_errno(r
, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg
);
2594 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Realm '%s' is not a valid DNS domain: %m", optarg
);
2596 r
= json_variant_set_field_string(&arg_identity_extra
, "realm", optarg
);
2598 return log_error_errno(r
, "Failed to set realm field: %m");
2601 case ARG_EMAIL_ADDRESS
:
2604 case ARG_CIFS_USER_NAME
:
2605 case ARG_CIFS_DOMAIN
:
2606 case ARG_CIFS_EXTRA_MOUNT_OPTIONS
:
2607 case ARG_LUKS_EXTRA_MOUNT_OPTIONS
: {
2610 c
== ARG_EMAIL_ADDRESS
? "emailAddress" :
2611 c
== ARG_LOCATION
? "location" :
2612 c
== ARG_ICON_NAME
? "iconName" :
2613 c
== ARG_CIFS_USER_NAME
? "cifsUserName" :
2614 c
== ARG_CIFS_DOMAIN
? "cifsDomain" :
2615 c
== ARG_CIFS_EXTRA_MOUNT_OPTIONS
? "cifsExtraMountOptions" :
2616 c
== ARG_LUKS_EXTRA_MOUNT_OPTIONS
? "luksExtraMountOptions" :
2621 if (isempty(optarg
)) {
2622 r
= drop_from_identity(field
);
2629 r
= json_variant_set_field_string(&arg_identity_extra
, field
, optarg
);
2631 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2636 case ARG_CIFS_SERVICE
:
2637 if (isempty(optarg
)) {
2638 r
= drop_from_identity("cifsService");
2645 r
= parse_cifs_service(optarg
, NULL
, NULL
, NULL
);
2647 return log_error_errno(r
, "Failed to validate CIFS service name: %s", optarg
);
2649 r
= json_variant_set_field_string(&arg_identity_extra
, "cifsService", optarg
);
2651 return log_error_errno(r
, "Failed to set cifsService field: %m");
2655 case ARG_PASSWORD_HINT
:
2656 if (isempty(optarg
)) {
2657 r
= drop_from_identity("passwordHint");
2664 r
= json_variant_set_field_string(&arg_identity_extra_privileged
, "passwordHint", optarg
);
2666 return log_error_errno(r
, "Failed to set passwordHint field: %m");
2668 string_erase(optarg
);
2674 if (isempty(optarg
)) {
2675 r
= drop_from_identity("niceLevel");
2681 r
= parse_nice(optarg
, &nc
);
2683 return log_error_errno(r
, "Failed to parse nice level: %s", optarg
);
2685 r
= json_variant_set_field_integer(&arg_identity_extra
, "niceLevel", nc
);
2687 return log_error_errno(r
, "Failed to set niceLevel field: %m");
2693 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *jcur
= NULL
, *jmax
= NULL
;
2694 _cleanup_free_
char *field
= NULL
, *t
= NULL
;
2699 if (isempty(optarg
)) {
2700 /* Remove all resource limits */
2702 r
= drop_from_identity("resourceLimits");
2706 arg_identity_filter_rlimits
= strv_free(arg_identity_filter_rlimits
);
2707 arg_identity_extra_rlimits
= json_variant_unref(arg_identity_extra_rlimits
);
2711 eq
= strchr(optarg
, '=');
2713 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Can't parse resource limit assignment: %s", optarg
);
2715 field
= strndup(optarg
, eq
- optarg
);
2719 l
= rlimit_from_string_harder(field
);
2721 return log_error_errno(l
, "Unknown resource limit type: %s", field
);
2723 if (isempty(eq
+ 1)) {
2724 /* Remove only the specific rlimit */
2726 r
= strv_extend(&arg_identity_filter_rlimits
, rlimit_to_string(l
));
2730 r
= json_variant_filter(&arg_identity_extra_rlimits
, STRV_MAKE(field
));
2732 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2737 r
= rlimit_parse(l
, eq
+ 1, &rl
);
2739 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to parse resource limit value: %s", eq
+ 1);
2741 r
= rl
.rlim_cur
== RLIM_INFINITY
? json_variant_new_null(&jcur
) : json_variant_new_unsigned(&jcur
, rl
.rlim_cur
);
2743 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to allocate current integer: %m");
2745 r
= rl
.rlim_max
== RLIM_INFINITY
? json_variant_new_null(&jmax
) : json_variant_new_unsigned(&jmax
, rl
.rlim_max
);
2747 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to allocate maximum integer: %m");
2751 JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur
)),
2752 JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax
))));
2754 return log_error_errno(r
, "Failed to build resource limit: %m");
2756 t
= strjoin("RLIMIT_", rlimit_to_string(l
));
2760 r
= json_variant_set_field(&arg_identity_extra_rlimits
, t
, v
);
2762 return log_error_errno(r
, "Failed to set %s field: %m", rlimit_to_string(l
));
2770 if (isempty(optarg
)) {
2771 r
= drop_from_identity("uid");
2778 r
= parse_uid(optarg
, &uid
);
2780 return log_error_errno(r
, "Failed to parse UID '%s'.", optarg
);
2782 if (uid_is_system(uid
))
2783 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in system range, refusing.", uid
);
2784 if (uid_is_dynamic(uid
))
2785 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in dynamic range, refusing.", uid
);
2786 if (uid
== UID_NOBODY
)
2787 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is nobody UID, refusing.", uid
);
2789 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "uid", uid
);
2791 return log_error_errno(r
, "Failed to set realm field: %m");
2797 case ARG_IMAGE_PATH
: {
2798 const char *field
= c
== 'k' ? "skeletonDirectory" : "imagePath";
2799 _cleanup_free_
char *v
= NULL
;
2801 if (isempty(optarg
)) {
2802 r
= drop_from_identity(field
);
2809 r
= parse_path_argument(optarg
, false, &v
);
2813 r
= json_variant_set_field_string(&arg_identity_extra_this_machine
, field
, v
);
2815 return log_error_errno(r
, "Failed to set %s field: %m", v
);
2821 if (isempty(optarg
)) {
2822 r
= drop_from_identity("shell");
2829 if (!valid_shell(optarg
))
2830 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Shell '%s' not valid.", optarg
);
2832 r
= json_variant_set_field_string(&arg_identity_extra
, "shell", optarg
);
2834 return log_error_errno(r
, "Failed to set shell field: %m");
2839 _cleanup_free_
char **l
= NULL
;
2840 _cleanup_(json_variant_unrefp
) JsonVariant
*ne
= NULL
;
2843 if (isempty(optarg
)) {
2844 r
= drop_from_identity("environment");
2851 e
= json_variant_by_key(arg_identity_extra
, "environment");
2853 r
= json_variant_strv(e
, &l
);
2855 return log_error_errno(r
, "Failed to parse JSON environment field: %m");
2858 r
= strv_env_replace_strdup_passthrough(&l
, optarg
);
2860 return log_error_errno(r
, "Cannot assign environment variable %s: %m", optarg
);
2864 r
= json_variant_new_array_strv(&ne
, l
);
2866 return log_error_errno(r
, "Failed to allocate environment list JSON: %m");
2868 r
= json_variant_set_field(&arg_identity_extra
, "environment", ne
);
2870 return log_error_errno(r
, "Failed to set environment list: %m");
2877 if (isempty(optarg
)) {
2878 r
= drop_from_identity("timeZone");
2885 if (!timezone_is_valid(optarg
, LOG_DEBUG
))
2886 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Timezone '%s' is not valid.", optarg
);
2888 r
= json_variant_set_field_string(&arg_identity_extra
, "timeZone", optarg
);
2890 return log_error_errno(r
, "Failed to set timezone field: %m");
2895 if (isempty(optarg
)) {
2896 r
= drop_from_identity("language");
2903 if (!locale_is_valid(optarg
))
2904 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Locale '%s' is not valid.", optarg
);
2906 if (locale_is_installed(optarg
) <= 0)
2907 log_warning("Locale '%s' is not installed, accepting anyway.", optarg
);
2909 r
= json_variant_set_field_string(&arg_identity_extra
, "preferredLanguage", optarg
);
2911 return log_error_errno(r
, "Failed to set preferredLanguage field: %m");
2919 case ARG_KILL_PROCESSES
:
2920 case ARG_ENFORCE_PASSWORD_POLICY
:
2921 case ARG_AUTO_LOGIN
:
2922 case ARG_PASSWORD_CHANGE_NOW
: {
2924 c
== ARG_LOCKED
? "locked" :
2925 c
== ARG_NOSUID
? "mountNoSuid" :
2926 c
== ARG_NODEV
? "mountNoDevices" :
2927 c
== ARG_NOEXEC
? "mountNoExecute" :
2928 c
== ARG_KILL_PROCESSES
? "killProcesses" :
2929 c
== ARG_ENFORCE_PASSWORD_POLICY
? "enforcePasswordPolicy" :
2930 c
== ARG_AUTO_LOGIN
? "autoLogin" :
2931 c
== ARG_PASSWORD_CHANGE_NOW
? "passwordChangeNow" :
2936 if (isempty(optarg
)) {
2937 r
= drop_from_identity(field
);
2944 r
= parse_boolean(optarg
);
2946 return log_error_errno(r
, "Failed to parse %s boolean: %m", field
);
2948 r
= json_variant_set_field_boolean(&arg_identity_extra
, field
, r
> 0);
2950 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2956 r
= json_variant_set_field_boolean(&arg_identity_extra
, "enforcePasswordPolicy", false);
2958 return log_error_errno(r
, "Failed to set enforcePasswordPolicy field: %m");
2963 if (isempty(optarg
)) {
2966 FOREACH_STRING(prop
, "diskSize", "diskSizeRelative", "rebalanceWeight") {
2967 r
= drop_from_identity(prop
);
2972 arg_disk_size
= arg_disk_size_relative
= UINT64_MAX
;
2976 r
= parse_permyriad(optarg
);
2978 r
= parse_disk_size(optarg
, &arg_disk_size
);
2982 r
= drop_from_identity("diskSizeRelative");
2986 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "diskSize", arg_disk_size
);
2988 return log_error_errno(r
, "Failed to set diskSize field: %m");
2990 arg_disk_size_relative
= UINT64_MAX
;
2992 /* Normalize to UINT32_MAX == 100% */
2993 arg_disk_size_relative
= UINT32_SCALE_FROM_PERMYRIAD(r
);
2995 r
= drop_from_identity("diskSize");
2999 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "diskSizeRelative", arg_disk_size_relative
);
3001 return log_error_errno(r
, "Failed to set diskSizeRelative field: %m");
3003 arg_disk_size
= UINT64_MAX
;
3006 /* Automatically turn off the rebalance logic if user configured a size explicitly */
3007 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "rebalanceWeight", REBALANCE_WEIGHT_OFF
);
3009 return log_error_errno(r
, "Failed to set rebalanceWeight field: %m");
3013 case ARG_ACCESS_MODE
: {
3016 if (isempty(optarg
)) {
3017 r
= drop_from_identity("accessMode");
3024 r
= parse_mode(optarg
, &mode
);
3026 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Access mode '%s' not valid.", optarg
);
3028 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "accessMode", mode
);
3030 return log_error_errno(r
, "Failed to set access mode field: %m");
3035 case ARG_LUKS_DISCARD
:
3036 if (isempty(optarg
)) {
3037 r
= drop_from_identity("luksDiscard");
3044 r
= parse_boolean(optarg
);
3046 return log_error_errno(r
, "Failed to parse --luks-discard= parameter: %s", optarg
);
3048 r
= json_variant_set_field_boolean(&arg_identity_extra
, "luksDiscard", r
);
3050 return log_error_errno(r
, "Failed to set discard field: %m");
3054 case ARG_LUKS_OFFLINE_DISCARD
:
3055 if (isempty(optarg
)) {
3056 r
= drop_from_identity("luksOfflineDiscard");
3063 r
= parse_boolean(optarg
);
3065 return log_error_errno(r
, "Failed to parse --luks-offline-discard= parameter: %s", optarg
);
3067 r
= json_variant_set_field_boolean(&arg_identity_extra
, "luksOfflineDiscard", r
);
3069 return log_error_errno(r
, "Failed to set offline discard field: %m");
3073 case ARG_LUKS_VOLUME_KEY_SIZE
:
3074 case ARG_LUKS_PBKDF_PARALLEL_THREADS
:
3075 case ARG_RATE_LIMIT_BURST
: {
3077 c
== ARG_LUKS_VOLUME_KEY_SIZE
? "luksVolumeKeySize" :
3078 c
== ARG_LUKS_PBKDF_PARALLEL_THREADS
? "luksPbkdfParallelThreads" :
3079 c
== ARG_RATE_LIMIT_BURST
? "rateLimitBurst" : NULL
;
3084 if (isempty(optarg
)) {
3085 r
= drop_from_identity(field
);
3090 r
= safe_atou(optarg
, &n
);
3092 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
3094 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3096 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3104 if (isempty(optarg
)) {
3105 r
= drop_from_identity("umask");
3112 r
= parse_mode(optarg
, &m
);
3114 return log_error_errno(r
, "Failed to parse umask: %m");
3116 r
= json_variant_set_field_integer(&arg_identity_extra
, "umask", m
);
3118 return log_error_errno(r
, "Failed to set umask field: %m");
3123 case ARG_SSH_AUTHORIZED_KEYS
: {
3124 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
3125 _cleanup_(strv_freep
) char **l
= NULL
, **add
= NULL
;
3127 if (isempty(optarg
)) {
3128 r
= drop_from_identity("sshAuthorizedKeys");
3135 if (optarg
[0] == '@') {
3136 _cleanup_fclose_
FILE *f
= NULL
;
3138 /* If prefixed with '@' read from a file */
3140 f
= fopen(optarg
+1, "re");
3142 return log_error_errno(errno
, "Failed to open '%s': %m", optarg
+1);
3145 _cleanup_free_
char *line
= NULL
;
3147 r
= read_line(f
, LONG_LINE_MAX
, &line
);
3149 return log_error_errno(r
, "Failed to read from '%s': %m", optarg
+1);
3159 r
= strv_consume(&add
, TAKE_PTR(line
));
3164 /* Otherwise, assume it's a literal key. Let's do some superficial checks
3165 * before accept it though. */
3167 if (string_has_cc(optarg
, NULL
))
3168 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Authorized key contains control characters, refusing.");
3169 if (optarg
[0] == '#')
3170 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specified key is a comment?");
3172 add
= strv_new(optarg
);
3177 v
= json_variant_ref(json_variant_by_key(arg_identity_extra_privileged
, "sshAuthorizedKeys"));
3179 r
= json_variant_strv(v
, &l
);
3181 return log_error_errno(r
, "Failed to parse SSH authorized keys list: %m");
3184 r
= strv_extend_strv(&l
, add
, true);
3188 v
= json_variant_unref(v
);
3190 r
= json_variant_new_array_strv(&v
, l
);
3194 r
= json_variant_set_field(&arg_identity_extra_privileged
, "sshAuthorizedKeys", v
);
3196 return log_error_errno(r
, "Failed to set authorized keys: %m");
3201 case ARG_NOT_BEFORE
:
3207 field
= c
== ARG_NOT_BEFORE
? "notBeforeUSec" :
3208 IN_SET(c
, ARG_NOT_AFTER
, 'e') ? "notAfterUSec" : NULL
;
3212 if (isempty(optarg
)) {
3213 r
= drop_from_identity(field
);
3220 /* Note the minor discrepancy regarding -e parsing here: we support that for compat
3221 * reasons, and in the original useradd(8) implementation it accepts dates in the
3222 * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
3223 * with greater precision. */
3224 r
= parse_timestamp(optarg
, &n
);
3226 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
3228 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3230 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3234 case ARG_PASSWORD_CHANGE_MIN
:
3235 case ARG_PASSWORD_CHANGE_MAX
:
3236 case ARG_PASSWORD_CHANGE_WARN
:
3237 case ARG_PASSWORD_CHANGE_INACTIVE
: {
3241 field
= c
== ARG_PASSWORD_CHANGE_MIN
? "passwordChangeMinUSec" :
3242 c
== ARG_PASSWORD_CHANGE_MAX
? "passwordChangeMaxUSec" :
3243 c
== ARG_PASSWORD_CHANGE_WARN
? "passwordChangeWarnUSec" :
3244 c
== ARG_PASSWORD_CHANGE_INACTIVE
? "passwordChangeInactiveUSec" :
3249 if (isempty(optarg
)) {
3250 r
= drop_from_identity(field
);
3257 r
= parse_sec(optarg
, &n
);
3259 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
3261 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3263 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3269 case ARG_LUKS_CIPHER
:
3270 case ARG_LUKS_CIPHER_MODE
:
3271 case ARG_LUKS_PBKDF_TYPE
:
3272 case ARG_LUKS_PBKDF_HASH_ALGORITHM
: {
3275 c
== ARG_STORAGE
? "storage" :
3276 c
== ARG_FS_TYPE
? "fileSystemType" :
3277 c
== ARG_LUKS_CIPHER
? "luksCipher" :
3278 c
== ARG_LUKS_CIPHER_MODE
? "luksCipherMode" :
3279 c
== ARG_LUKS_PBKDF_TYPE
? "luksPbkdfType" :
3280 c
== ARG_LUKS_PBKDF_HASH_ALGORITHM
? "luksPbkdfHashAlgorithm" : NULL
;
3284 if (isempty(optarg
)) {
3285 r
= drop_from_identity(field
);
3292 if (!string_is_safe(optarg
))
3293 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Parameter for %s field not valid: %s", field
, optarg
);
3295 r
= json_variant_set_field_string(
3296 IN_SET(c
, ARG_STORAGE
, ARG_FS_TYPE
) ?
3297 &arg_identity_extra_this_machine
:
3298 &arg_identity_extra
, field
, optarg
);
3300 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3305 case ARG_LUKS_PBKDF_TIME_COST
:
3306 case ARG_RATE_LIMIT_INTERVAL
:
3307 case ARG_STOP_DELAY
: {
3309 c
== ARG_LUKS_PBKDF_TIME_COST
? "luksPbkdfTimeCostUSec" :
3310 c
== ARG_RATE_LIMIT_INTERVAL
? "rateLimitIntervalUSec" :
3311 c
== ARG_STOP_DELAY
? "stopDelayUSec" :
3317 if (isempty(optarg
)) {
3318 r
= drop_from_identity(field
);
3325 r
= parse_sec(optarg
, &t
);
3327 return log_error_errno(r
, "Failed to parse %s field: %s", field
, optarg
);
3329 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, t
);
3331 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3337 const char *p
= optarg
;
3340 r
= drop_from_identity("memberOf");
3348 _cleanup_(json_variant_unrefp
) JsonVariant
*mo
= NULL
;
3349 _cleanup_strv_free_
char **list
= NULL
;
3350 _cleanup_free_
char *word
= NULL
;
3352 r
= extract_first_word(&p
, &word
, ",", 0);
3354 return log_error_errno(r
, "Failed to parse group list: %m");
3358 if (!valid_user_group_name(word
, 0))
3359 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid group name %s.", word
);
3361 mo
= json_variant_ref(json_variant_by_key(arg_identity_extra
, "memberOf"));
3363 r
= json_variant_strv(mo
, &list
);
3365 return log_error_errno(r
, "Failed to parse group list: %m");
3367 r
= strv_extend(&list
, word
);
3374 mo
= json_variant_unref(mo
);
3375 r
= json_variant_new_array_strv(&mo
, list
);
3377 return log_error_errno(r
, "Failed to create group list JSON: %m");
3379 r
= json_variant_set_field(&arg_identity_extra
, "memberOf", mo
);
3381 return log_error_errno(r
, "Failed to update group list: %m");
3387 case ARG_TASKS_MAX
: {
3390 if (isempty(optarg
)) {
3391 r
= drop_from_identity("tasksMax");
3397 r
= safe_atou64(optarg
, &u
);
3399 return log_error_errno(r
, "Failed to parse --tasks-max= parameter: %s", optarg
);
3401 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "tasksMax", u
);
3403 return log_error_errno(r
, "Failed to set tasksMax field: %m");
3408 case ARG_MEMORY_MAX
:
3409 case ARG_MEMORY_HIGH
:
3410 case ARG_LUKS_PBKDF_MEMORY_COST
: {
3412 c
== ARG_MEMORY_MAX
? "memoryMax" :
3413 c
== ARG_MEMORY_HIGH
? "memoryHigh" :
3414 c
== ARG_LUKS_PBKDF_MEMORY_COST
? "luksPbkdfMemoryCost" : NULL
;
3420 if (isempty(optarg
)) {
3421 r
= drop_from_identity(field
);
3427 r
= parse_size(optarg
, 1024, &u
);
3429 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
3431 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, field
, u
);
3433 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3438 case ARG_CPU_WEIGHT
:
3439 case ARG_IO_WEIGHT
: {
3440 const char *field
= c
== ARG_CPU_WEIGHT
? "cpuWeight" :
3441 c
== ARG_IO_WEIGHT
? "ioWeight" : NULL
;
3446 if (isempty(optarg
)) {
3447 r
= drop_from_identity(field
);
3453 r
= safe_atou64(optarg
, &u
);
3455 return log_error_errno(r
, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg
);
3457 if (!CGROUP_WEIGHT_IS_OK(u
))
3458 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Weight %" PRIu64
" is out of valid weight range.", u
);
3460 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, u
);
3462 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3467 case ARG_PKCS11_TOKEN_URI
: {
3470 if (streq(optarg
, "list"))
3471 return pkcs11_list_tokens();
3473 /* If --pkcs11-token-uri= is specified we always drop everything old */
3474 FOREACH_STRING(p
, "pkcs11TokenUri", "pkcs11EncryptedKey") {
3475 r
= drop_from_identity(p
);
3480 if (isempty(optarg
)) {
3481 arg_pkcs11_token_uri
= strv_free(arg_pkcs11_token_uri
);
3485 if (streq(optarg
, "auto")) {
3486 _cleanup_free_
char *found
= NULL
;
3488 r
= pkcs11_find_token_auto(&found
);
3491 r
= strv_consume(&arg_pkcs11_token_uri
, TAKE_PTR(found
));
3493 if (!pkcs11_uri_valid(optarg
))
3494 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Not a valid PKCS#11 URI: %s", optarg
);
3496 r
= strv_extend(&arg_pkcs11_token_uri
, optarg
);
3501 strv_uniq(arg_pkcs11_token_uri
);
3505 case ARG_FIDO2_DEVICE
: {
3508 if (streq(optarg
, "list"))
3509 return fido2_list_devices();
3511 FOREACH_STRING(p
, "fido2HmacCredential", "fido2HmacSalt") {
3512 r
= drop_from_identity(p
);
3517 if (isempty(optarg
)) {
3518 arg_fido2_device
= strv_free(arg_fido2_device
);
3522 if (streq(optarg
, "auto")) {
3523 _cleanup_free_
char *found
= NULL
;
3525 r
= fido2_find_device_auto(&found
);
3529 r
= strv_consume(&arg_fido2_device
, TAKE_PTR(found
));
3531 r
= strv_extend(&arg_fido2_device
, optarg
);
3535 strv_uniq(arg_fido2_device
);
3539 case ARG_FIDO2_WITH_PIN
: {
3542 r
= parse_boolean_argument("--fido2-with-client-pin=", optarg
, &lock_with_pin
);
3546 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_PIN
, lock_with_pin
);
3550 case ARG_FIDO2_WITH_UP
: {
3553 r
= parse_boolean_argument("--fido2-with-user-presence=", optarg
, &lock_with_up
);
3557 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UP
, lock_with_up
);
3561 case ARG_FIDO2_WITH_UV
: {
3564 r
= parse_boolean_argument("--fido2-with-user-verification=", optarg
, &lock_with_uv
);
3568 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UV
, lock_with_uv
);
3572 case ARG_RECOVERY_KEY
: {
3575 r
= parse_boolean(optarg
);
3577 return log_error_errno(r
, "Failed to parse --recovery-key= argument: %s", optarg
);
3579 arg_recovery_key
= r
;
3581 FOREACH_STRING(p
, "recoveryKey", "recoveryKeyType") {
3582 r
= drop_from_identity(p
);
3590 case ARG_AUTO_RESIZE_MODE
:
3591 if (isempty(optarg
)) {
3592 r
= drop_from_identity("autoResizeMode");
3599 r
= auto_resize_mode_from_string(optarg
);
3601 return log_error_errno(r
, "Failed to parse --auto-resize-mode= argument: %s", optarg
);
3603 r
= json_variant_set_field_string(&arg_identity_extra
, "autoResizeMode", auto_resize_mode_to_string(r
));
3605 return log_error_errno(r
, "Failed to set autoResizeMode field: %m");
3609 case ARG_REBALANCE_WEIGHT
: {
3612 if (isempty(optarg
)) {
3613 r
= drop_from_identity("rebalanceWeight");
3618 if (streq(optarg
, "off"))
3619 u
= REBALANCE_WEIGHT_OFF
;
3621 r
= safe_atou64(optarg
, &u
);
3623 return log_error_errno(r
, "Failed to parse --rebalance-weight= argument: %s", optarg
);
3625 if (u
< REBALANCE_WEIGHT_MIN
|| u
> REBALANCE_WEIGHT_MAX
)
3626 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
), "Rebalancing weight out of valid range %" PRIu64
"…%" PRIu64
": %s",
3627 REBALANCE_WEIGHT_MIN
, REBALANCE_WEIGHT_MAX
, optarg
);
3630 /* Drop from per machine stuff and everywhere */
3631 r
= drop_from_identity("rebalanceWeight");
3635 /* Add to main identity */
3636 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "rebalanceWeight", u
);
3638 return log_error_errno(r
, "Failed to set rebalanceWeight field: %m");
3644 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
3648 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
3655 if (arg_export_format
== EXPORT_FORMAT_FULL
)
3656 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
3657 else if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
3658 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
3660 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specifying -E more than twice is not supported.");
3662 arg_json_format_flags
&= ~JSON_FORMAT_OFF
;
3663 if (arg_json_format_flags
== 0)
3664 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
3667 case ARG_EXPORT_FORMAT
:
3668 if (streq(optarg
, "full"))
3669 arg_export_format
= EXPORT_FORMAT_FULL
;
3670 else if (streq(optarg
, "stripped"))
3671 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
3672 else if (streq(optarg
, "minimal"))
3673 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
3674 else if (streq(optarg
, "help")) {
3683 case ARG_AND_RESIZE
:
3684 arg_and_resize
= true;
3687 case ARG_AND_CHANGE_PASSWORD
:
3688 arg_and_change_password
= true;
3691 case ARG_DROP_CACHES
: {
3694 if (isempty(optarg
)) {
3695 r
= drop_from_identity("dropCaches");
3700 r
= parse_boolean_argument("--drop-caches=", optarg
, &drop_caches
);
3704 r
= json_variant_set_field_boolean(&arg_identity_extra
, "dropCaches", r
);
3706 return log_error_errno(r
, "Failed to set drop caches field: %m");
3715 assert_not_reached();
3719 if (!strv_isempty(arg_pkcs11_token_uri
) || !strv_isempty(arg_fido2_device
))
3720 arg_and_change_password
= true;
3722 if (arg_disk_size
!= UINT64_MAX
|| arg_disk_size_relative
!= UINT64_MAX
)
3723 arg_and_resize
= true;
3728 static int redirect_bus_mgr(void) {
3731 /* Talk to a different service if that's requested. (The same env var is also understood by homed, so
3732 * that it is relatively easily possible to invoke a second instance of homed for debug purposes and
3733 * have homectl talk to it, without colliding with the host version. This is handy when operating
3734 * from a homed-managed account.) */
3736 suffix
= getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
3738 static BusLocator locator
= {
3739 .path
= "/org/freedesktop/home1",
3740 .interface
= "org.freedesktop.home1.Manager",
3743 /* Yes, we leak this memory, but there's little point to collect this, given that we only do
3744 * this in a debug environment, do it only once, and the string shall live for out entire
3745 * process runtime. */
3747 locator
.destination
= strjoin("org.freedesktop.home1.", suffix
);
3748 if (!locator
.destination
)
3753 bus_mgr
= bus_home_mgr
;
3758 static int run(int argc
, char *argv
[]) {
3759 static const Verb verbs
[] = {
3760 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
3761 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_homes
},
3762 { "activate", 2, VERB_ANY
, 0, activate_home
},
3763 { "deactivate", 2, VERB_ANY
, 0, deactivate_home
},
3764 { "inspect", VERB_ANY
, VERB_ANY
, 0, inspect_home
},
3765 { "authenticate", VERB_ANY
, VERB_ANY
, 0, authenticate_home
},
3766 { "create", VERB_ANY
, 2, 0, create_home
},
3767 { "remove", 2, VERB_ANY
, 0, remove_home
},
3768 { "update", VERB_ANY
, 2, 0, update_home
},
3769 { "passwd", VERB_ANY
, 2, 0, passwd_home
},
3770 { "resize", 2, 3, 0, resize_home
},
3771 { "lock", 2, VERB_ANY
, 0, lock_home
},
3772 { "unlock", 2, VERB_ANY
, 0, unlock_home
},
3773 { "with", 2, VERB_ANY
, 0, with_home
},
3774 { "lock-all", VERB_ANY
, 1, 0, lock_all_homes
},
3775 { "deactivate-all", VERB_ANY
, 1, 0, deactivate_all_homes
},
3776 { "rebalance", VERB_ANY
, 1, 0, rebalance
},
3784 r
= redirect_bus_mgr();
3788 r
= parse_argv(argc
, argv
);
3792 return dispatch_verb(argc
, argv
, verbs
, NULL
);
3795 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);