1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "ask-password-api.h"
9 #include "bus-common-errors.h"
10 #include "bus-error.h"
11 #include "bus-locator.h"
13 #include "capability-util.h"
14 #include "cgroup-util.h"
16 #include "creds-util.h"
17 #include "dirent-util.h"
18 #include "dns-domain.h"
22 #include "format-table.h"
24 #include "glyph-util.h"
26 #include "home-util.h"
27 #include "homectl-fido2.h"
28 #include "homectl-pkcs11.h"
29 #include "homectl-recovery-key.h"
30 #include "libfido2-util.h"
31 #include "locale-util.h"
32 #include "main-func.h"
33 #include "memory-util.h"
35 #include "parse-argument.h"
36 #include "parse-util.h"
37 #include "password-quality-util.h"
38 #include "path-util.h"
39 #include "percent-util.h"
40 #include "pkcs11-util.h"
41 #include "pretty-print.h"
42 #include "proc-cmdline.h"
43 #include "process-util.h"
44 #include "recurse-dir.h"
45 #include "rlimit-util.h"
47 #include "spawn-polkit-agent.h"
48 #include "terminal-util.h"
49 #include "tmpfile-util.h"
50 #include "uid-classification.h"
51 #include "user-record.h"
52 #include "user-record-password-quality.h"
53 #include "user-record-show.h"
54 #include "user-record-util.h"
55 #include "user-util.h"
59 static PagerFlags arg_pager_flags
= 0;
60 static bool arg_legend
= true;
61 static bool arg_ask_password
= true;
62 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
63 static const char *arg_host
= NULL
;
64 static bool arg_offline
= false;
65 static const char *arg_identity
= NULL
;
66 static JsonVariant
*arg_identity_extra
= NULL
;
67 static JsonVariant
*arg_identity_extra_privileged
= NULL
;
68 static JsonVariant
*arg_identity_extra_this_machine
= NULL
;
69 static JsonVariant
*arg_identity_extra_rlimits
= NULL
;
70 static char **arg_identity_filter
= NULL
; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
71 static char **arg_identity_filter_rlimits
= NULL
;
72 static uint64_t arg_disk_size
= UINT64_MAX
;
73 static uint64_t arg_disk_size_relative
= UINT64_MAX
;
74 static char **arg_pkcs11_token_uri
= NULL
;
75 static char **arg_fido2_device
= NULL
;
76 static Fido2EnrollFlags arg_fido2_lock_with
= FIDO2ENROLL_PIN
| FIDO2ENROLL_UP
;
78 static int arg_fido2_cred_alg
= COSE_ES256
;
80 static int arg_fido2_cred_alg
= 0;
82 static bool arg_recovery_key
= false;
83 static JsonFormatFlags arg_json_format_flags
= JSON_FORMAT_OFF
;
84 static bool arg_and_resize
= false;
85 static bool arg_and_change_password
= false;
87 EXPORT_FORMAT_FULL
, /* export the full record */
88 EXPORT_FORMAT_STRIPPED
, /* strip "state" + "binding", but leave signature in place */
89 EXPORT_FORMAT_MINIMAL
, /* also strip signature */
90 } arg_export_format
= EXPORT_FORMAT_FULL
;
91 static uint64_t arg_capability_bounding_set
= UINT64_MAX
;
92 static uint64_t arg_capability_ambient_set
= UINT64_MAX
;
93 static bool arg_prompt_new_user
= false;
94 static char *arg_blob_dir
= NULL
;
95 static bool arg_blob_clear
= false;
96 static Hashmap
*arg_blob_files
= NULL
;
98 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra
, json_variant_unrefp
);
99 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine
, json_variant_unrefp
);
100 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged
, json_variant_unrefp
);
101 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits
, json_variant_unrefp
);
102 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter
, strv_freep
);
103 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits
, strv_freep
);
104 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri
, strv_freep
);
105 STATIC_DESTRUCTOR_REGISTER(arg_fido2_device
, strv_freep
);
106 STATIC_DESTRUCTOR_REGISTER(arg_blob_dir
, freep
);
107 STATIC_DESTRUCTOR_REGISTER(arg_blob_files
, hashmap_freep
);
109 static const BusLocator
*bus_mgr
;
111 static bool identity_properties_specified(void) {
114 !json_variant_is_blank_object(arg_identity_extra
) ||
115 !json_variant_is_blank_object(arg_identity_extra_privileged
) ||
116 !json_variant_is_blank_object(arg_identity_extra_this_machine
) ||
117 !json_variant_is_blank_object(arg_identity_extra_rlimits
) ||
118 !strv_isempty(arg_identity_filter
) ||
119 !strv_isempty(arg_identity_filter_rlimits
) ||
120 !strv_isempty(arg_pkcs11_token_uri
) ||
121 !strv_isempty(arg_fido2_device
) ||
124 !hashmap_isempty(arg_blob_files
);
127 static int acquire_bus(sd_bus
**bus
) {
135 r
= bus_connect_transport(arg_transport
, arg_host
, RUNTIME_SCOPE_SYSTEM
, bus
);
137 return bus_log_connect_error(r
, arg_transport
);
139 (void) sd_bus_set_allow_interactive_authorization(*bus
, arg_ask_password
);
144 static int list_homes(int argc
, char *argv
[], void *userdata
) {
145 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
146 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
147 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
148 _cleanup_(table_unrefp
) Table
*table
= NULL
;
151 r
= acquire_bus(&bus
);
155 r
= bus_call_method(bus
, bus_mgr
, "ListHomes", &error
, &reply
, NULL
);
157 return log_error_errno(r
, "Failed to list homes: %s", bus_error_message(&error
, r
));
159 table
= table_new("name", "uid", "gid", "state", "realname", "home", "shell");
163 r
= sd_bus_message_enter_container(reply
, 'a', "(susussso)");
165 return bus_log_parse_error(r
);
168 const char *name
, *state
, *realname
, *home
, *shell
, *color
;
172 r
= sd_bus_message_read(reply
, "(susussso)", &name
, &uid
, &state
, &gid
, &realname
, &home
, &shell
, NULL
);
174 return bus_log_parse_error(r
);
178 r
= table_add_many(table
,
183 return table_log_add_error(r
);
186 r
= table_add_cell(table
, &cell
, TABLE_STRING
, state
);
188 return table_log_add_error(r
);
190 color
= user_record_state_color(state
);
192 (void) table_set_color(table
, cell
, color
);
194 r
= table_add_many(table
,
195 TABLE_STRING
, strna(empty_to_null(realname
)),
197 TABLE_STRING
, strna(empty_to_null(shell
)));
199 return table_log_add_error(r
);
202 r
= sd_bus_message_exit_container(reply
);
204 return bus_log_parse_error(r
);
206 if (!table_isempty(table
) || !FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
207 r
= table_set_sort(table
, (size_t) 0);
209 return table_log_sort_error(r
);
211 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, arg_legend
);
216 if (arg_legend
&& !FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
217 if (table_isempty(table
))
218 printf("No home areas.\n");
220 printf("\n%zu home areas listed.\n", table_get_rows(table
) - 1);
226 static int acquire_existing_password(
227 const char *user_name
,
229 bool emphasize_current
,
230 AskPasswordFlags flags
) {
232 _cleanup_strv_free_erase_
char **password
= NULL
;
233 _cleanup_(erase_and_freep
) char *envpw
= NULL
;
234 _cleanup_free_
char *question
= NULL
;
240 r
= getenv_steal_erase("PASSWORD", &envpw
);
242 return log_error_errno(r
, "Failed to acquire password from environment: %m");
244 /* People really shouldn't use environment variables for passing passwords. We support this
245 * only for testing purposes, and do not document the behaviour, so that people won't
246 * actually use this outside of testing. */
248 r
= user_record_set_password(hr
, STRV_MAKE(envpw
), true);
250 return log_error_errno(r
, "Failed to store password: %m");
255 /* If this is not our own user, then don't use the password cache */
256 if (is_this_me(user_name
) <= 0)
257 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
259 if (asprintf(&question
, emphasize_current
?
260 "Please enter current password for user %s:" :
261 "Please enter password for user %s:",
265 AskPasswordRequest req
= {
268 .keyring
= "home-password",
269 .credential
= "home.password",
272 r
= ask_password_auto(&req
, USEC_INFINITY
, flags
, &password
);
273 if (r
== -EUNATCH
) { /* EUNATCH is returned if no password was found and asking interactively was
274 * disabled via the flags. Not an error for us. */
275 log_debug_errno(r
, "No passwords acquired.");
279 return log_error_errno(r
, "Failed to acquire password: %m");
281 r
= user_record_set_password(hr
, password
, true);
283 return log_error_errno(r
, "Failed to store password: %m");
288 static int acquire_recovery_key(
289 const char *user_name
,
291 AskPasswordFlags flags
) {
293 _cleanup_strv_free_erase_
char **recovery_key
= NULL
;
294 _cleanup_(erase_and_freep
) char *envpw
= NULL
;
295 _cleanup_free_
char *question
= NULL
;
301 r
= getenv_steal_erase("PASSWORD", &envpw
);
303 return log_error_errno(r
, "Failed to acquire password from environment: %m");
305 /* People really shouldn't use environment variables for passing secrets. We support this
306 * only for testing purposes, and do not document the behaviour, so that people won't
307 * actually use this outside of testing. */
309 r
= user_record_set_password(hr
, STRV_MAKE(envpw
), true); /* recovery keys are stored in the record exactly like regular passwords! */
311 return log_error_errno(r
, "Failed to store recovery key: %m");
316 /* If this is not our own user, then don't use the password cache */
317 if (is_this_me(user_name
) <= 0)
318 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
320 if (asprintf(&question
, "Please enter recovery key for user %s:", user_name
) < 0)
323 AskPasswordRequest req
= {
326 .keyring
= "home-recovery-key",
327 .credential
= "home.recovery-key",
330 r
= ask_password_auto(&req
, USEC_INFINITY
, flags
, &recovery_key
);
331 if (r
== -EUNATCH
) { /* EUNATCH is returned if no recovery key was found and asking interactively was
332 * disabled via the flags. Not an error for us. */
333 log_debug_errno(r
, "No recovery keys acquired.");
337 return log_error_errno(r
, "Failed to acquire recovery keys: %m");
339 r
= user_record_set_password(hr
, recovery_key
, true);
341 return log_error_errno(r
, "Failed to store recovery keys: %m");
346 static int acquire_token_pin(
347 const char *user_name
,
349 AskPasswordFlags flags
) {
351 _cleanup_strv_free_erase_
char **pin
= NULL
;
352 _cleanup_(erase_and_freep
) char *envpin
= NULL
;
353 _cleanup_free_
char *question
= NULL
;
359 r
= getenv_steal_erase("PIN", &envpin
);
361 return log_error_errno(r
, "Failed to acquire PIN from environment: %m");
363 r
= user_record_set_token_pin(hr
, STRV_MAKE(envpin
), false);
365 return log_error_errno(r
, "Failed to store token PIN: %m");
370 /* If this is not our own user, then don't use the password cache */
371 if (is_this_me(user_name
) <= 0)
372 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
374 if (asprintf(&question
, "Please enter security token PIN for user %s:", user_name
) < 0)
377 AskPasswordRequest req
= {
380 .keyring
= "token-pin",
381 .credential
= "home.token-pin",
384 r
= ask_password_auto(&req
, USEC_INFINITY
, flags
, &pin
);
385 if (r
== -EUNATCH
) { /* EUNATCH is returned if no PIN was found and asking interactively was disabled
386 * via the flags. Not an error for us. */
387 log_debug_errno(r
, "No security token PINs acquired.");
391 return log_error_errno(r
, "Failed to acquire security token PIN: %m");
393 r
= user_record_set_token_pin(hr
, pin
, false);
395 return log_error_errno(r
, "Failed to store security token PIN: %m");
400 static int handle_generic_user_record_error(
401 const char *user_name
,
403 const sd_bus_error
*error
,
405 bool emphasize_current_password
) {
411 if (sd_bus_error_has_name(error
, BUS_ERROR_HOME_ABSENT
))
412 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE
),
413 "Home of user %s is currently absent, please plug in the necessary storage device or backing file system.", user_name
);
415 else if (sd_bus_error_has_name(error
, BUS_ERROR_AUTHENTICATION_LIMIT_HIT
))
416 return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS
),
417 "Too frequent login attempts for user %s, try again later.", user_name
);
419 else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD
)) {
421 if (!strv_isempty(hr
->password
))
422 log_notice("Password incorrect or not sufficient, please try again.");
424 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
425 * let's push what we acquire here into the cache */
426 r
= acquire_existing_password(
429 emphasize_current_password
,
430 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
434 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_RECOVERY_KEY
)) {
436 if (!strv_isempty(hr
->password
))
437 log_notice("Recovery key incorrect or not sufficient, please try again.");
439 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
440 * let's push what we acquire here into the cache */
441 r
= acquire_recovery_key(
444 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
448 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
)) {
450 if (strv_isempty(hr
->password
))
451 log_notice("Security token not inserted, please enter password.");
453 log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
455 r
= acquire_existing_password(
458 emphasize_current_password
,
459 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
463 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_NEEDED
)) {
465 /* First time the PIN is requested, let's accept cached data, and allow using credential store */
466 r
= acquire_token_pin(
469 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_PUSH_CACHE
);
473 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED
)) {
475 log_notice("%s%sPlease authenticate physically on security token.",
476 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
477 emoji_enabled() ? " " : "");
479 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, true);
481 return log_error_errno(r
, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
483 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED
)) {
485 log_notice("%s%sPlease confirm presence on security token.",
486 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
487 emoji_enabled() ? " " : "");
489 r
= user_record_set_fido2_user_presence_permitted(hr
, true);
491 return log_error_errno(r
, "Failed to set FIDO2 user presence permitted flag: %m");
493 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED
)) {
495 log_notice("%s%sPlease verify user on security token.",
496 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
497 emoji_enabled() ? " " : "");
499 r
= user_record_set_fido2_user_verification_permitted(hr
, true);
501 return log_error_errno(r
, "Failed to set FIDO2 user verification permitted flag: %m");
503 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_LOCKED
))
504 return log_error_errno(SYNTHETIC_ERRNO(EPERM
), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
506 else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN
)) {
508 log_notice("Security token PIN incorrect, please try again.");
510 /* If the previous PIN was wrong don't accept cached info anymore, but add to cache. Also, don't use the credential data */
511 r
= acquire_token_pin(
514 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
518 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT
)) {
520 log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
522 r
= acquire_token_pin(
525 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
529 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT
)) {
531 log_notice("Security token PIN incorrect, please try again (only one try left!).");
533 r
= acquire_token_pin(
536 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
540 return log_error_errno(ret
, "Operation on home %s failed: %s", user_name
, bus_error_message(error
, ret
));
545 static int acquire_passed_secrets(const char *user_name
, UserRecord
**ret
) {
546 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
551 /* Generates an initial secret objects that contains passwords supplied via $PASSWORD, the password
552 * cache or the credentials subsystem, but excluding any interactive stuff. If nothing is passed,
553 * returns an empty secret object. */
555 secret
= user_record_new();
559 r
= acquire_existing_password(
562 /* emphasize_current_password = */ false,
563 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
567 r
= acquire_token_pin(
570 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
574 r
= acquire_recovery_key(
577 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
581 *ret
= TAKE_PTR(secret
);
585 static int activate_home(int argc
, char *argv
[], void *userdata
) {
586 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
589 r
= acquire_bus(&bus
);
593 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
594 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
596 r
= acquire_passed_secrets(*i
, &secret
);
601 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
602 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
604 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ActivateHome");
606 return bus_log_create_error(r
);
608 r
= sd_bus_message_append(m
, "s", *i
);
610 return bus_log_create_error(r
);
612 r
= bus_message_append_secret(m
, secret
);
614 return bus_log_create_error(r
);
616 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
618 r
= handle_generic_user_record_error(*i
, secret
, &error
, r
, /* emphasize_current_password= */ false);
633 static int deactivate_home(int argc
, char *argv
[], void *userdata
) {
634 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
637 r
= acquire_bus(&bus
);
641 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
642 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
643 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
645 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "DeactivateHome");
647 return bus_log_create_error(r
);
649 r
= sd_bus_message_append(m
, "s", *i
);
651 return bus_log_create_error(r
);
653 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
655 log_error_errno(r
, "Failed to deactivate user home: %s", bus_error_message(&error
, r
));
664 static void dump_home_record(UserRecord
*hr
) {
669 if (hr
->incomplete
) {
671 log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr
->user_name
);
674 if (arg_json_format_flags
& JSON_FORMAT_OFF
)
675 user_record_show(hr
, true);
677 _cleanup_(user_record_unrefp
) UserRecord
*stripped
= NULL
;
679 if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
680 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_EMBEDDED
|USER_RECORD_PERMISSIVE
, &stripped
);
681 else if (arg_export_format
== EXPORT_FORMAT_MINIMAL
)
682 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_SIGNABLE
|USER_RECORD_PERMISSIVE
, &stripped
);
686 log_warning_errno(r
, "Failed to strip user record, ignoring: %m");
690 json_variant_dump(hr
->json
, arg_json_format_flags
, stdout
, NULL
);
694 static char **mangle_user_list(char **list
, char ***ret_allocated
) {
695 _cleanup_free_
char *myself
= NULL
;
698 if (!strv_isempty(list
)) {
699 *ret_allocated
= NULL
;
703 myself
= getusername_malloc();
711 l
[0] = TAKE_PTR(myself
);
718 static int inspect_home(int argc
, char *argv
[], void *userdata
) {
719 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
720 _cleanup_strv_free_
char **mangled_list
= NULL
;
724 pager_open(arg_pager_flags
);
726 r
= acquire_bus(&bus
);
730 items
= mangle_user_list(strv_skip(argv
, 1), &mangled_list
);
734 STRV_FOREACH(i
, items
) {
735 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
736 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
737 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
738 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
743 r
= parse_uid(*i
, &uid
);
745 if (!valid_user_group_name(*i
, 0)) {
746 log_error("Invalid user name '%s'.", *i
);
753 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByName", &error
, &reply
, "s", *i
);
755 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByUID", &error
, &reply
, "u", (uint32_t) uid
);
757 log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
764 r
= sd_bus_message_read(reply
, "sbo", &json
, &incomplete
, NULL
);
766 bus_log_parse_error(r
);
773 r
= json_parse(json
, JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
775 log_error_errno(r
, "Failed to parse JSON identity: %m");
782 hr
= user_record_new();
786 r
= user_record_load(hr
, v
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_LOG
|USER_RECORD_PERMISSIVE
);
794 hr
->incomplete
= incomplete
;
795 dump_home_record(hr
);
801 static int authenticate_home(int argc
, char *argv
[], void *userdata
) {
802 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
803 _cleanup_strv_free_
char **mangled_list
= NULL
;
807 items
= mangle_user_list(strv_skip(argv
, 1), &mangled_list
);
811 r
= acquire_bus(&bus
);
815 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
817 STRV_FOREACH(i
, items
) {
818 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
820 r
= acquire_passed_secrets(*i
, &secret
);
825 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
826 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
828 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "AuthenticateHome");
830 return bus_log_create_error(r
);
832 r
= sd_bus_message_append(m
, "s", *i
);
834 return bus_log_create_error(r
);
836 r
= bus_message_append_secret(m
, secret
);
838 return bus_log_create_error(r
);
840 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
842 r
= handle_generic_user_record_error(*i
, secret
, &error
, r
, false);
857 static int update_last_change(JsonVariant
**v
, bool with_password
, bool override
) {
864 n
= now(CLOCK_REALTIME
);
866 c
= json_variant_by_key(*v
, "lastChangeUSec");
871 goto update_password
;
873 if (!json_variant_is_unsigned(c
))
874 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec field is not an unsigned integer, refusing.");
876 u
= json_variant_unsigned(c
);
878 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec is from the future, can't update.");
881 r
= json_variant_set_field_unsigned(v
, "lastChangeUSec", n
);
883 return log_error_errno(r
, "Failed to update lastChangeUSec: %m");
889 c
= json_variant_by_key(*v
, "lastPasswordChangeUSec");
896 if (!json_variant_is_unsigned(c
))
897 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec field is not an unsigned integer, refusing.");
899 u
= json_variant_unsigned(c
);
901 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec is from the future, can't update.");
904 r
= json_variant_set_field_unsigned(v
, "lastPasswordChangeUSec", n
);
906 return log_error_errno(r
, "Failed to update lastPasswordChangeUSec: %m");
911 static int apply_identity_changes(JsonVariant
**_v
) {
912 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
917 v
= json_variant_ref(*_v
);
919 r
= json_variant_filter(&v
, arg_identity_filter
);
921 return log_error_errno(r
, "Failed to filter identity: %m");
923 r
= json_variant_merge_object(&v
, arg_identity_extra
);
925 return log_error_errno(r
, "Failed to merge identities: %m");
927 if (arg_identity_extra_this_machine
|| !strv_isempty(arg_identity_filter
)) {
928 _cleanup_(json_variant_unrefp
) JsonVariant
*per_machine
= NULL
, *mmid
= NULL
;
931 r
= sd_id128_get_machine(&mid
);
933 return log_error_errno(r
, "Failed to acquire machine ID: %m");
935 r
= json_variant_new_string(&mmid
, SD_ID128_TO_STRING(mid
));
937 return log_error_errno(r
, "Failed to allocate matchMachineId object: %m");
939 per_machine
= json_variant_ref(json_variant_by_key(v
, "perMachine"));
941 _cleanup_(json_variant_unrefp
) JsonVariant
*npm
= NULL
, *add
= NULL
;
942 _cleanup_free_ JsonVariant
**array
= NULL
;
946 if (!json_variant_is_array(per_machine
))
947 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine field is not an array, refusing.");
949 array
= new(JsonVariant
*, json_variant_elements(per_machine
) + 1);
953 JSON_VARIANT_ARRAY_FOREACH(z
, per_machine
) {
956 if (!json_variant_is_object(z
))
957 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine entry is not an object, refusing.");
961 u
= json_variant_by_key(z
, "matchMachineId");
965 if (!json_variant_equal(u
, mmid
))
968 r
= json_variant_merge_object(&add
, z
);
970 return log_error_errno(r
, "Failed to merge perMachine entry: %m");
975 r
= json_variant_filter(&add
, arg_identity_filter
);
977 return log_error_errno(r
, "Failed to filter perMachine: %m");
979 r
= json_variant_merge_object(&add
, arg_identity_extra_this_machine
);
981 return log_error_errno(r
, "Failed to merge in perMachine fields: %m");
983 if (arg_identity_filter_rlimits
|| arg_identity_extra_rlimits
) {
984 _cleanup_(json_variant_unrefp
) JsonVariant
*rlv
= NULL
;
986 rlv
= json_variant_ref(json_variant_by_key(add
, "resourceLimits"));
988 r
= json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
990 return log_error_errno(r
, "Failed to filter resource limits: %m");
992 r
= json_variant_merge_object(&rlv
, arg_identity_extra_rlimits
);
994 return log_error_errno(r
, "Failed to set resource limits: %m");
996 if (json_variant_is_blank_object(rlv
)) {
997 r
= json_variant_filter(&add
, STRV_MAKE("resourceLimits"));
999 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
1001 r
= json_variant_set_field(&add
, "resourceLimits", rlv
);
1003 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
1007 if (!json_variant_is_blank_object(add
)) {
1008 r
= json_variant_set_field(&add
, "matchMachineId", mmid
);
1010 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
1015 r
= json_variant_new_array(&npm
, array
, i
);
1017 return log_error_errno(r
, "Failed to allocate new perMachine array: %m");
1019 json_variant_unref(per_machine
);
1020 per_machine
= TAKE_PTR(npm
);
1022 _cleanup_(json_variant_unrefp
) JsonVariant
*item
= json_variant_ref(arg_identity_extra_this_machine
);
1024 if (arg_identity_extra_rlimits
) {
1025 r
= json_variant_set_field(&item
, "resourceLimits", arg_identity_extra_rlimits
);
1027 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
1030 r
= json_variant_set_field(&item
, "matchMachineId", mmid
);
1032 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
1034 r
= json_variant_append_array(&per_machine
, item
);
1036 return log_error_errno(r
, "Failed to append to perMachine array: %m");
1039 r
= json_variant_set_field(&v
, "perMachine", per_machine
);
1041 return log_error_errno(r
, "Failed to update per machine record: %m");
1044 if (arg_identity_extra_privileged
|| arg_identity_filter
) {
1045 _cleanup_(json_variant_unrefp
) JsonVariant
*privileged
= NULL
;
1047 privileged
= json_variant_ref(json_variant_by_key(v
, "privileged"));
1049 r
= json_variant_filter(&privileged
, arg_identity_filter
);
1051 return log_error_errno(r
, "Failed to filter identity (privileged part): %m");
1053 r
= json_variant_merge_object(&privileged
, arg_identity_extra_privileged
);
1055 return log_error_errno(r
, "Failed to merge identities (privileged part): %m");
1057 if (json_variant_is_blank_object(privileged
)) {
1058 r
= json_variant_filter(&v
, STRV_MAKE("privileged"));
1060 return log_error_errno(r
, "Failed to drop privileged part from identity: %m");
1062 r
= json_variant_set_field(&v
, "privileged", privileged
);
1064 return log_error_errno(r
, "Failed to update privileged part of identity: %m");
1068 if (arg_identity_filter_rlimits
) {
1069 _cleanup_(json_variant_unrefp
) JsonVariant
*rlv
= NULL
;
1071 rlv
= json_variant_ref(json_variant_by_key(v
, "resourceLimits"));
1073 r
= json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
1075 return log_error_errno(r
, "Failed to filter resource limits: %m");
1077 /* Note that we only filter resource limits here, but don't apply them. We do that in the perMachine section */
1079 if (json_variant_is_blank_object(rlv
)) {
1080 r
= json_variant_filter(&v
, STRV_MAKE("resourceLimits"));
1082 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
1084 r
= json_variant_set_field(&v
, "resourceLimits", rlv
);
1086 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
1090 json_variant_unref(*_v
);
1096 static int add_disposition(JsonVariant
**v
) {
1101 if (json_variant_by_key(*v
, "disposition"))
1104 /* Set the disposition to regular, if not configured explicitly */
1105 r
= json_variant_set_field_string(v
, "disposition", "regular");
1107 return log_error_errno(r
, "Failed to set disposition field: %m");
1112 static int acquire_new_home_record(JsonVariant
*input
, UserRecord
**ret
) {
1113 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
1114 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1120 unsigned line
, column
;
1123 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Two identity records specified, refusing.");
1125 r
= json_parse_file(
1126 streq(arg_identity
, "-") ? stdin
: NULL
,
1127 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, JSON_PARSE_SENSITIVE
, &v
, &line
, &column
);
1129 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
1131 v
= json_variant_ref(input
);
1133 r
= apply_identity_changes(&v
);
1137 r
= add_disposition(&v
);
1141 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
1142 r
= identity_add_pkcs11_key_data(&v
, *i
);
1147 STRV_FOREACH(i
, arg_fido2_device
) {
1148 r
= identity_add_fido2_parameters(&v
, *i
, arg_fido2_lock_with
, arg_fido2_cred_alg
);
1153 if (arg_recovery_key
) {
1154 r
= identity_add_recovery_key(&v
);
1159 r
= update_last_change(&v
, true, false);
1164 json_variant_dump(v
, JSON_FORMAT_PRETTY
, NULL
, NULL
);
1166 hr
= user_record_new();
1170 r
= user_record_load(
1173 USER_RECORD_REQUIRE_REGULAR
|
1174 USER_RECORD_ALLOW_SECRET
|
1175 USER_RECORD_ALLOW_PRIVILEGED
|
1176 USER_RECORD_ALLOW_PER_MACHINE
|
1177 USER_RECORD_STRIP_BINDING
|
1178 USER_RECORD_STRIP_STATUS
|
1179 USER_RECORD_STRIP_SIGNATURE
|
1181 USER_RECORD_PERMISSIVE
);
1185 *ret
= TAKE_PTR(hr
);
1189 static int acquire_new_password(
1190 const char *user_name
,
1195 _cleanup_(erase_and_freep
) char *envpw
= NULL
;
1202 r
= getenv_steal_erase("NEWPASSWORD", &envpw
);
1204 return log_error_errno(r
, "Failed to acquire password from environment: %m");
1206 /* As above, this is not for use, just for testing */
1208 r
= user_record_set_password(hr
, STRV_MAKE(envpw
), /* prepend = */ true);
1210 return log_error_errno(r
, "Failed to store password: %m");
1213 *ret
= TAKE_PTR(envpw
);
1219 (void) suggest_passwords();
1222 _cleanup_strv_free_erase_
char **first
= NULL
, **second
= NULL
;
1223 _cleanup_free_
char *question
= NULL
;
1226 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY
), "Too many attempts, giving up:");
1228 if (asprintf(&question
, "Please enter new password for user %s:", user_name
) < 0)
1231 AskPasswordRequest req
= {
1232 .message
= question
,
1233 .icon
= "user-home",
1234 .keyring
= "home-password",
1235 .credential
= "home.new-password",
1238 r
= ask_password_auto(
1241 /* flags= */ 0, /* no caching, we want to collect a new password here after all */
1244 return log_error_errno(r
, "Failed to acquire password: %m");
1246 question
= mfree(question
);
1247 if (asprintf(&question
, "Please enter new password for user %s (repeat):", user_name
) < 0)
1250 req
.message
= question
;
1252 r
= ask_password_auto(
1255 /* flags= */ 0, /* no caching */
1258 return log_error_errno(r
, "Failed to acquire password: %m");
1260 if (strv_equal(first
, second
)) {
1261 _cleanup_(erase_and_freep
) char *copy
= NULL
;
1264 copy
= strdup(first
[0]);
1269 r
= user_record_set_password(hr
, first
, /* prepend = */ true);
1271 return log_error_errno(r
, "Failed to store password: %m");
1274 *ret
= TAKE_PTR(copy
);
1279 log_error("Password didn't match, try again.");
1283 static int acquire_merged_blob_dir(UserRecord
*hr
, bool existing
, Hashmap
**ret
) {
1284 _cleanup_free_
char *sys_blob_path
= NULL
;
1285 _cleanup_hashmap_free_ Hashmap
*blobs
= NULL
;
1286 _cleanup_closedir_
DIR *d
= NULL
;
1287 const char *src_blob_path
, *filename
;
1293 HASHMAP_FOREACH_KEY(fd_ptr
, filename
, arg_blob_files
) {
1294 _cleanup_free_
char *filename_dup
= NULL
;
1295 _cleanup_close_
int fd_dup
= -EBADF
;
1297 filename_dup
= strdup(filename
);
1301 if (PTR_TO_FD(fd_ptr
) != -EBADF
) {
1302 fd_dup
= fcntl(PTR_TO_FD(fd_ptr
), F_DUPFD_CLOEXEC
, 3);
1304 return log_error_errno(errno
, "Failed to duplicate fd of %s: %m", filename
);
1307 r
= hashmap_ensure_put(&blobs
, &blob_fd_hash_ops
, filename_dup
, FD_TO_PTR(fd_dup
));
1310 TAKE_PTR(filename_dup
); /* Ownership transferred to hashmap */
1315 src_blob_path
= arg_blob_dir
;
1316 else if (existing
&& !arg_blob_clear
) {
1317 if (hr
->blob_directory
)
1318 src_blob_path
= hr
->blob_directory
;
1320 /* This isn't technically a correct thing to do for generic user records,
1321 * so anyone looking at this code for reference shouldn't replicate it.
1322 * However, since homectl is tied to homed, this is OK. This adds robustness
1323 * for situations where the user record is coming directly from the CLI and
1324 * thus doesn't have a blobDirectory set */
1326 sys_blob_path
= path_join(home_system_blob_dir(), hr
->user_name
);
1330 src_blob_path
= sys_blob_path
;
1333 goto nodir
; /* Shortcut: no dir to merge with, so just return copy of arg_blob_files */
1335 d
= opendir(src_blob_path
);
1337 return log_error_errno(errno
, "Failed to open %s: %m", src_blob_path
);
1339 FOREACH_DIRENT_ALL(de
, d
, return log_error_errno(errno
, "Failed to read %s: %m", src_blob_path
)) {
1340 _cleanup_free_
char *name
= NULL
;
1341 _cleanup_close_
int fd
= -EBADF
;
1343 if (dot_or_dot_dot(de
->d_name
))
1346 if (hashmap_contains(blobs
, de
->d_name
))
1347 continue; /* arg_blob_files should override the base dir */
1349 if (!suitable_blob_filename(de
->d_name
)) {
1350 log_warning("File %s in blob directory %s has an invalid filename. Skipping.", de
->d_name
, src_blob_path
);
1354 name
= strdup(de
->d_name
);
1358 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1360 return log_error_errno(errno
, "Failed to open %s in %s: %m", de
->d_name
, src_blob_path
);
1362 r
= fd_verify_regular(fd
);
1364 log_warning_errno(r
, "Entry %s in blob directory %s is not a regular file. Skipping.", de
->d_name
, src_blob_path
);
1368 r
= hashmap_ensure_put(&blobs
, &blob_fd_hash_ops
, name
, FD_TO_PTR(fd
));
1371 TAKE_PTR(name
); /* Ownership transferred to hashmap */
1376 *ret
= TAKE_PTR(blobs
);
1380 static int bus_message_append_blobs(sd_bus_message
*m
, Hashmap
*blobs
) {
1381 const char *filename
;
1387 r
= sd_bus_message_open_container(m
, 'a', "{sh}");
1391 HASHMAP_FOREACH_KEY(fd_ptr
, filename
, blobs
) {
1392 int fd
= PTR_TO_FD(fd_ptr
);
1394 if (fd
== -EBADF
) /* File marked for deletion */
1397 r
= sd_bus_message_append(m
, "{sh}", filename
, fd
);
1402 return sd_bus_message_close_container(m
);
1405 static int create_home_common(JsonVariant
*input
) {
1406 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1407 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1408 _cleanup_hashmap_free_ Hashmap
*blobs
= NULL
;
1411 r
= acquire_bus(&bus
);
1415 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1417 r
= acquire_new_home_record(input
, &hr
);
1421 r
= acquire_merged_blob_dir(hr
, false, &blobs
);
1425 /* If the JSON record carries no plain text password (besides the recovery key), then let's query it
1427 if (strv_length(hr
->password
) <= arg_recovery_key
) {
1429 if (strv_isempty(hr
->hashed_password
)) {
1430 _cleanup_(erase_and_freep
) char *new_password
= NULL
;
1432 /* No regular (i.e. non-PKCS#11) hashed passwords set in the record, let's fix that. */
1433 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ true, &new_password
);
1437 r
= user_record_make_hashed_password(hr
, STRV_MAKE(new_password
), /* extend = */ false);
1439 return log_error_errno(r
, "Failed to hash password: %m");
1441 /* There's a hash password set in the record, acquire the unhashed version of it. */
1442 r
= acquire_existing_password(
1445 /* emphasize_current= */ false,
1446 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_PUSH_CACHE
);
1452 if (hr
->enforce_password_policy
== 0) {
1453 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1455 /* If password quality enforcement is disabled, let's at least warn client side */
1457 r
= user_record_check_password_quality(hr
, hr
, &error
);
1459 log_warning_errno(r
, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error
, r
));
1463 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1464 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1465 _cleanup_(erase_and_freep
) char *formatted
= NULL
;
1467 r
= json_variant_format(hr
->json
, 0, &formatted
);
1469 return log_error_errno(r
, "Failed to format user record: %m");
1471 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "CreateHomeEx");
1473 return bus_log_create_error(r
);
1475 (void) sd_bus_message_sensitive(m
);
1477 r
= sd_bus_message_append(m
, "s", formatted
);
1479 return bus_log_create_error(r
);
1481 r
= bus_message_append_blobs(m
, blobs
);
1483 return bus_log_create_error(r
);
1485 r
= sd_bus_message_append(m
, "t", UINT64_C(0));
1487 return bus_log_create_error(r
);
1489 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1491 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
1492 _cleanup_(erase_and_freep
) char *new_password
= NULL
;
1494 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
1495 log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
1497 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ false, &new_password
);
1501 r
= user_record_make_hashed_password(hr
, STRV_MAKE(new_password
), /* extend = */ false);
1503 return log_error_errno(r
, "Failed to hash passwords: %m");
1505 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1516 static int create_home(int argc
, char *argv
[], void *userdata
) {
1520 /* If a username was specified, use it */
1522 if (valid_user_group_name(argv
[1], 0))
1523 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", argv
[1]);
1525 _cleanup_free_
char *un
= NULL
, *rr
= NULL
;
1527 /* Before we consider the user name invalid, let's check if we can split it? */
1528 r
= split_user_name_realm(argv
[1], &un
, &rr
);
1530 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name '%s' is not valid: %m", argv
[1]);
1533 r
= json_variant_set_field_string(&arg_identity_extra
, "realm", rr
);
1535 return log_error_errno(r
, "Failed to set realm field: %m");
1538 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", un
);
1541 return log_error_errno(r
, "Failed to set userName field: %m");
1543 /* If neither a username nor an identity have been specified we cannot operate. */
1545 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name required.");
1548 return create_home_common(/* input= */ NULL
);
1551 static int remove_home(int argc
, char *argv
[], void *userdata
) {
1552 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1555 r
= acquire_bus(&bus
);
1559 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1561 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1562 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1563 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1565 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "RemoveHome");
1567 return bus_log_create_error(r
);
1569 r
= sd_bus_message_append(m
, "s", *i
);
1571 return bus_log_create_error(r
);
1573 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1575 log_error_errno(r
, "Failed to remove home: %s", bus_error_message(&error
, r
));
1584 static int acquire_updated_home_record(
1586 const char *username
,
1589 _cleanup_(json_variant_unrefp
) JsonVariant
*json
= NULL
;
1590 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1596 unsigned line
, column
;
1599 r
= json_parse_file(
1600 streq(arg_identity
, "-") ? stdin
: NULL
,
1601 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, JSON_PARSE_SENSITIVE
, &json
, &line
, &column
);
1603 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
1605 un
= json_variant_by_key(json
, "userName");
1607 if (!json_variant_is_string(un
) || (username
&& !streq(json_variant_string(un
), username
)))
1608 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name specified on command line and in JSON record do not match.");
1611 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No username specified.");
1613 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", username
);
1615 return log_error_errno(r
, "Failed to set userName field: %m");
1619 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1620 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1624 if (!identity_properties_specified())
1625 return log_error_errno(SYNTHETIC_ERRNO(EALREADY
), "No field to change specified.");
1627 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByName", &error
, &reply
, "s", username
);
1629 return log_error_errno(r
, "Failed to acquire user home record: %s", bus_error_message(&error
, r
));
1631 r
= sd_bus_message_read(reply
, "sbo", &text
, &incomplete
, NULL
);
1633 return bus_log_parse_error(r
);
1636 return log_error_errno(SYNTHETIC_ERRNO(EACCES
), "Lacking rights to acquire user record including privileged metadata, can't update record.");
1638 r
= json_parse(text
, JSON_PARSE_SENSITIVE
, &json
, NULL
, NULL
);
1640 return log_error_errno(r
, "Failed to parse JSON identity: %m");
1642 reply
= sd_bus_message_unref(reply
);
1644 r
= json_variant_filter(&json
, STRV_MAKE("binding", "status", "signature", "blobManifest"));
1646 return log_error_errno(r
, "Failed to strip binding and status from record to update: %m");
1649 r
= apply_identity_changes(&json
);
1653 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
1654 r
= identity_add_pkcs11_key_data(&json
, *i
);
1659 STRV_FOREACH(i
, arg_fido2_device
) {
1660 r
= identity_add_fido2_parameters(&json
, *i
, arg_fido2_lock_with
, arg_fido2_cred_alg
);
1665 /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
1667 r
= update_last_change(&json
, arg_pkcs11_token_uri
|| arg_fido2_device
, !arg_identity
);
1672 json_variant_dump(json
, JSON_FORMAT_PRETTY
, NULL
, NULL
);
1674 hr
= user_record_new();
1678 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
);
1682 *ret
= TAKE_PTR(hr
);
1686 static int home_record_reset_human_interaction_permission(UserRecord
*hr
) {
1691 /* When we execute multiple operations one after the other, let's reset the permission to ask the
1692 * user each time, so that if interaction is necessary we will be told so again and thus can print a
1693 * nice message to the user, telling the user so. */
1695 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, -1);
1697 return log_error_errno(r
, "Failed to reset PKCS#11 protected authentication path permission flag: %m");
1699 r
= user_record_set_fido2_user_presence_permitted(hr
, -1);
1701 return log_error_errno(r
, "Failed to reset FIDO2 user presence permission flag: %m");
1703 r
= user_record_set_fido2_user_verification_permitted(hr
, -1);
1705 return log_error_errno(r
, "Failed to reset FIDO2 user verification permission flag: %m");
1710 static int update_home(int argc
, char *argv
[], void *userdata
) {
1711 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1712 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
, *secret
= NULL
;
1713 _cleanup_free_
char *buffer
= NULL
;
1714 _cleanup_hashmap_free_ Hashmap
*blobs
= NULL
;
1715 const char *username
;
1721 else if (!arg_identity
) {
1722 buffer
= getusername_malloc();
1730 r
= acquire_bus(&bus
);
1734 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1736 r
= acquire_updated_home_record(bus
, username
, &hr
);
1740 /* Add in all secrets we can acquire cheaply */
1741 r
= acquire_passed_secrets(username
, &secret
);
1745 r
= user_record_merge_secret(hr
, secret
);
1749 r
= acquire_merged_blob_dir(hr
, true, &blobs
);
1753 /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
1754 * authentication might be confusing. */
1756 if (arg_and_resize
|| arg_and_change_password
)
1757 log_info("Updating home directory.");
1760 flags
|= SD_HOMED_UPDATE_OFFLINE
;
1763 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1764 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1765 _cleanup_free_
char *formatted
= NULL
;
1767 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "UpdateHomeEx");
1769 return bus_log_create_error(r
);
1771 r
= json_variant_format(hr
->json
, 0, &formatted
);
1773 return log_error_errno(r
, "Failed to format user record: %m");
1775 (void) sd_bus_message_sensitive(m
);
1777 r
= sd_bus_message_append(m
, "s", formatted
);
1779 return bus_log_create_error(r
);
1781 r
= bus_message_append_blobs(m
, blobs
);
1783 return bus_log_create_error(r
);
1785 r
= sd_bus_message_append(m
, "t", flags
);
1787 return bus_log_create_error(r
);
1789 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1791 if (arg_and_change_password
&&
1792 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1793 /* In the generic handler we'd ask for a password in this case, but when
1794 * changing passwords that's not sufficient, as we need to acquire all keys
1796 return log_error_errno(r
, "Security token not inserted, refusing.");
1798 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1806 log_info("Resizing home.");
1808 (void) home_record_reset_human_interaction_permission(hr
);
1810 /* Also sync down disk size to underlying LUKS/fscrypt/quota */
1811 while (arg_and_resize
) {
1812 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1813 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1815 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ResizeHome");
1817 return bus_log_create_error(r
);
1819 /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
1820 r
= sd_bus_message_append(m
, "st", hr
->user_name
, UINT64_MAX
);
1822 return bus_log_create_error(r
);
1824 r
= bus_message_append_secret(m
, hr
);
1826 return bus_log_create_error(r
);
1828 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1830 if (arg_and_change_password
&&
1831 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1832 return log_error_errno(r
, "Security token not inserted, refusing.");
1834 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1841 if (arg_and_change_password
)
1842 log_info("Synchronizing passwords and encryption keys.");
1844 (void) home_record_reset_human_interaction_permission(hr
);
1846 /* Also sync down passwords to underlying LUKS/fscrypt */
1847 while (arg_and_change_password
) {
1848 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1849 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1851 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ChangePasswordHome");
1853 return bus_log_create_error(r
);
1855 /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
1856 r
= sd_bus_message_append(m
, "ss", hr
->user_name
, "{}");
1858 return bus_log_create_error(r
);
1860 r
= bus_message_append_secret(m
, hr
);
1862 return bus_log_create_error(r
);
1864 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1866 if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1867 return log_error_errno(r
, "Security token not inserted, refusing.");
1869 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1879 static int passwd_home(int argc
, char *argv
[], void *userdata
) {
1880 _cleanup_(user_record_unrefp
) UserRecord
*old_secret
= NULL
, *new_secret
= NULL
;
1881 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1882 _cleanup_free_
char *buffer
= NULL
;
1883 const char *username
;
1886 if (arg_pkcs11_token_uri
)
1887 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1888 "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=%s'.",
1889 special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
1890 if (arg_fido2_device
)
1891 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1892 "To change the FIDO2 security token use 'homectl update --fido2-device=%s'.",
1893 special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
1894 if (identity_properties_specified())
1895 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "The 'passwd' verb does not permit changing other record properties at the same time.");
1900 buffer
= getusername_malloc();
1907 r
= acquire_bus(&bus
);
1911 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1913 r
= acquire_passed_secrets(username
, &old_secret
);
1917 new_secret
= user_record_new();
1921 r
= acquire_new_password(username
, new_secret
, /* suggest = */ true, NULL
);
1926 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1927 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1929 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ChangePasswordHome");
1931 return bus_log_create_error(r
);
1933 r
= sd_bus_message_append(m
, "s", username
);
1935 return bus_log_create_error(r
);
1937 r
= bus_message_append_secret(m
, new_secret
);
1939 return bus_log_create_error(r
);
1941 r
= bus_message_append_secret(m
, old_secret
);
1943 return bus_log_create_error(r
);
1945 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1947 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
1949 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
1951 r
= acquire_new_password(username
, new_secret
, /* suggest = */ false, NULL
);
1953 } else if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1955 /* In the generic handler we'd ask for a password in this case, but when
1956 * changing passwords that's not sufficeint, as we need to acquire all keys
1958 return log_error_errno(r
, "Security token not inserted, refusing.");
1960 r
= handle_generic_user_record_error(username
, old_secret
, &error
, r
, true);
1970 static int parse_disk_size(const char *t
, uint64_t *ret
) {
1976 if (streq(t
, "min"))
1978 else if (streq(t
, "max"))
1979 *ret
= UINT64_MAX
-1; /* Largest size that isn't UINT64_MAX special marker */
1983 r
= parse_size(t
, 1024, &ds
);
1985 return log_error_errno(r
, "Failed to parse disk size parameter: %s", t
);
1987 if (ds
>= UINT64_MAX
) /* UINT64_MAX has special meaning for us ("dont change"), refuse */
1988 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
), "Disk size out of range: %s", t
);
1996 static int resize_home(int argc
, char *argv
[], void *userdata
) {
1997 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1998 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
1999 uint64_t ds
= UINT64_MAX
;
2002 r
= acquire_bus(&bus
);
2006 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2008 if (arg_disk_size_relative
!= UINT64_MAX
||
2009 (argc
> 2 && parse_permyriad(argv
[2]) >= 0))
2010 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
2011 "Relative disk size specification currently not supported when resizing.");
2014 r
= parse_disk_size(argv
[2], &ds
);
2019 if (arg_disk_size
!= UINT64_MAX
) {
2020 if (ds
!= UINT64_MAX
&& ds
!= arg_disk_size
)
2021 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Disk size specified twice and doesn't match, refusing.");
2026 r
= acquire_passed_secrets(argv
[1], &secret
);
2031 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2032 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2034 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ResizeHome");
2036 return bus_log_create_error(r
);
2038 r
= sd_bus_message_append(m
, "st", argv
[1], ds
);
2040 return bus_log_create_error(r
);
2042 r
= bus_message_append_secret(m
, secret
);
2044 return bus_log_create_error(r
);
2046 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2048 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2058 static int lock_home(int argc
, char *argv
[], void *userdata
) {
2059 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2062 r
= acquire_bus(&bus
);
2066 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
2067 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2068 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2070 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "LockHome");
2072 return bus_log_create_error(r
);
2074 r
= sd_bus_message_append(m
, "s", *i
);
2076 return bus_log_create_error(r
);
2078 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2080 log_error_errno(r
, "Failed to lock home: %s", bus_error_message(&error
, r
));
2089 static int unlock_home(int argc
, char *argv
[], void *userdata
) {
2090 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2093 r
= acquire_bus(&bus
);
2097 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
2098 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
2100 r
= acquire_passed_secrets(*i
, &secret
);
2105 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2106 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2108 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "UnlockHome");
2110 return bus_log_create_error(r
);
2112 r
= sd_bus_message_append(m
, "s", *i
);
2114 return bus_log_create_error(r
);
2116 r
= bus_message_append_secret(m
, secret
);
2118 return bus_log_create_error(r
);
2120 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2122 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2137 static int with_home(int argc
, char *argv
[], void *userdata
) {
2138 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2139 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
2140 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2141 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
2142 _cleanup_close_
int acquired_fd
= -EBADF
;
2143 _cleanup_strv_free_
char **cmdline
= NULL
;
2148 r
= acquire_bus(&bus
);
2153 _cleanup_free_
char *shell
= NULL
;
2155 /* If no command is specified, spawn a shell */
2156 r
= get_shell(&shell
);
2158 return log_error_errno(r
, "Failed to acquire shell: %m");
2160 cmdline
= strv_new(shell
);
2162 cmdline
= strv_copy(argv
+ 2);
2166 r
= acquire_passed_secrets(argv
[1], &secret
);
2171 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "AcquireHome");
2173 return bus_log_create_error(r
);
2175 r
= sd_bus_message_append(m
, "s", argv
[1]);
2177 return bus_log_create_error(r
);
2179 r
= bus_message_append_secret(m
, secret
);
2181 return bus_log_create_error(r
);
2183 r
= sd_bus_message_append(m
, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
2185 return bus_log_create_error(r
);
2187 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
2188 m
= sd_bus_message_unref(m
);
2190 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2194 sd_bus_error_free(&error
);
2198 r
= sd_bus_message_read(reply
, "h", &fd
);
2200 return bus_log_parse_error(r
);
2202 acquired_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
2203 if (acquired_fd
< 0)
2204 return log_error_errno(errno
, "Failed to duplicate acquired fd: %m");
2206 reply
= sd_bus_message_unref(reply
);
2211 r
= bus_call_method(bus
, bus_mgr
, "GetHomeByName", &error
, &reply
, "s", argv
[1]);
2213 return log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
2215 r
= sd_bus_message_read(reply
, "usussso", NULL
, NULL
, NULL
, NULL
, &home
, NULL
, NULL
);
2217 return bus_log_parse_error(r
);
2219 r
= safe_fork("(with)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG_SIGTERM
|FORK_LOG
|FORK_RLIMIT_NOFILE_SAFE
|FORK_REOPEN_LOG
, &pid
);
2223 if (chdir(home
) < 0) {
2224 log_error_errno(errno
, "Failed to change to directory %s: %m", home
);
2228 execvp(cmdline
[0], cmdline
);
2229 log_error_errno(errno
, "Failed to execute %s: %m", cmdline
[0]);
2233 ret
= wait_for_terminate_and_check(cmdline
[0], pid
, WAIT_LOG_ABNORMAL
);
2235 /* Close the fd that pings the home now. */
2236 acquired_fd
= safe_close(acquired_fd
);
2238 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ReleaseHome");
2240 return bus_log_create_error(r
);
2242 r
= sd_bus_message_append(m
, "s", argv
[1]);
2244 return bus_log_create_error(r
);
2246 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2248 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_BUSY
))
2249 log_notice("Not deactivating home directory of %s, as it is still used.", argv
[1]);
2251 return log_error_errno(r
, "Failed to release user home: %s", bus_error_message(&error
, r
));
2257 static int lock_all_homes(int argc
, char *argv
[], void *userdata
) {
2258 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2259 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2260 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2263 r
= acquire_bus(&bus
);
2267 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "LockAllHomes");
2269 return bus_log_create_error(r
);
2271 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2273 return log_error_errno(r
, "Failed to lock all homes: %s", bus_error_message(&error
, r
));
2278 static int deactivate_all_homes(int argc
, char *argv
[], void *userdata
) {
2279 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2280 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2281 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2284 r
= acquire_bus(&bus
);
2288 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "DeactivateAllHomes");
2290 return bus_log_create_error(r
);
2292 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2294 return log_error_errno(r
, "Failed to deactivate all homes: %s", bus_error_message(&error
, r
));
2299 static int rebalance(int argc
, char *argv
[], void *userdata
) {
2300 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2301 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2302 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2305 r
= acquire_bus(&bus
);
2309 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "Rebalance");
2311 return bus_log_create_error(r
);
2313 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2315 if (sd_bus_error_has_name(&error
, BUS_ERROR_REBALANCE_NOT_NEEDED
))
2316 log_info("No homes needed rebalancing.");
2318 return log_error_errno(r
, "Failed to rebalance: %s", bus_error_message(&error
, r
));
2320 log_info("Completed rebalancing.");
2325 static int create_from_credentials(void) {
2326 _cleanup_close_
int fd
= -EBADF
;
2327 int ret
= 0, n_created
= 0, r
;
2329 fd
= open_credentials_dir();
2330 if (IN_SET(fd
, -ENXIO
, -ENOENT
)) /* Credential env var not set, or dir doesn't exist. */
2333 return log_error_errno(fd
, "Failed to open credentials directory: %m");
2335 _cleanup_free_ DirectoryEntries
*des
= NULL
;
2336 r
= readdir_all(fd
, RECURSE_DIR_SORT
|RECURSE_DIR_IGNORE_DOT
|RECURSE_DIR_ENSURE_TYPE
, &des
);
2338 return log_error_errno(r
, "Failed to enumerate credentials: %m");
2340 FOREACH_ARRAY(i
, des
->entries
, des
->n_entries
) {
2341 _cleanup_(json_variant_unrefp
) JsonVariant
*identity
= NULL
;
2342 struct dirent
*de
= *i
;
2345 if (de
->d_type
!= DT_REG
)
2348 e
= startswith(de
->d_name
, "home.create.");
2352 if (!valid_user_group_name(e
, 0)) {
2353 log_notice("Skipping over credential with name that is not a suitable user name: %s", de
->d_name
);
2357 r
= json_parse_file_at(
2363 /* ret_line= */ NULL
,
2364 /* ret_column= */ NULL
);
2366 log_warning_errno(r
, "Failed to parse user record in credential '%s', ignoring: %m", de
->d_name
);
2371 un
= json_variant_by_key(identity
, "userName");
2373 if (!json_variant_is_string(un
)) {
2374 log_warning("User record from credential '%s' contains 'userName' field of invalid type, ignoring.", de
->d_name
);
2378 if (!streq(json_variant_string(un
), e
)) {
2379 log_warning("User record from credential '%s' contains 'userName' field (%s) that doesn't match credential name (%s), ignoring.", de
->d_name
, json_variant_string(un
), e
);
2383 r
= json_variant_set_field_string(&identity
, "userName", e
);
2385 return log_warning_errno(r
, "Failed to set userName field: %m");
2388 log_notice("Processing user '%s' from credentials.", e
);
2390 r
= create_home_common(identity
);
2397 return ret
< 0 ? ret
: n_created
;
2400 static int has_regular_user(void) {
2401 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
2404 r
= userdb_all(USERDB_SUPPRESS_SHADOW
, &iterator
);
2406 return log_error_errno(r
, "Failed to create user enumerator: %m");
2409 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
2411 r
= userdb_iterator_get(iterator
, &ur
);
2415 return log_error_errno(r
, "Failed to enumerate users: %m");
2417 if (user_record_disposition(ur
) == USER_REGULAR
)
2424 static int create_interactively(void) {
2425 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2426 _cleanup_free_
char *username
= NULL
;
2429 if (!arg_prompt_new_user
) {
2430 log_debug("Prompting for user creation was not requested.");
2434 r
= acquire_bus(&bus
);
2438 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2440 (void) reset_terminal_fd(STDIN_FILENO
, /* switch_to_text= */ false);
2443 username
= mfree(username
);
2445 r
= ask_string(&username
,
2446 "%s Please enter user name to create (empty to skip): ",
2447 special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET
));
2449 return log_error_errno(r
, "Failed to query user for username: %m");
2451 if (isempty(username
)) {
2452 log_info("No data entered, skipping.");
2456 if (!valid_user_group_name(username
, /* flags= */ 0)) {
2457 log_notice("Specified user name is not a valid UNIX user name, try again: %s", username
);
2461 r
= userdb_by_name(username
, USERDB_SUPPRESS_SHADOW
, /* ret= */ NULL
);
2465 return log_error_errno(r
, "Failed to check if specified user '%s' already exists: %m", username
);
2467 log_notice("Specified user '%s' exists already, try again.", username
);
2470 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", username
);
2472 return log_error_errno(r
, "Failed to set userName field: %m");
2474 return create_home_common(/* input= */ NULL
);
2477 static int verb_firstboot(int argc
, char *argv
[], void *userdata
) {
2480 /* Let's honour the systemd.firstboot kernel command line option, just like the systemd-firstboot
2484 r
= proc_cmdline_get_bool("systemd.firstboot", /* flags = */ 0, &enabled
);
2486 return log_error_errno(r
, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
2487 if (r
> 0 && !enabled
) {
2488 log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
2489 arg_prompt_new_user
= false;
2492 r
= create_from_credentials();
2495 if (r
> 0) /* Already created users from credentials */
2498 r
= has_regular_user();
2502 log_info("Regular user already present in user database, skipping user creation.");
2506 return create_interactively();
2509 static int drop_from_identity(const char *field
) {
2514 /* If we are called to update an identity record and drop some field, let's keep track of what to
2515 * remove from the old record */
2516 r
= strv_extend(&arg_identity_filter
, field
);
2520 /* Let's also drop the field if it was previously set to a new value on the same command line */
2521 r
= json_variant_filter(&arg_identity_extra
, STRV_MAKE(field
));
2523 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2525 r
= json_variant_filter(&arg_identity_extra_this_machine
, STRV_MAKE(field
));
2527 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2529 r
= json_variant_filter(&arg_identity_extra_privileged
, STRV_MAKE(field
));
2531 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2536 static int help(int argc
, char *argv
[], void *userdata
) {
2537 _cleanup_free_
char *link
= NULL
;
2540 pager_open(arg_pager_flags
);
2542 r
= terminal_urlify_man("homectl", "1", &link
);
2546 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
2547 "%2$sCreate, manipulate or inspect home directories.%3$s\n"
2548 "\n%4$sCommands:%5$s\n"
2549 " list List home areas\n"
2550 " activate USER… Activate a home area\n"
2551 " deactivate USER… Deactivate a home area\n"
2552 " inspect USER… Inspect a home area\n"
2553 " authenticate USER… Authenticate a home area\n"
2554 " create USER Create a home area\n"
2555 " remove USER… Remove a home area\n"
2556 " update USER Update a home area\n"
2557 " passwd USER Change password of a home area\n"
2558 " resize USER SIZE Resize a home area\n"
2559 " lock USER… Temporarily lock an active home area\n"
2560 " unlock USER… Unlock a temporarily locked home area\n"
2561 " lock-all Lock all suitable home areas\n"
2562 " deactivate-all Deactivate all active home areas\n"
2563 " rebalance Rebalance free space between home areas\n"
2564 " with USER [COMMAND…] Run shell or command with access to a home area\n"
2565 " firstboot Run first-boot home area creation wizard\n"
2566 "\n%4$sOptions:%5$s\n"
2567 " -h --help Show this help\n"
2568 " --version Show package version\n"
2569 " --no-pager Do not pipe output into a pager\n"
2570 " --no-legend Do not show the headers and footers\n"
2571 " --no-ask-password Do not ask for system passwords\n"
2572 " --offline Don't update record embedded in home directory\n"
2573 " -H --host=[USER@]HOST Operate on remote host\n"
2574 " -M --machine=CONTAINER Operate on local container\n"
2575 " --identity=PATH Read JSON identity from file\n"
2576 " --json=FORMAT Output inspection data in JSON (takes one of\n"
2577 " pretty, short, off)\n"
2578 " -j Equivalent to --json=pretty (on TTY) or\n"
2579 " --json=short (otherwise)\n"
2580 " --export-format= Strip JSON inspection data (full, stripped,\n"
2582 " -E When specified once equals -j --export-format=\n"
2583 " stripped, when specified twice equals\n"
2584 " -j --export-format=minimal\n"
2585 " --prompt-new-user firstboot: Query user interactively for user\n"
2587 "\n%4$sGeneral User Record Properties:%5$s\n"
2588 " -c --real-name=REALNAME Real name for user\n"
2589 " --realm=REALM Realm to create user in\n"
2590 " --email-address=EMAIL Email address for user\n"
2591 " --location=LOCATION Set location of user on earth\n"
2592 " --icon-name=NAME Icon name for user\n"
2593 " -d --home-dir=PATH Home directory\n"
2594 " -u --uid=UID Numeric UID for user\n"
2595 " -G --member-of=GROUP Add user to group\n"
2596 " --capability-bounding-set=CAPS\n"
2597 " Bounding POSIX capability set\n"
2598 " --capability-ambient-set=CAPS\n"
2599 " Ambient POSIX capability set\n"
2600 " --skel=PATH Skeleton directory to use\n"
2601 " --shell=PATH Shell for account\n"
2602 " --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n"
2603 " --timezone=TIMEZONE Set a time-zone\n"
2604 " --language=LOCALE Set preferred languages\n"
2605 " --ssh-authorized-keys=KEYS\n"
2606 " Specify SSH public keys\n"
2607 " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
2608 " private key and matching X.509 certificate\n"
2609 " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
2611 " --fido2-with-client-pin=BOOL\n"
2612 " Whether to require entering a PIN to unlock the\n"
2614 " --fido2-with-user-presence=BOOL\n"
2615 " Whether to require user presence to unlock the\n"
2617 " --fido2-with-user-verification=BOOL\n"
2618 " Whether to require user verification to unlock\n"
2620 " --recovery-key=BOOL Add a recovery key\n"
2621 "\n%4$sBlob Directory User Record Properties:%5$s\n"
2622 " -b --blob=[FILENAME=]PATH\n"
2623 " Path to a replacement blob directory, or replace\n"
2624 " an individual files in the blob directory.\n"
2625 " --avatar=PATH Path to user avatar picture\n"
2626 " --login-background=PATH Path to user login background picture\n"
2627 "\n%4$sAccount Management User Record Properties:%5$s\n"
2628 " --locked=BOOL Set locked account state\n"
2629 " --not-before=TIMESTAMP Do not allow logins before\n"
2630 " --not-after=TIMESTAMP Do not allow logins after\n"
2631 " --rate-limit-interval=SECS\n"
2632 " Login rate-limit interval in seconds\n"
2633 " --rate-limit-burst=NUMBER\n"
2634 " Login rate-limit attempts per interval\n"
2635 "\n%4$sPassword Policy User Record Properties:%5$s\n"
2636 " --password-hint=HINT Set Password hint\n"
2637 " --enforce-password-policy=BOOL\n"
2638 " Control whether to enforce system's password\n"
2639 " policy for this user\n"
2640 " -P Same as --enforce-password-password=no\n"
2641 " --password-change-now=BOOL\n"
2642 " Require the password to be changed on next login\n"
2643 " --password-change-min=TIME\n"
2644 " Require minimum time between password changes\n"
2645 " --password-change-max=TIME\n"
2646 " Require maximum time between password changes\n"
2647 " --password-change-warn=TIME\n"
2648 " How much time to warn before password expiry\n"
2649 " --password-change-inactive=TIME\n"
2650 " How much time to block password after expiry\n"
2651 "\n%4$sResource Management User Record Properties:%5$s\n"
2652 " --disk-size=BYTES Size to assign the user on disk\n"
2653 " --access-mode=MODE User home directory access mode\n"
2654 " --umask=MODE Umask for user when logging in\n"
2655 " --nice=NICE Nice level for user\n"
2656 " --rlimit=LIMIT=VALUE[:VALUE]\n"
2657 " Set resource limits\n"
2658 " --tasks-max=MAX Set maximum number of per-user tasks\n"
2659 " --memory-high=BYTES Set high memory threshold in bytes\n"
2660 " --memory-max=BYTES Set maximum memory limit\n"
2661 " --cpu-weight=WEIGHT Set CPU weight\n"
2662 " --io-weight=WEIGHT Set IO weight\n"
2663 "\n%4$sStorage User Record Properties:%5$s\n"
2664 " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
2665 " subvolume, cifs)\n"
2666 " --image-path=PATH Path to image file/directory\n"
2667 " --drop-caches=BOOL Whether to automatically drop caches on logout\n"
2668 "\n%4$sLUKS Storage User Record Properties:%5$s\n"
2669 " --fs-type=TYPE File system type to use in case of luks\n"
2670 " storage (btrfs, ext4, xfs)\n"
2671 " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
2672 " when activated (mounted)\n"
2673 " --luks-offline-discard=BOOL\n"
2674 " Whether to trim file on logout\n"
2675 " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
2676 " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
2677 " --luks-volume-key-size=BITS\n"
2678 " Volume key size to use for LUKS encryption\n"
2679 " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
2680 " --luks-pbkdf-hash-algorithm=ALGORITHM\n"
2681 " PBKDF hash algorithm to use\n"
2682 " --luks-pbkdf-time-cost=SECS\n"
2683 " Time cost for PBKDF in seconds\n"
2684 " --luks-pbkdf-memory-cost=BYTES\n"
2685 " Memory cost for PBKDF in bytes\n"
2686 " --luks-pbkdf-parallel-threads=NUMBER\n"
2687 " Number of parallel threads for PKBDF\n"
2688 " --luks-sector-size=BYTES\n"
2689 " Sector size for LUKS encryption in bytes\n"
2690 " --luks-extra-mount-options=OPTIONS\n"
2691 " LUKS extra mount options\n"
2692 " --auto-resize-mode=MODE Automatically grow/shrink home on login/logout\n"
2693 " --rebalance-weight=WEIGHT Weight while rebalancing\n"
2694 "\n%4$sMounting User Record Properties:%5$s\n"
2695 " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
2696 " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
2697 " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
2698 "\n%4$sCIFS User Record Properties:%5$s\n"
2699 " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
2700 " --cifs-user-name=USER CIFS (Windows) user name\n"
2701 " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
2702 " --cifs-extra-mount-options=OPTIONS\n"
2703 " CIFS (Windows) extra mount options\n"
2704 "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
2705 " --stop-delay=SECS How long to leave user services running after\n"
2707 " --kill-processes=BOOL Whether to kill user processes when sessions\n"
2709 " --auto-login=BOOL Try to log this user in automatically\n"
2710 " --session-launcher=LAUNCHER\n"
2711 " Preferred session launcher file\n"
2712 " --session-type=TYPE Preferred session type\n"
2713 "\nSee the %6$s for details.\n",
2714 program_invocation_short_name
,
2724 static int parse_argv(int argc
, char *argv
[]) {
2725 _cleanup_strv_free_
char **arg_languages
= NULL
;
2728 ARG_VERSION
= 0x100,
2731 ARG_NO_ASK_PASSWORD
,
2742 ARG_LUKS_OFFLINE_DISCARD
,
2748 ARG_SSH_AUTHORIZED_KEYS
,
2757 ARG_LUKS_CIPHER_MODE
,
2758 ARG_LUKS_VOLUME_KEY_SIZE
,
2765 ARG_CIFS_EXTRA_MOUNT_OPTIONS
,
2771 ARG_LUKS_PBKDF_TYPE
,
2772 ARG_LUKS_PBKDF_HASH_ALGORITHM
,
2773 ARG_LUKS_PBKDF_FORCE_ITERATIONS
,
2774 ARG_LUKS_PBKDF_TIME_COST
,
2775 ARG_LUKS_PBKDF_MEMORY_COST
,
2776 ARG_LUKS_PBKDF_PARALLEL_THREADS
,
2777 ARG_LUKS_SECTOR_SIZE
,
2778 ARG_RATE_LIMIT_INTERVAL
,
2779 ARG_RATE_LIMIT_BURST
,
2782 ARG_ENFORCE_PASSWORD_POLICY
,
2783 ARG_PASSWORD_CHANGE_NOW
,
2784 ARG_PASSWORD_CHANGE_MIN
,
2785 ARG_PASSWORD_CHANGE_MAX
,
2786 ARG_PASSWORD_CHANGE_WARN
,
2787 ARG_PASSWORD_CHANGE_INACTIVE
,
2790 ARG_SESSION_LAUNCHER
,
2792 ARG_PKCS11_TOKEN_URI
,
2799 ARG_AND_CHANGE_PASSWORD
,
2801 ARG_LUKS_EXTRA_MOUNT_OPTIONS
,
2802 ARG_AUTO_RESIZE_MODE
,
2803 ARG_REBALANCE_WEIGHT
,
2805 ARG_CAPABILITY_BOUNDING_SET
,
2806 ARG_CAPABILITY_AMBIENT_SET
,
2807 ARG_PROMPT_NEW_USER
,
2809 ARG_LOGIN_BACKGROUND
,
2812 static const struct option options
[] = {
2813 { "help", no_argument
, NULL
, 'h' },
2814 { "version", no_argument
, NULL
, ARG_VERSION
},
2815 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2816 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2817 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2818 { "offline", no_argument
, NULL
, ARG_OFFLINE
},
2819 { "host", required_argument
, NULL
, 'H' },
2820 { "machine", required_argument
, NULL
, 'M' },
2821 { "identity", required_argument
, NULL
, 'I' },
2822 { "real-name", required_argument
, NULL
, 'c' },
2823 { "comment", required_argument
, NULL
, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
2824 { "realm", required_argument
, NULL
, ARG_REALM
},
2825 { "email-address", required_argument
, NULL
, ARG_EMAIL_ADDRESS
},
2826 { "location", required_argument
, NULL
, ARG_LOCATION
},
2827 { "password-hint", required_argument
, NULL
, ARG_PASSWORD_HINT
},
2828 { "icon-name", required_argument
, NULL
, ARG_ICON_NAME
},
2829 { "home-dir", required_argument
, NULL
, 'd' }, /* Compatible with useradd(8) */
2830 { "uid", required_argument
, NULL
, 'u' }, /* Compatible with useradd(8) */
2831 { "member-of", required_argument
, NULL
, 'G' },
2832 { "groups", required_argument
, NULL
, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
2833 { "skel", required_argument
, NULL
, 'k' }, /* Compatible with useradd(8) */
2834 { "shell", required_argument
, NULL
, 's' }, /* Compatible with useradd(8) */
2835 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2836 { "timezone", required_argument
, NULL
, ARG_TIMEZONE
},
2837 { "language", required_argument
, NULL
, ARG_LANGUAGE
},
2838 { "locked", required_argument
, NULL
, ARG_LOCKED
},
2839 { "not-before", required_argument
, NULL
, ARG_NOT_BEFORE
},
2840 { "not-after", required_argument
, NULL
, ARG_NOT_AFTER
},
2841 { "expiredate", required_argument
, NULL
, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
2842 { "ssh-authorized-keys", required_argument
, NULL
, ARG_SSH_AUTHORIZED_KEYS
},
2843 { "disk-size", required_argument
, NULL
, ARG_DISK_SIZE
},
2844 { "access-mode", required_argument
, NULL
, ARG_ACCESS_MODE
},
2845 { "umask", required_argument
, NULL
, ARG_UMASK
},
2846 { "nice", required_argument
, NULL
, ARG_NICE
},
2847 { "rlimit", required_argument
, NULL
, ARG_RLIMIT
},
2848 { "tasks-max", required_argument
, NULL
, ARG_TASKS_MAX
},
2849 { "memory-high", required_argument
, NULL
, ARG_MEMORY_HIGH
},
2850 { "memory-max", required_argument
, NULL
, ARG_MEMORY_MAX
},
2851 { "cpu-weight", required_argument
, NULL
, ARG_CPU_WEIGHT
},
2852 { "io-weight", required_argument
, NULL
, ARG_IO_WEIGHT
},
2853 { "storage", required_argument
, NULL
, ARG_STORAGE
},
2854 { "image-path", required_argument
, NULL
, ARG_IMAGE_PATH
},
2855 { "fs-type", required_argument
, NULL
, ARG_FS_TYPE
},
2856 { "luks-discard", required_argument
, NULL
, ARG_LUKS_DISCARD
},
2857 { "luks-offline-discard", required_argument
, NULL
, ARG_LUKS_OFFLINE_DISCARD
},
2858 { "luks-cipher", required_argument
, NULL
, ARG_LUKS_CIPHER
},
2859 { "luks-cipher-mode", required_argument
, NULL
, ARG_LUKS_CIPHER_MODE
},
2860 { "luks-volume-key-size", required_argument
, NULL
, ARG_LUKS_VOLUME_KEY_SIZE
},
2861 { "luks-pbkdf-type", required_argument
, NULL
, ARG_LUKS_PBKDF_TYPE
},
2862 { "luks-pbkdf-hash-algorithm", required_argument
, NULL
, ARG_LUKS_PBKDF_HASH_ALGORITHM
},
2863 { "luks-pbkdf-force-iterations", required_argument
, NULL
, ARG_LUKS_PBKDF_FORCE_ITERATIONS
},
2864 { "luks-pbkdf-time-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_TIME_COST
},
2865 { "luks-pbkdf-memory-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_MEMORY_COST
},
2866 { "luks-pbkdf-parallel-threads", required_argument
, NULL
, ARG_LUKS_PBKDF_PARALLEL_THREADS
},
2867 { "luks-sector-size", required_argument
, NULL
, ARG_LUKS_SECTOR_SIZE
},
2868 { "nosuid", required_argument
, NULL
, ARG_NOSUID
},
2869 { "nodev", required_argument
, NULL
, ARG_NODEV
},
2870 { "noexec", required_argument
, NULL
, ARG_NOEXEC
},
2871 { "cifs-user-name", required_argument
, NULL
, ARG_CIFS_USER_NAME
},
2872 { "cifs-domain", required_argument
, NULL
, ARG_CIFS_DOMAIN
},
2873 { "cifs-service", required_argument
, NULL
, ARG_CIFS_SERVICE
},
2874 { "cifs-extra-mount-options", required_argument
, NULL
, ARG_CIFS_EXTRA_MOUNT_OPTIONS
},
2875 { "rate-limit-interval", required_argument
, NULL
, ARG_RATE_LIMIT_INTERVAL
},
2876 { "rate-limit-burst", required_argument
, NULL
, ARG_RATE_LIMIT_BURST
},
2877 { "stop-delay", required_argument
, NULL
, ARG_STOP_DELAY
},
2878 { "kill-processes", required_argument
, NULL
, ARG_KILL_PROCESSES
},
2879 { "enforce-password-policy", required_argument
, NULL
, ARG_ENFORCE_PASSWORD_POLICY
},
2880 { "password-change-now", required_argument
, NULL
, ARG_PASSWORD_CHANGE_NOW
},
2881 { "password-change-min", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MIN
},
2882 { "password-change-max", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MAX
},
2883 { "password-change-warn", required_argument
, NULL
, ARG_PASSWORD_CHANGE_WARN
},
2884 { "password-change-inactive", required_argument
, NULL
, ARG_PASSWORD_CHANGE_INACTIVE
},
2885 { "auto-login", required_argument
, NULL
, ARG_AUTO_LOGIN
},
2886 { "session-launcher", required_argument
, NULL
, ARG_SESSION_LAUNCHER
, },
2887 { "session-type", required_argument
, NULL
, ARG_SESSION_TYPE
, },
2888 { "json", required_argument
, NULL
, ARG_JSON
},
2889 { "export-format", required_argument
, NULL
, ARG_EXPORT_FORMAT
},
2890 { "pkcs11-token-uri", required_argument
, NULL
, ARG_PKCS11_TOKEN_URI
},
2891 { "fido2-credential-algorithm", required_argument
, NULL
, ARG_FIDO2_CRED_ALG
},
2892 { "fido2-device", required_argument
, NULL
, ARG_FIDO2_DEVICE
},
2893 { "fido2-with-client-pin", required_argument
, NULL
, ARG_FIDO2_WITH_PIN
},
2894 { "fido2-with-user-presence", required_argument
, NULL
, ARG_FIDO2_WITH_UP
},
2895 { "fido2-with-user-verification",required_argument
, NULL
, ARG_FIDO2_WITH_UV
},
2896 { "recovery-key", required_argument
, NULL
, ARG_RECOVERY_KEY
},
2897 { "and-resize", required_argument
, NULL
, ARG_AND_RESIZE
},
2898 { "and-change-password", required_argument
, NULL
, ARG_AND_CHANGE_PASSWORD
},
2899 { "drop-caches", required_argument
, NULL
, ARG_DROP_CACHES
},
2900 { "luks-extra-mount-options", required_argument
, NULL
, ARG_LUKS_EXTRA_MOUNT_OPTIONS
},
2901 { "auto-resize-mode", required_argument
, NULL
, ARG_AUTO_RESIZE_MODE
},
2902 { "rebalance-weight", required_argument
, NULL
, ARG_REBALANCE_WEIGHT
},
2903 { "capability-bounding-set", required_argument
, NULL
, ARG_CAPABILITY_BOUNDING_SET
},
2904 { "capability-ambient-set", required_argument
, NULL
, ARG_CAPABILITY_AMBIENT_SET
},
2905 { "prompt-new-user", no_argument
, NULL
, ARG_PROMPT_NEW_USER
},
2906 { "blob", required_argument
, NULL
, 'b' },
2907 { "avatar", required_argument
, NULL
, ARG_AVATAR
},
2908 { "login-background", required_argument
, NULL
, ARG_LOGIN_BACKGROUND
},
2920 c
= getopt_long(argc
, argv
, "hH:M:I:c:d:u:G:k:s:e:b:jPE", options
, NULL
);
2927 return help(0, NULL
, NULL
);
2933 arg_pager_flags
|= PAGER_DISABLE
;
2940 case ARG_NO_ASK_PASSWORD
:
2941 arg_ask_password
= false;
2949 arg_transport
= BUS_TRANSPORT_REMOTE
;
2954 arg_transport
= BUS_TRANSPORT_MACHINE
;
2959 arg_identity
= optarg
;
2963 if (isempty(optarg
)) {
2964 r
= drop_from_identity("realName");
2971 if (!valid_gecos(optarg
))
2972 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Real name '%s' not a valid GECOS field.", optarg
);
2974 r
= json_variant_set_field_string(&arg_identity_extra
, "realName", optarg
);
2976 return log_error_errno(r
, "Failed to set realName field: %m");
2981 _cleanup_free_
char *hd
= NULL
;
2983 if (isempty(optarg
)) {
2984 r
= drop_from_identity("homeDirectory");
2991 r
= parse_path_argument(optarg
, false, &hd
);
2995 if (!valid_home(hd
))
2996 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Home directory '%s' not valid.", hd
);
2998 r
= json_variant_set_field_string(&arg_identity_extra
, "homeDirectory", hd
);
3000 return log_error_errno(r
, "Failed to set homeDirectory field: %m");
3006 if (isempty(optarg
)) {
3007 r
= drop_from_identity("realm");
3014 r
= dns_name_is_valid(optarg
);
3016 return log_error_errno(r
, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg
);
3018 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Realm '%s' is not a valid DNS domain: %m", optarg
);
3020 r
= json_variant_set_field_string(&arg_identity_extra
, "realm", optarg
);
3022 return log_error_errno(r
, "Failed to set realm field: %m");
3025 case ARG_EMAIL_ADDRESS
:
3028 case ARG_CIFS_USER_NAME
:
3029 case ARG_CIFS_DOMAIN
:
3030 case ARG_CIFS_EXTRA_MOUNT_OPTIONS
:
3031 case ARG_LUKS_EXTRA_MOUNT_OPTIONS
:
3032 case ARG_SESSION_LAUNCHER
:
3033 case ARG_SESSION_TYPE
: {
3036 c
== ARG_EMAIL_ADDRESS
? "emailAddress" :
3037 c
== ARG_LOCATION
? "location" :
3038 c
== ARG_ICON_NAME
? "iconName" :
3039 c
== ARG_CIFS_USER_NAME
? "cifsUserName" :
3040 c
== ARG_CIFS_DOMAIN
? "cifsDomain" :
3041 c
== ARG_CIFS_EXTRA_MOUNT_OPTIONS
? "cifsExtraMountOptions" :
3042 c
== ARG_LUKS_EXTRA_MOUNT_OPTIONS
? "luksExtraMountOptions" :
3043 c
== ARG_SESSION_LAUNCHER
? "preferredSessionLauncher" :
3044 c
== ARG_SESSION_TYPE
? "preferredSessionType" :
3049 if (isempty(optarg
)) {
3050 r
= drop_from_identity(field
);
3057 r
= json_variant_set_field_string(&arg_identity_extra
, field
, optarg
);
3059 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3064 case ARG_CIFS_SERVICE
:
3065 if (isempty(optarg
)) {
3066 r
= drop_from_identity("cifsService");
3073 r
= parse_cifs_service(optarg
, NULL
, NULL
, NULL
);
3075 return log_error_errno(r
, "Failed to validate CIFS service name: %s", optarg
);
3077 r
= json_variant_set_field_string(&arg_identity_extra
, "cifsService", optarg
);
3079 return log_error_errno(r
, "Failed to set cifsService field: %m");
3083 case ARG_PASSWORD_HINT
:
3084 if (isempty(optarg
)) {
3085 r
= drop_from_identity("passwordHint");
3092 r
= json_variant_set_field_string(&arg_identity_extra_privileged
, "passwordHint", optarg
);
3094 return log_error_errno(r
, "Failed to set passwordHint field: %m");
3096 string_erase(optarg
);
3102 if (isempty(optarg
)) {
3103 r
= drop_from_identity("niceLevel");
3109 r
= parse_nice(optarg
, &nc
);
3111 return log_error_errno(r
, "Failed to parse nice level: %s", optarg
);
3113 r
= json_variant_set_field_integer(&arg_identity_extra
, "niceLevel", nc
);
3115 return log_error_errno(r
, "Failed to set niceLevel field: %m");
3121 _cleanup_(json_variant_unrefp
) JsonVariant
*jcur
= NULL
, *jmax
= NULL
;
3122 _cleanup_free_
char *field
= NULL
, *t
= NULL
;
3127 if (isempty(optarg
)) {
3128 /* Remove all resource limits */
3130 r
= drop_from_identity("resourceLimits");
3134 arg_identity_filter_rlimits
= strv_free(arg_identity_filter_rlimits
);
3135 arg_identity_extra_rlimits
= json_variant_unref(arg_identity_extra_rlimits
);
3139 eq
= strchr(optarg
, '=');
3141 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Can't parse resource limit assignment: %s", optarg
);
3143 field
= strndup(optarg
, eq
- optarg
);
3147 l
= rlimit_from_string_harder(field
);
3149 return log_error_errno(l
, "Unknown resource limit type: %s", field
);
3151 if (isempty(eq
+ 1)) {
3152 /* Remove only the specific rlimit */
3154 r
= strv_extend(&arg_identity_filter_rlimits
, rlimit_to_string(l
));
3158 r
= json_variant_filter(&arg_identity_extra_rlimits
, STRV_MAKE(field
));
3160 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
3165 r
= rlimit_parse(l
, eq
+ 1, &rl
);
3167 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to parse resource limit value: %s", eq
+ 1);
3169 r
= rl
.rlim_cur
== RLIM_INFINITY
? json_variant_new_null(&jcur
) : json_variant_new_unsigned(&jcur
, rl
.rlim_cur
);
3171 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to allocate current integer: %m");
3173 r
= rl
.rlim_max
== RLIM_INFINITY
? json_variant_new_null(&jmax
) : json_variant_new_unsigned(&jmax
, rl
.rlim_max
);
3175 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to allocate maximum integer: %m");
3177 t
= strjoin("RLIMIT_", rlimit_to_string(l
));
3181 r
= json_variant_set_fieldb(
3182 &arg_identity_extra_rlimits
, t
,
3184 JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur
)),
3185 JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax
))));
3187 return log_error_errno(r
, "Failed to set %s field: %m", rlimit_to_string(l
));
3195 if (isempty(optarg
)) {
3196 r
= drop_from_identity("uid");
3203 r
= parse_uid(optarg
, &uid
);
3205 return log_error_errno(r
, "Failed to parse UID '%s'.", optarg
);
3207 if (uid_is_system(uid
))
3208 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in system range, refusing.", uid
);
3209 if (uid_is_dynamic(uid
))
3210 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in dynamic range, refusing.", uid
);
3211 if (uid
== UID_NOBODY
)
3212 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is nobody UID, refusing.", uid
);
3214 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "uid", uid
);
3216 return log_error_errno(r
, "Failed to set realm field: %m");
3222 case ARG_IMAGE_PATH
: {
3223 const char *field
= c
== 'k' ? "skeletonDirectory" : "imagePath";
3224 _cleanup_free_
char *v
= NULL
;
3226 if (isempty(optarg
)) {
3227 r
= drop_from_identity(field
);
3234 r
= parse_path_argument(optarg
, false, &v
);
3238 r
= json_variant_set_field_string(&arg_identity_extra_this_machine
, field
, v
);
3240 return log_error_errno(r
, "Failed to set %s field: %m", v
);
3246 if (isempty(optarg
)) {
3247 r
= drop_from_identity("shell");
3254 if (!valid_shell(optarg
))
3255 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Shell '%s' not valid.", optarg
);
3257 r
= json_variant_set_field_string(&arg_identity_extra
, "shell", optarg
);
3259 return log_error_errno(r
, "Failed to set shell field: %m");
3264 _cleanup_free_
char **l
= NULL
;
3265 _cleanup_(json_variant_unrefp
) JsonVariant
*ne
= NULL
;
3268 if (isempty(optarg
)) {
3269 r
= drop_from_identity("environment");
3276 e
= json_variant_by_key(arg_identity_extra
, "environment");
3278 r
= json_variant_strv(e
, &l
);
3280 return log_error_errno(r
, "Failed to parse JSON environment field: %m");
3283 r
= strv_env_replace_strdup_passthrough(&l
, optarg
);
3285 return log_error_errno(r
, "Cannot assign environment variable %s: %m", optarg
);
3289 r
= json_variant_new_array_strv(&ne
, l
);
3291 return log_error_errno(r
, "Failed to allocate environment list JSON: %m");
3293 r
= json_variant_set_field(&arg_identity_extra
, "environment", ne
);
3295 return log_error_errno(r
, "Failed to set environment list: %m");
3302 if (isempty(optarg
)) {
3303 r
= drop_from_identity("timeZone");
3310 if (!timezone_is_valid(optarg
, LOG_DEBUG
))
3311 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Timezone '%s' is not valid.", optarg
);
3313 r
= json_variant_set_field_string(&arg_identity_extra
, "timeZone", optarg
);
3315 return log_error_errno(r
, "Failed to set timezone field: %m");
3319 case ARG_LANGUAGE
: {
3320 const char *p
= optarg
;
3323 r
= drop_from_identity("preferredLanguage");
3327 r
= drop_from_identity("additionalLanguages");
3331 arg_languages
= strv_free(arg_languages
);
3336 _cleanup_free_
char *word
= NULL
;
3338 r
= extract_first_word(&p
, &word
, ",:", 0);
3340 return log_error_errno(r
, "Failed to parse locale list: %m");
3344 if (!locale_is_valid(word
))
3345 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Locale '%s' is not valid.", word
);
3347 if (locale_is_installed(word
) <= 0)
3348 log_warning("Locale '%s' is not installed, accepting anyway.", word
);
3350 r
= strv_consume(&arg_languages
, TAKE_PTR(word
));
3354 strv_uniq(arg_languages
);
3364 case ARG_KILL_PROCESSES
:
3365 case ARG_ENFORCE_PASSWORD_POLICY
:
3366 case ARG_AUTO_LOGIN
:
3367 case ARG_PASSWORD_CHANGE_NOW
: {
3369 c
== ARG_LOCKED
? "locked" :
3370 c
== ARG_NOSUID
? "mountNoSuid" :
3371 c
== ARG_NODEV
? "mountNoDevices" :
3372 c
== ARG_NOEXEC
? "mountNoExecute" :
3373 c
== ARG_KILL_PROCESSES
? "killProcesses" :
3374 c
== ARG_ENFORCE_PASSWORD_POLICY
? "enforcePasswordPolicy" :
3375 c
== ARG_AUTO_LOGIN
? "autoLogin" :
3376 c
== ARG_PASSWORD_CHANGE_NOW
? "passwordChangeNow" :
3381 if (isempty(optarg
)) {
3382 r
= drop_from_identity(field
);
3389 r
= parse_boolean(optarg
);
3391 return log_error_errno(r
, "Failed to parse %s boolean: %m", field
);
3393 r
= json_variant_set_field_boolean(&arg_identity_extra
, field
, r
> 0);
3395 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3401 r
= json_variant_set_field_boolean(&arg_identity_extra
, "enforcePasswordPolicy", false);
3403 return log_error_errno(r
, "Failed to set enforcePasswordPolicy field: %m");
3408 if (isempty(optarg
)) {
3409 FOREACH_STRING(prop
, "diskSize", "diskSizeRelative", "rebalanceWeight") {
3410 r
= drop_from_identity(prop
);
3415 arg_disk_size
= arg_disk_size_relative
= UINT64_MAX
;
3419 r
= parse_permyriad(optarg
);
3421 r
= parse_disk_size(optarg
, &arg_disk_size
);
3425 r
= drop_from_identity("diskSizeRelative");
3429 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "diskSize", arg_disk_size
);
3431 return log_error_errno(r
, "Failed to set diskSize field: %m");
3433 arg_disk_size_relative
= UINT64_MAX
;
3435 /* Normalize to UINT32_MAX == 100% */
3436 arg_disk_size_relative
= UINT32_SCALE_FROM_PERMYRIAD(r
);
3438 r
= drop_from_identity("diskSize");
3442 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "diskSizeRelative", arg_disk_size_relative
);
3444 return log_error_errno(r
, "Failed to set diskSizeRelative field: %m");
3446 arg_disk_size
= UINT64_MAX
;
3449 /* Automatically turn off the rebalance logic if user configured a size explicitly */
3450 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "rebalanceWeight", REBALANCE_WEIGHT_OFF
);
3452 return log_error_errno(r
, "Failed to set rebalanceWeight field: %m");
3456 case ARG_ACCESS_MODE
: {
3459 if (isempty(optarg
)) {
3460 r
= drop_from_identity("accessMode");
3467 r
= parse_mode(optarg
, &mode
);
3469 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Access mode '%s' not valid.", optarg
);
3471 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "accessMode", mode
);
3473 return log_error_errno(r
, "Failed to set access mode field: %m");
3478 case ARG_LUKS_DISCARD
:
3479 if (isempty(optarg
)) {
3480 r
= drop_from_identity("luksDiscard");
3487 r
= parse_boolean(optarg
);
3489 return log_error_errno(r
, "Failed to parse --luks-discard= parameter: %s", optarg
);
3491 r
= json_variant_set_field_boolean(&arg_identity_extra
, "luksDiscard", r
);
3493 return log_error_errno(r
, "Failed to set discard field: %m");
3497 case ARG_LUKS_OFFLINE_DISCARD
:
3498 if (isempty(optarg
)) {
3499 r
= drop_from_identity("luksOfflineDiscard");
3506 r
= parse_boolean(optarg
);
3508 return log_error_errno(r
, "Failed to parse --luks-offline-discard= parameter: %s", optarg
);
3510 r
= json_variant_set_field_boolean(&arg_identity_extra
, "luksOfflineDiscard", r
);
3512 return log_error_errno(r
, "Failed to set offline discard field: %m");
3516 case ARG_LUKS_VOLUME_KEY_SIZE
:
3517 case ARG_LUKS_PBKDF_FORCE_ITERATIONS
:
3518 case ARG_LUKS_PBKDF_PARALLEL_THREADS
:
3519 case ARG_RATE_LIMIT_BURST
: {
3521 c
== ARG_LUKS_VOLUME_KEY_SIZE
? "luksVolumeKeySize" :
3522 c
== ARG_LUKS_PBKDF_FORCE_ITERATIONS
? "luksPbkdfForceIterations" :
3523 c
== ARG_LUKS_PBKDF_PARALLEL_THREADS
? "luksPbkdfParallelThreads" :
3524 c
== ARG_RATE_LIMIT_BURST
? "rateLimitBurst" : NULL
;
3529 if (isempty(optarg
)) {
3530 r
= drop_from_identity(field
);
3535 r
= safe_atou(optarg
, &n
);
3537 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
3539 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3541 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3546 case ARG_LUKS_SECTOR_SIZE
: {
3549 if (isempty(optarg
)) {
3550 r
= drop_from_identity("luksSectorSize");
3557 r
= parse_sector_size(optarg
, &ss
);
3561 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "luksSectorSize", ss
);
3563 return log_error_errno(r
, "Failed to set sector size field: %m");
3571 if (isempty(optarg
)) {
3572 r
= drop_from_identity("umask");
3579 r
= parse_mode(optarg
, &m
);
3581 return log_error_errno(r
, "Failed to parse umask: %m");
3583 r
= json_variant_set_field_integer(&arg_identity_extra
, "umask", m
);
3585 return log_error_errno(r
, "Failed to set umask field: %m");
3590 case ARG_SSH_AUTHORIZED_KEYS
: {
3591 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
3592 _cleanup_strv_free_
char **l
= NULL
, **add
= NULL
;
3594 if (isempty(optarg
)) {
3595 r
= drop_from_identity("sshAuthorizedKeys");
3602 if (optarg
[0] == '@') {
3603 _cleanup_fclose_
FILE *f
= NULL
;
3605 /* If prefixed with '@' read from a file */
3607 f
= fopen(optarg
+1, "re");
3609 return log_error_errno(errno
, "Failed to open '%s': %m", optarg
+1);
3612 _cleanup_free_
char *line
= NULL
;
3614 r
= read_line(f
, LONG_LINE_MAX
, &line
);
3616 return log_error_errno(r
, "Failed to read from '%s': %m", optarg
+1);
3626 r
= strv_consume(&add
, TAKE_PTR(line
));
3631 /* Otherwise, assume it's a literal key. Let's do some superficial checks
3632 * before accept it though. */
3634 if (string_has_cc(optarg
, NULL
))
3635 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Authorized key contains control characters, refusing.");
3636 if (optarg
[0] == '#')
3637 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specified key is a comment?");
3639 add
= strv_new(optarg
);
3644 v
= json_variant_ref(json_variant_by_key(arg_identity_extra_privileged
, "sshAuthorizedKeys"));
3646 r
= json_variant_strv(v
, &l
);
3648 return log_error_errno(r
, "Failed to parse SSH authorized keys list: %m");
3651 r
= strv_extend_strv(&l
, add
, true);
3655 v
= json_variant_unref(v
);
3657 r
= json_variant_new_array_strv(&v
, l
);
3661 r
= json_variant_set_field(&arg_identity_extra_privileged
, "sshAuthorizedKeys", v
);
3663 return log_error_errno(r
, "Failed to set authorized keys: %m");
3668 case ARG_NOT_BEFORE
:
3674 field
= c
== ARG_NOT_BEFORE
? "notBeforeUSec" :
3675 IN_SET(c
, ARG_NOT_AFTER
, 'e') ? "notAfterUSec" : NULL
;
3679 if (isempty(optarg
)) {
3680 r
= drop_from_identity(field
);
3687 /* Note the minor discrepancy regarding -e parsing here: we support that for compat
3688 * reasons, and in the original useradd(8) implementation it accepts dates in the
3689 * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
3690 * with greater precision. */
3691 r
= parse_timestamp(optarg
, &n
);
3693 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
3695 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3697 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3701 case ARG_PASSWORD_CHANGE_MIN
:
3702 case ARG_PASSWORD_CHANGE_MAX
:
3703 case ARG_PASSWORD_CHANGE_WARN
:
3704 case ARG_PASSWORD_CHANGE_INACTIVE
: {
3708 field
= c
== ARG_PASSWORD_CHANGE_MIN
? "passwordChangeMinUSec" :
3709 c
== ARG_PASSWORD_CHANGE_MAX
? "passwordChangeMaxUSec" :
3710 c
== ARG_PASSWORD_CHANGE_WARN
? "passwordChangeWarnUSec" :
3711 c
== ARG_PASSWORD_CHANGE_INACTIVE
? "passwordChangeInactiveUSec" :
3716 if (isempty(optarg
)) {
3717 r
= drop_from_identity(field
);
3724 r
= parse_sec(optarg
, &n
);
3726 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
3728 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3730 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3736 case ARG_LUKS_CIPHER
:
3737 case ARG_LUKS_CIPHER_MODE
:
3738 case ARG_LUKS_PBKDF_TYPE
:
3739 case ARG_LUKS_PBKDF_HASH_ALGORITHM
: {
3742 c
== ARG_STORAGE
? "storage" :
3743 c
== ARG_FS_TYPE
? "fileSystemType" :
3744 c
== ARG_LUKS_CIPHER
? "luksCipher" :
3745 c
== ARG_LUKS_CIPHER_MODE
? "luksCipherMode" :
3746 c
== ARG_LUKS_PBKDF_TYPE
? "luksPbkdfType" :
3747 c
== ARG_LUKS_PBKDF_HASH_ALGORITHM
? "luksPbkdfHashAlgorithm" : NULL
;
3751 if (isempty(optarg
)) {
3752 r
= drop_from_identity(field
);
3759 if (!string_is_safe(optarg
))
3760 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Parameter for %s field not valid: %s", field
, optarg
);
3762 r
= json_variant_set_field_string(
3763 IN_SET(c
, ARG_STORAGE
, ARG_FS_TYPE
) ?
3764 &arg_identity_extra_this_machine
:
3765 &arg_identity_extra
, field
, optarg
);
3767 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3772 case ARG_LUKS_PBKDF_TIME_COST
:
3773 case ARG_RATE_LIMIT_INTERVAL
:
3774 case ARG_STOP_DELAY
: {
3776 c
== ARG_LUKS_PBKDF_TIME_COST
? "luksPbkdfTimeCostUSec" :
3777 c
== ARG_RATE_LIMIT_INTERVAL
? "rateLimitIntervalUSec" :
3778 c
== ARG_STOP_DELAY
? "stopDelayUSec" :
3784 if (isempty(optarg
)) {
3785 r
= drop_from_identity(field
);
3792 r
= parse_sec(optarg
, &t
);
3794 return log_error_errno(r
, "Failed to parse %s field: %s", field
, optarg
);
3796 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, t
);
3798 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3804 const char *p
= optarg
;
3807 r
= drop_from_identity("memberOf");
3815 _cleanup_(json_variant_unrefp
) JsonVariant
*mo
= NULL
;
3816 _cleanup_strv_free_
char **list
= NULL
;
3817 _cleanup_free_
char *word
= NULL
;
3819 r
= extract_first_word(&p
, &word
, ",", 0);
3821 return log_error_errno(r
, "Failed to parse group list: %m");
3825 if (!valid_user_group_name(word
, 0))
3826 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid group name %s.", word
);
3828 mo
= json_variant_ref(json_variant_by_key(arg_identity_extra
, "memberOf"));
3830 r
= json_variant_strv(mo
, &list
);
3832 return log_error_errno(r
, "Failed to parse group list: %m");
3834 r
= strv_extend(&list
, word
);
3841 mo
= json_variant_unref(mo
);
3842 r
= json_variant_new_array_strv(&mo
, list
);
3844 return log_error_errno(r
, "Failed to create group list JSON: %m");
3846 r
= json_variant_set_field(&arg_identity_extra
, "memberOf", mo
);
3848 return log_error_errno(r
, "Failed to update group list: %m");
3854 case ARG_TASKS_MAX
: {
3857 if (isempty(optarg
)) {
3858 r
= drop_from_identity("tasksMax");
3864 r
= safe_atou64(optarg
, &u
);
3866 return log_error_errno(r
, "Failed to parse --tasks-max= parameter: %s", optarg
);
3868 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "tasksMax", u
);
3870 return log_error_errno(r
, "Failed to set tasksMax field: %m");
3875 case ARG_MEMORY_MAX
:
3876 case ARG_MEMORY_HIGH
:
3877 case ARG_LUKS_PBKDF_MEMORY_COST
: {
3879 c
== ARG_MEMORY_MAX
? "memoryMax" :
3880 c
== ARG_MEMORY_HIGH
? "memoryHigh" :
3881 c
== ARG_LUKS_PBKDF_MEMORY_COST
? "luksPbkdfMemoryCost" : NULL
;
3887 if (isempty(optarg
)) {
3888 r
= drop_from_identity(field
);
3894 r
= parse_size(optarg
, 1024, &u
);
3896 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
3898 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, field
, u
);
3900 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3905 case ARG_CPU_WEIGHT
:
3906 case ARG_IO_WEIGHT
: {
3907 const char *field
= c
== ARG_CPU_WEIGHT
? "cpuWeight" :
3908 c
== ARG_IO_WEIGHT
? "ioWeight" : NULL
;
3913 if (isempty(optarg
)) {
3914 r
= drop_from_identity(field
);
3920 r
= safe_atou64(optarg
, &u
);
3922 return log_error_errno(r
, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg
);
3924 if (!CGROUP_WEIGHT_IS_OK(u
))
3925 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Weight %" PRIu64
" is out of valid weight range.", u
);
3927 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, u
);
3929 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3934 case ARG_PKCS11_TOKEN_URI
:
3935 if (streq(optarg
, "list"))
3936 return pkcs11_list_tokens();
3938 /* If --pkcs11-token-uri= is specified we always drop everything old */
3939 FOREACH_STRING(p
, "pkcs11TokenUri", "pkcs11EncryptedKey") {
3940 r
= drop_from_identity(p
);
3945 if (isempty(optarg
)) {
3946 arg_pkcs11_token_uri
= strv_free(arg_pkcs11_token_uri
);
3950 if (streq(optarg
, "auto")) {
3951 _cleanup_free_
char *found
= NULL
;
3953 r
= pkcs11_find_token_auto(&found
);
3956 r
= strv_consume(&arg_pkcs11_token_uri
, TAKE_PTR(found
));
3958 if (!pkcs11_uri_valid(optarg
))
3959 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Not a valid PKCS#11 URI: %s", optarg
);
3961 r
= strv_extend(&arg_pkcs11_token_uri
, optarg
);
3966 strv_uniq(arg_pkcs11_token_uri
);
3969 case ARG_FIDO2_CRED_ALG
:
3970 r
= parse_fido2_algorithm(optarg
, &arg_fido2_cred_alg
);
3972 return log_error_errno(r
, "Failed to parse COSE algorithm: %s", optarg
);
3975 case ARG_FIDO2_DEVICE
:
3976 if (streq(optarg
, "list"))
3977 return fido2_list_devices();
3979 FOREACH_STRING(p
, "fido2HmacCredential", "fido2HmacSalt") {
3980 r
= drop_from_identity(p
);
3985 if (isempty(optarg
)) {
3986 arg_fido2_device
= strv_free(arg_fido2_device
);
3990 if (streq(optarg
, "auto")) {
3991 _cleanup_free_
char *found
= NULL
;
3993 r
= fido2_find_device_auto(&found
);
3997 r
= strv_consume(&arg_fido2_device
, TAKE_PTR(found
));
3999 r
= strv_extend(&arg_fido2_device
, optarg
);
4003 strv_uniq(arg_fido2_device
);
4006 case ARG_FIDO2_WITH_PIN
:
4007 r
= parse_boolean_argument("--fido2-with-client-pin=", optarg
, NULL
);
4011 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_PIN
, r
);
4014 case ARG_FIDO2_WITH_UP
:
4015 r
= parse_boolean_argument("--fido2-with-user-presence=", optarg
, NULL
);
4019 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UP
, r
);
4022 case ARG_FIDO2_WITH_UV
:
4023 r
= parse_boolean_argument("--fido2-with-user-verification=", optarg
, NULL
);
4027 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UV
, r
);
4030 case ARG_RECOVERY_KEY
:
4031 r
= parse_boolean(optarg
);
4033 return log_error_errno(r
, "Failed to parse --recovery-key= argument: %s", optarg
);
4035 arg_recovery_key
= r
;
4037 FOREACH_STRING(p
, "recoveryKey", "recoveryKeyType") {
4038 r
= drop_from_identity(p
);
4045 case ARG_AUTO_RESIZE_MODE
:
4046 if (isempty(optarg
)) {
4047 r
= drop_from_identity("autoResizeMode");
4054 r
= auto_resize_mode_from_string(optarg
);
4056 return log_error_errno(r
, "Failed to parse --auto-resize-mode= argument: %s", optarg
);
4058 r
= json_variant_set_field_string(&arg_identity_extra
, "autoResizeMode", auto_resize_mode_to_string(r
));
4060 return log_error_errno(r
, "Failed to set autoResizeMode field: %m");
4064 case ARG_REBALANCE_WEIGHT
: {
4067 if (isempty(optarg
)) {
4068 r
= drop_from_identity("rebalanceWeight");
4074 if (streq(optarg
, "off"))
4075 u
= REBALANCE_WEIGHT_OFF
;
4077 r
= safe_atou64(optarg
, &u
);
4079 return log_error_errno(r
, "Failed to parse --rebalance-weight= argument: %s", optarg
);
4081 if (u
< REBALANCE_WEIGHT_MIN
|| u
> REBALANCE_WEIGHT_MAX
)
4082 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
), "Rebalancing weight out of valid range %" PRIu64
"%s%" PRIu64
": %s",
4083 REBALANCE_WEIGHT_MIN
, special_glyph(SPECIAL_GLYPH_ELLIPSIS
), REBALANCE_WEIGHT_MAX
, optarg
);
4086 /* Drop from per machine stuff and everywhere */
4087 r
= drop_from_identity("rebalanceWeight");
4091 /* Add to main identity */
4092 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "rebalanceWeight", u
);
4094 return log_error_errno(r
, "Failed to set rebalanceWeight field: %m");
4100 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
4104 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
4111 if (arg_export_format
== EXPORT_FORMAT_FULL
)
4112 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
4113 else if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
4114 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
4116 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specifying -E more than twice is not supported.");
4118 arg_json_format_flags
&= ~JSON_FORMAT_OFF
;
4119 if (arg_json_format_flags
== 0)
4120 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
4123 case ARG_EXPORT_FORMAT
:
4124 if (streq(optarg
, "full"))
4125 arg_export_format
= EXPORT_FORMAT_FULL
;
4126 else if (streq(optarg
, "stripped"))
4127 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
4128 else if (streq(optarg
, "minimal"))
4129 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
4130 else if (streq(optarg
, "help")) {
4139 case ARG_AND_RESIZE
:
4140 arg_and_resize
= true;
4143 case ARG_AND_CHANGE_PASSWORD
:
4144 arg_and_change_password
= true;
4147 case ARG_DROP_CACHES
: {
4148 if (isempty(optarg
)) {
4149 r
= drop_from_identity("dropCaches");
4155 r
= parse_boolean_argument("--drop-caches=", optarg
, NULL
);
4159 r
= json_variant_set_field_boolean(&arg_identity_extra
, "dropCaches", r
);
4161 return log_error_errno(r
, "Failed to set drop caches field: %m");
4166 case ARG_CAPABILITY_AMBIENT_SET
:
4167 case ARG_CAPABILITY_BOUNDING_SET
: {
4168 _cleanup_strv_free_
char **l
= NULL
;
4169 bool subtract
= false;
4170 uint64_t parsed
, *which
, updated
;
4171 const char *p
, *field
;
4173 if (c
== ARG_CAPABILITY_AMBIENT_SET
) {
4174 which
= &arg_capability_ambient_set
;
4175 field
= "capabilityAmbientSet";
4177 assert(c
== ARG_CAPABILITY_BOUNDING_SET
);
4178 which
= &arg_capability_bounding_set
;
4179 field
= "capabilityBoundingSet";
4182 if (isempty(optarg
)) {
4183 r
= drop_from_identity(field
);
4187 *which
= UINT64_MAX
;
4197 r
= capability_set_from_string(p
, &parsed
);
4199 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid capabilities in capability string '%s'.", p
);
4201 return log_error_errno(r
, "Failed to parse capability string '%s': %m", p
);
4203 if (*which
== UINT64_MAX
)
4204 updated
= subtract
? all_capabilities() & ~parsed
: parsed
;
4206 updated
= *which
& ~parsed
;
4208 updated
= *which
| parsed
;
4210 if (capability_set_to_strv(updated
, &l
) < 0)
4213 r
= json_variant_set_field_strv(&arg_identity_extra
, field
, l
);
4215 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4221 case ARG_PROMPT_NEW_USER
:
4222 arg_prompt_new_user
= true;
4227 case ARG_LOGIN_BACKGROUND
: {
4228 _cleanup_close_
int fd
= -EBADF
;
4229 _cleanup_free_
char *path
= NULL
, *filename
= NULL
;
4234 if (isempty(optarg
)) { /* --blob= deletes everything, including existing blob dirs */
4235 hashmap_clear(arg_blob_files
);
4236 arg_blob_dir
= mfree(arg_blob_dir
);
4237 arg_blob_clear
= true;
4241 eq
= strrchr(optarg
, '=');
4242 if (!eq
) { /* --blob=/some/path replaces the blob dir */
4243 r
= parse_path_argument(optarg
, false, &arg_blob_dir
);
4245 return log_error_errno(r
, "Failed to parse path %s: %m", optarg
);
4249 /* --blob=filename=/some/path replaces the file "filename" with /some/path */
4250 filename
= strndup(optarg
, eq
- optarg
);
4254 if (isempty(filename
))
4255 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Can't parse blob file assignment: %s", optarg
);
4256 if (!suitable_blob_filename(filename
))
4257 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid blob filename: %s", filename
);
4259 r
= parse_path_argument(eq
+ 1, false, &path
);
4261 return log_error_errno(r
, "Failed to parse path %s: %m", eq
+ 1);
4263 const char *well_known_filename
=
4264 c
== ARG_AVATAR
? "avatar" :
4265 c
== ARG_LOGIN_BACKGROUND
? "login-background" :
4267 assert(well_known_filename
);
4269 filename
= strdup(well_known_filename
);
4273 r
= parse_path_argument(optarg
, false, &path
);
4275 return log_error_errno(r
, "Failed to parse path %s: %m", optarg
);
4279 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
4281 return log_error_errno(errno
, "Failed to open %s: %m", path
);
4283 if (fd_verify_regular(fd
) < 0)
4284 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Provided blob is not a regular file: %s", path
);
4286 fd
= -EBADF
; /* Delete the file */
4288 r
= hashmap_ensure_put(&arg_blob_files
, &blob_fd_hash_ops
, filename
, FD_TO_PTR(fd
));
4290 return log_error_errno(r
, "Failed to map %s to %s in blob directory: %m", path
, filename
);
4291 TAKE_PTR(filename
); /* hashmap takes ownership */
4301 assert_not_reached();
4305 if (!strv_isempty(arg_pkcs11_token_uri
) || !strv_isempty(arg_fido2_device
))
4306 arg_and_change_password
= true;
4308 if (arg_disk_size
!= UINT64_MAX
|| arg_disk_size_relative
!= UINT64_MAX
)
4309 arg_and_resize
= true;
4311 if (!strv_isempty(arg_languages
)) {
4314 r
= json_variant_set_field_string(&arg_identity_extra
, "preferredLanguage", arg_languages
[0]);
4316 return log_error_errno(r
, "Failed to update preferred language: %m");
4318 additional
= strv_skip(arg_languages
, 1);
4319 if (!strv_isempty(additional
)) {
4320 r
= json_variant_set_field_strv(&arg_identity_extra
, "additionalLanguages", additional
);
4322 return log_error_errno(r
, "Failed to update additional language list: %m");
4324 r
= drop_from_identity("additionalLanguages");
4333 static int redirect_bus_mgr(void) {
4336 /* Talk to a different service if that's requested. (The same env var is also understood by homed, so
4337 * that it is relatively easily possible to invoke a second instance of homed for debug purposes and
4338 * have homectl talk to it, without colliding with the host version. This is handy when operating
4339 * from a homed-managed account.) */
4341 suffix
= getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
4343 static BusLocator locator
= {
4344 .path
= "/org/freedesktop/home1",
4345 .interface
= "org.freedesktop.home1.Manager",
4348 /* Yes, we leak this memory, but there's little point to collect this, given that we only do
4349 * this in a debug environment, do it only once, and the string shall live for out entire
4350 * process runtime. */
4352 locator
.destination
= strjoin("org.freedesktop.home1.", suffix
);
4353 if (!locator
.destination
)
4358 bus_mgr
= bus_home_mgr
;
4363 static bool is_fallback_shell(const char *p
) {
4370 /* Skip over login shell dash */
4373 if (streq(p
, "ystemd-home-fallback-shell")) /* maybe the dash was used to override the binary name? */
4377 q
= strrchr(p
, '/'); /* Skip over path */
4381 return streq(p
, "systemd-home-fallback-shell");
4384 static int fallback_shell(int argc
, char *argv
[]) {
4385 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
, *hr
= NULL
;
4386 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
4387 _cleanup_strv_free_
char **l
= NULL
;
4388 _cleanup_free_
char *argv0
= NULL
;
4389 const char *json
, *hd
, *shell
;
4392 /* So here's the deal: if users log into a system via ssh, and their homed-managed home directory
4393 * wasn't activated yet, SSH will permit the access but the home directory isn't actually available
4394 * yet. SSH doesn't allow us to ask authentication questions from the PAM session stack, and doesn't
4395 * run the PAM authentication stack (because it authenticates via its own key management, after
4396 * all). So here's our way to support this: homectl can be invoked as a multi-call binary under the
4397 * name "systemd-home-fallback-shell". If so, it will chainload a login shell, but first try to
4398 * unlock the home directory of the user it is invoked as. systemd-homed will then override the shell
4399 * listed in user records whose home directory is not activated yet with this pseudo-shell. Net
4400 * effect: one SSH auth succeeds this pseudo shell gets invoked, which will unlock the homedir
4401 * (possibly asking for a passphrase) and then chainload the regular shell. Once the login is
4402 * complete the user record will look like any other. */
4404 r
= acquire_bus(&bus
);
4408 for (unsigned n_tries
= 0;; n_tries
++) {
4409 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
4410 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
4411 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
4414 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
4415 "Failed to activate home dir, even after %u tries.", n_tries
);
4417 /* Let's start by checking if this all is even necessary, i.e. if the useFallback boolean field is actually set. */
4418 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByName", &error
, &reply
, "s", NULL
); /* empty user string means: our calling user */
4420 return log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
4422 r
= sd_bus_message_read(reply
, "sbo", &json
, NULL
, NULL
);
4424 return bus_log_parse_error(r
);
4426 r
= json_parse(json
, JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
4428 return log_error_errno(r
, "Failed to parse JSON identity: %m");
4430 hr
= user_record_new();
4434 r
= user_record_load(hr
, v
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_LOG
|USER_RECORD_PERMISSIVE
);
4438 if (!hr
->use_fallback
) /* Nice! We are done, fallback logic not necessary */
4442 r
= acquire_passed_secrets(hr
->user_name
, &secret
);
4448 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
4450 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ActivateHomeIfReferenced");
4452 return bus_log_create_error(r
);
4454 r
= sd_bus_message_append(m
, "s", NULL
); /* empty user string means: our calling user */
4456 return bus_log_create_error(r
);
4458 r
= bus_message_append_secret(m
, secret
);
4460 return bus_log_create_error(r
);
4462 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
4464 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_NOT_REFERENCED
))
4465 return log_error_errno(r
, "Called without reference on home taken, can't operate.");
4467 r
= handle_generic_user_record_error(hr
->user_name
, secret
, &error
, r
, false);
4471 sd_bus_error_free(&error
);
4477 hr
= user_record_unref(hr
);
4480 incomplete
= getenv_bool("XDG_SESSION_INCOMPLETE"); /* pam_systemd_home reports this state via an environment variable to us. */
4481 if (incomplete
< 0 && incomplete
!= -ENXIO
)
4482 return log_error_errno(incomplete
, "Failed to parse $XDG_SESSION_INCOMPLETE environment variable: %m");
4483 if (incomplete
> 0) {
4484 /* We are still in an "incomplete" session here. Now upgrade it to a full one. This will make logind
4485 * start the user@.service instance for us. */
4486 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
4487 r
= sd_bus_call_method(
4489 "org.freedesktop.login1",
4490 "/org/freedesktop/login1/session/self",
4491 "org.freedesktop.login1.Session",
4494 /* ret_reply= */ NULL
,
4498 return log_error_errno(r
, "Failed to upgrade session: %s", bus_error_message(&error
, r
));
4500 if (setenv("XDG_SESSION_CLASS", "user", /* overwrite= */ true) < 0) /* Update the XDG_SESSION_CLASS environment variable to match the above */
4501 return log_error_errno(errno
, "Failed to set $XDG_SESSION_CLASS: %m");
4503 if (unsetenv("XDG_SESSION_INCOMPLETE") < 0) /* Unset the 'incomplete' env var */
4504 return log_error_errno(errno
, "Failed to unset $XDG_SESSION_INCOMPLETE: %m");
4507 /* We are going to invoke execv() soon. Let's be extra accurate and flush/close our bus connection
4508 * first, just to make sure anything queued is flushed out (though there shouldn't be anything) */
4509 bus
= sd_bus_flush_close_unref(bus
);
4511 assert(!hr
->use_fallback
);
4512 assert_se(shell
= user_record_shell(hr
));
4513 assert_se(hd
= user_record_home_directory(hr
));
4515 /* Extra protection: avoid loops */
4516 if (is_fallback_shell(shell
))
4517 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Primary shell of '%s' is fallback shell, refusing loop.", hr
->user_name
);
4520 return log_error_errno(errno
, "Failed to change directory to home directory '%s': %m", hd
);
4522 if (setenv("SHELL", shell
, /* overwrite= */ true) < 0)
4523 return log_error_errno(errno
, "Failed to set $SHELL: %m");
4525 if (setenv("HOME", hd
, /* overwrite= */ true) < 0)
4526 return log_error_errno(errno
, "Failed to set $HOME: %m");
4528 /* Paranoia: in case the client passed some passwords to us to help us unlock, unlock things now */
4529 FOREACH_STRING(ue
, "PASSWORD", "NEWPASSWORD", "PIN")
4530 if (unsetenv(ue
) < 0)
4531 return log_error_errno(errno
, "Failed to unset $%s: %m", ue
);
4533 r
= path_extract_filename(shell
, &argv0
);
4535 return log_error_errno(r
, "Unable to extract file name from '%s': %m", shell
);
4536 if (r
== O_DIRECTORY
)
4537 return log_error_errno(SYNTHETIC_ERRNO(EISDIR
), "Shell '%s' is a path to a directory, refusing.", shell
);
4539 /* Invoke this as login shell, by setting argv[0][0] to '-' (unless we ourselves weren't called as login shell) */
4540 if (!argv
|| isempty(argv
[0]) || argv
[0][0] == '-')
4543 l
= strv_new(argv0
);
4547 if (strv_extend_strv(&l
, strv_skip(argv
, 1), /* filter_duplicates= */ false) < 0)
4551 return log_error_errno(errno
, "Failed to execute shell '%s': %m", shell
);
4554 static int run(int argc
, char *argv
[]) {
4555 static const Verb verbs
[] = {
4556 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
4557 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_homes
},
4558 { "activate", 2, VERB_ANY
, 0, activate_home
},
4559 { "deactivate", 2, VERB_ANY
, 0, deactivate_home
},
4560 { "inspect", VERB_ANY
, VERB_ANY
, 0, inspect_home
},
4561 { "authenticate", VERB_ANY
, VERB_ANY
, 0, authenticate_home
},
4562 { "create", VERB_ANY
, 2, 0, create_home
},
4563 { "remove", 2, VERB_ANY
, 0, remove_home
},
4564 { "update", VERB_ANY
, 2, 0, update_home
},
4565 { "passwd", VERB_ANY
, 2, 0, passwd_home
},
4566 { "resize", 2, 3, 0, resize_home
},
4567 { "lock", 2, VERB_ANY
, 0, lock_home
},
4568 { "unlock", 2, VERB_ANY
, 0, unlock_home
},
4569 { "with", 2, VERB_ANY
, 0, with_home
},
4570 { "lock-all", VERB_ANY
, 1, 0, lock_all_homes
},
4571 { "deactivate-all", VERB_ANY
, 1, 0, deactivate_all_homes
},
4572 { "rebalance", VERB_ANY
, 1, 0, rebalance
},
4573 { "firstboot", VERB_ANY
, 1, 0, verb_firstboot
},
4581 r
= redirect_bus_mgr();
4585 if (is_fallback_shell(argv
[0]))
4586 return fallback_shell(argc
, argv
);
4588 r
= parse_argv(argc
, argv
);
4592 return dispatch_verb(argc
, argv
, verbs
, NULL
);
4595 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);