1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "ask-password-api.h"
11 #include "bus-common-errors.h"
12 #include "bus-error.h"
13 #include "bus-locator.h"
15 #include "capability-list.h"
16 #include "capability-util.h"
17 #include "cgroup-util.h"
18 #include "creds-util.h"
19 #include "dirent-util.h"
20 #include "dns-domain.h"
22 #include "errno-util.h"
23 #include "extract-word.h"
26 #include "format-table.h"
27 #include "format-util.h"
29 #include "glyph-util.h"
31 #include "hexdecoct.h"
32 #include "home-util.h"
33 #include "homectl-fido2.h"
34 #include "homectl-pkcs11.h"
35 #include "homectl-recovery-key.h"
36 #include "json-util.h"
37 #include "libfido2-util.h"
38 #include "locale-util.h"
39 #include "main-func.h"
40 #include "openssl-util.h"
42 #include "parse-argument.h"
43 #include "parse-util.h"
44 #include "password-quality-util.h"
45 #include "path-util.h"
46 #include "percent-util.h"
47 #include "pkcs11-util.h"
48 #include "polkit-agent.h"
49 #include "pretty-print.h"
50 #include "proc-cmdline.h"
51 #include "process-util.h"
52 #include "recurse-dir.h"
53 #include "rlimit-util.h"
54 #include "runtime-scope.h"
55 #include "stat-util.h"
56 #include "string-util.h"
58 #include "terminal-util.h"
59 #include "time-util.h"
60 #include "uid-classification.h"
61 #include "user-record.h"
62 #include "user-record-password-quality.h"
63 #include "user-record-show.h"
64 #include "user-record-util.h"
65 #include "user-util.h"
69 static PagerFlags arg_pager_flags
= 0;
70 static bool arg_legend
= true;
71 static bool arg_ask_password
= true;
72 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
73 static const char *arg_host
= NULL
;
74 static bool arg_offline
= false;
75 static const char *arg_identity
= NULL
;
76 static sd_json_variant
*arg_identity_extra
= NULL
;
77 static sd_json_variant
*arg_identity_extra_privileged
= NULL
;
78 static sd_json_variant
*arg_identity_extra_this_machine
= NULL
;
79 static sd_json_variant
*arg_identity_extra_other_machines
= NULL
;
80 static sd_json_variant
*arg_identity_extra_rlimits
= NULL
;
81 static char **arg_identity_filter
= NULL
; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
82 static char **arg_identity_filter_rlimits
= NULL
;
83 static uint64_t arg_disk_size
= UINT64_MAX
;
84 static uint64_t arg_disk_size_relative
= UINT64_MAX
;
85 static char **arg_pkcs11_token_uri
= NULL
;
86 static char **arg_fido2_device
= NULL
;
87 static Fido2EnrollFlags arg_fido2_lock_with
= FIDO2ENROLL_PIN
| FIDO2ENROLL_UP
;
89 static int arg_fido2_cred_alg
= COSE_ES256
;
91 static int arg_fido2_cred_alg
= 0;
93 static bool arg_recovery_key
= false;
94 static sd_json_format_flags_t arg_json_format_flags
= SD_JSON_FORMAT_OFF
;
95 static bool arg_and_resize
= false;
96 static bool arg_and_change_password
= false;
98 EXPORT_FORMAT_FULL
, /* export the full record */
99 EXPORT_FORMAT_STRIPPED
, /* strip "state" + "binding", but leave signature in place */
100 EXPORT_FORMAT_MINIMAL
, /* also strip signature */
101 } arg_export_format
= EXPORT_FORMAT_FULL
;
102 static uint64_t arg_capability_bounding_set
= UINT64_MAX
;
103 static uint64_t arg_capability_ambient_set
= UINT64_MAX
;
104 static bool arg_prompt_new_user
= false;
105 static char *arg_blob_dir
= NULL
;
106 static bool arg_blob_clear
= false;
107 static Hashmap
*arg_blob_files
= NULL
;
108 static char *arg_key_name
= NULL
;
109 static bool arg_dry_run
= false;
110 static bool arg_seize
= true;
112 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra
, sd_json_variant_unrefp
);
113 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine
, sd_json_variant_unrefp
);
114 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_other_machines
, sd_json_variant_unrefp
);
115 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged
, sd_json_variant_unrefp
);
116 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits
, sd_json_variant_unrefp
);
117 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter
, strv_freep
);
118 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits
, strv_freep
);
119 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri
, strv_freep
);
120 STATIC_DESTRUCTOR_REGISTER(arg_fido2_device
, strv_freep
);
121 STATIC_DESTRUCTOR_REGISTER(arg_blob_dir
, freep
);
122 STATIC_DESTRUCTOR_REGISTER(arg_blob_files
, hashmap_freep
);
123 STATIC_DESTRUCTOR_REGISTER(arg_key_name
, freep
);
125 static const BusLocator
*bus_mgr
;
127 static bool identity_properties_specified(void) {
130 !sd_json_variant_is_blank_object(arg_identity_extra
) ||
131 !sd_json_variant_is_blank_object(arg_identity_extra_privileged
) ||
132 !sd_json_variant_is_blank_object(arg_identity_extra_this_machine
) ||
133 !sd_json_variant_is_blank_object(arg_identity_extra_other_machines
) ||
134 !sd_json_variant_is_blank_object(arg_identity_extra_rlimits
) ||
135 !strv_isempty(arg_identity_filter
) ||
136 !strv_isempty(arg_identity_filter_rlimits
) ||
137 !strv_isempty(arg_pkcs11_token_uri
) ||
138 !strv_isempty(arg_fido2_device
) ||
141 !hashmap_isempty(arg_blob_files
);
144 static int acquire_bus(sd_bus
**bus
) {
152 r
= bus_connect_transport(arg_transport
, arg_host
, RUNTIME_SCOPE_SYSTEM
, bus
);
154 return bus_log_connect_error(r
, arg_transport
, RUNTIME_SCOPE_SYSTEM
);
156 (void) sd_bus_set_allow_interactive_authorization(*bus
, arg_ask_password
);
161 static int list_homes(int argc
, char *argv
[], void *userdata
) {
162 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
163 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
164 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
165 _cleanup_(table_unrefp
) Table
*table
= NULL
;
168 r
= acquire_bus(&bus
);
172 r
= bus_call_method(bus
, bus_mgr
, "ListHomes", &error
, &reply
, NULL
);
174 return log_error_errno(r
, "Failed to list homes: %s", bus_error_message(&error
, r
));
176 table
= table_new("name", "uid", "gid", "state", "realname", "home", "shell");
180 r
= sd_bus_message_enter_container(reply
, 'a', "(susussso)");
182 return bus_log_parse_error(r
);
185 const char *name
, *state
, *realname
, *home
, *shell
, *color
;
189 r
= sd_bus_message_read(reply
, "(susussso)", &name
, &uid
, &state
, &gid
, &realname
, &home
, &shell
, NULL
);
191 return bus_log_parse_error(r
);
195 r
= table_add_many(table
,
200 return table_log_add_error(r
);
202 r
= table_add_cell(table
, &cell
, TABLE_STRING
, state
);
204 return table_log_add_error(r
);
206 color
= user_record_state_color(state
);
208 (void) table_set_color(table
, cell
, color
);
210 r
= table_add_many(table
,
211 TABLE_STRING
, strna(empty_to_null(realname
)),
213 TABLE_STRING
, strna(empty_to_null(shell
)));
215 return table_log_add_error(r
);
218 r
= sd_bus_message_exit_container(reply
);
220 return bus_log_parse_error(r
);
222 if (!table_isempty(table
) || sd_json_format_enabled(arg_json_format_flags
)) {
223 r
= table_set_sort(table
, (size_t) 0);
225 return table_log_sort_error(r
);
227 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, arg_legend
);
232 if (arg_legend
&& !sd_json_format_enabled(arg_json_format_flags
)) {
233 if (table_isempty(table
))
234 printf("No home areas.\n");
236 printf("\n%zu home areas listed.\n", table_get_rows(table
) - 1);
242 static int acquire_existing_password(
243 const char *user_name
,
245 bool emphasize_current_password
,
246 AskPasswordFlags flags
) {
248 _cleanup_strv_free_erase_
char **password
= NULL
;
249 _cleanup_(erase_and_freep
) char *envpw
= NULL
;
250 _cleanup_free_
char *question
= NULL
;
256 r
= getenv_steal_erase("PASSWORD", &envpw
);
258 return log_error_errno(r
, "Failed to acquire password from environment: %m");
260 /* People really shouldn't use environment variables for passing passwords. We support this
261 * only for testing purposes, and do not document the behaviour, so that people won't
262 * actually use this outside of testing. */
264 r
= user_record_set_password(hr
, STRV_MAKE(envpw
), true);
266 return log_error_errno(r
, "Failed to store password: %m");
271 /* If this is not our own user, then don't use the password cache */
272 if (is_this_me(user_name
) <= 0)
273 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
275 if (asprintf(&question
, emphasize_current_password
?
276 "Please enter current password for user %s:" :
277 "Please enter password for user %s:",
281 AskPasswordRequest req
= {
285 .keyring
= "home-password",
286 .credential
= "home.password",
287 .until
= USEC_INFINITY
,
291 r
= ask_password_auto(&req
, flags
, &password
);
292 if (r
== -EUNATCH
) { /* EUNATCH is returned if no password was found and asking interactively was
293 * disabled via the flags. Not an error for us. */
294 log_debug_errno(r
, "No passwords acquired.");
298 return log_error_errno(r
, "Failed to acquire password: %m");
300 r
= user_record_set_password(hr
, password
, true);
302 return log_error_errno(r
, "Failed to store password: %m");
307 static int acquire_recovery_key(
308 const char *user_name
,
310 AskPasswordFlags flags
) {
312 _cleanup_strv_free_erase_
char **recovery_key
= NULL
;
313 _cleanup_(erase_and_freep
) char *envpw
= NULL
;
314 _cleanup_free_
char *question
= NULL
;
320 r
= getenv_steal_erase("PASSWORD", &envpw
);
322 return log_error_errno(r
, "Failed to acquire password from environment: %m");
324 /* People really shouldn't use environment variables for passing secrets. We support this
325 * only for testing purposes, and do not document the behaviour, so that people won't
326 * actually use this outside of testing. */
328 r
= user_record_set_password(hr
, STRV_MAKE(envpw
), true); /* recovery keys are stored in the record exactly like regular passwords! */
330 return log_error_errno(r
, "Failed to store recovery key: %m");
335 /* If this is not our own user, then don't use the password cache */
336 if (is_this_me(user_name
) <= 0)
337 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
339 if (asprintf(&question
, "Please enter recovery key for user %s:", user_name
) < 0)
342 AskPasswordRequest req
= {
346 .keyring
= "home-recovery-key",
347 .credential
= "home.recovery-key",
348 .until
= USEC_INFINITY
,
352 r
= ask_password_auto(&req
, flags
, &recovery_key
);
353 if (r
== -EUNATCH
) { /* EUNATCH is returned if no recovery key was found and asking interactively was
354 * disabled via the flags. Not an error for us. */
355 log_debug_errno(r
, "No recovery keys acquired.");
359 return log_error_errno(r
, "Failed to acquire recovery keys: %m");
361 r
= user_record_set_password(hr
, recovery_key
, true);
363 return log_error_errno(r
, "Failed to store recovery keys: %m");
368 static int acquire_token_pin(
369 const char *user_name
,
371 AskPasswordFlags flags
) {
373 _cleanup_strv_free_erase_
char **pin
= NULL
;
374 _cleanup_(erase_and_freep
) char *envpin
= NULL
;
375 _cleanup_free_
char *question
= NULL
;
381 r
= getenv_steal_erase("PIN", &envpin
);
383 return log_error_errno(r
, "Failed to acquire PIN from environment: %m");
385 r
= user_record_set_token_pin(hr
, STRV_MAKE(envpin
), false);
387 return log_error_errno(r
, "Failed to store token PIN: %m");
392 /* If this is not our own user, then don't use the password cache */
393 if (is_this_me(user_name
) <= 0)
394 SET_FLAG(flags
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, false);
396 if (asprintf(&question
, "Please enter security token PIN for user %s:", user_name
) < 0)
399 AskPasswordRequest req
= {
403 .keyring
= "token-pin",
404 .credential
= "home.token-pin",
405 .until
= USEC_INFINITY
,
409 r
= ask_password_auto(&req
, flags
, &pin
);
410 if (r
== -EUNATCH
) { /* EUNATCH is returned if no PIN was found and asking interactively was disabled
411 * via the flags. Not an error for us. */
412 log_debug_errno(r
, "No security token PINs acquired.");
416 return log_error_errno(r
, "Failed to acquire security token PIN: %m");
418 r
= user_record_set_token_pin(hr
, pin
, false);
420 return log_error_errno(r
, "Failed to store security token PIN: %m");
425 static int handle_generic_user_record_error(
426 const char *user_name
,
428 const sd_bus_error
*error
,
430 bool emphasize_current_password
) {
436 if (sd_bus_error_has_name(error
, BUS_ERROR_HOME_ABSENT
))
437 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE
),
438 "Home of user %s is currently absent, please plug in the necessary storage device or backing file system.", user_name
);
440 else if (sd_bus_error_has_name(error
, BUS_ERROR_AUTHENTICATION_LIMIT_HIT
))
441 return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS
),
442 "Too frequent login attempts for user %s, try again later.", user_name
);
444 else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD
)) {
446 if (!strv_isempty(hr
->password
))
447 log_notice("Password incorrect or not sufficient, please try again.");
449 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
450 * let's push what we acquire here into the cache */
451 r
= acquire_existing_password(
454 emphasize_current_password
,
455 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
459 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_RECOVERY_KEY
)) {
461 if (!strv_isempty(hr
->password
))
462 log_notice("Recovery key incorrect or not sufficient, please try again.");
464 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
465 * let's push what we acquire here into the cache */
466 r
= acquire_recovery_key(
469 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
473 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
)) {
475 if (strv_isempty(hr
->password
))
476 log_notice("Security token not inserted, please enter password.");
478 log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
480 r
= acquire_existing_password(
483 emphasize_current_password
,
484 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
488 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_NEEDED
)) {
490 /* First time the PIN is requested, let's accept cached data, and allow using credential store */
491 r
= acquire_token_pin(
494 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_PUSH_CACHE
);
498 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED
)) {
500 log_notice("%s%sPlease authenticate physically on security token.",
501 emoji_enabled() ? glyph(GLYPH_TOUCH
) : "",
502 emoji_enabled() ? " " : "");
504 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, true);
506 return log_error_errno(r
, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
508 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED
)) {
510 log_notice("%s%sPlease confirm presence on security token.",
511 emoji_enabled() ? glyph(GLYPH_TOUCH
) : "",
512 emoji_enabled() ? " " : "");
514 r
= user_record_set_fido2_user_presence_permitted(hr
, true);
516 return log_error_errno(r
, "Failed to set FIDO2 user presence permitted flag: %m");
518 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED
)) {
520 log_notice("%s%sPlease verify user on security token.",
521 emoji_enabled() ? glyph(GLYPH_TOUCH
) : "",
522 emoji_enabled() ? " " : "");
524 r
= user_record_set_fido2_user_verification_permitted(hr
, true);
526 return log_error_errno(r
, "Failed to set FIDO2 user verification permitted flag: %m");
528 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_LOCKED
))
529 return log_error_errno(SYNTHETIC_ERRNO(EPERM
), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
531 else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN
)) {
533 log_notice("Security token PIN incorrect, please try again.");
535 /* If the previous PIN was wrong don't accept cached info anymore, but add to cache. Also, don't use the credential data */
536 r
= acquire_token_pin(
539 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
543 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT
)) {
545 log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
547 r
= acquire_token_pin(
550 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
554 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT
)) {
556 log_notice("Security token PIN incorrect, please try again (only one try left!).");
558 r
= acquire_token_pin(
561 ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_NO_CREDENTIAL
);
565 return log_error_errno(ret
, "Operation on home %s failed: %s", user_name
, bus_error_message(error
, ret
));
570 static int acquire_passed_secrets(const char *user_name
, UserRecord
**ret
) {
571 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
576 /* Generates an initial secret objects that contains passwords supplied via $PASSWORD, the password
577 * cache or the credentials subsystem, but excluding any interactive stuff. If nothing is passed,
578 * returns an empty secret object. */
580 secret
= user_record_new();
584 r
= acquire_existing_password(
587 /* emphasize_current_password = */ false,
588 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
592 r
= acquire_token_pin(
595 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
599 r
= acquire_recovery_key(
602 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_NO_TTY
| ASK_PASSWORD_NO_AGENT
);
606 *ret
= TAKE_PTR(secret
);
610 static int activate_home(int argc
, char *argv
[], void *userdata
) {
611 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
614 r
= acquire_bus(&bus
);
618 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
619 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
621 r
= acquire_passed_secrets(*i
, &secret
);
626 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
627 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
629 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ActivateHome");
631 return bus_log_create_error(r
);
633 r
= sd_bus_message_append(m
, "s", *i
);
635 return bus_log_create_error(r
);
637 r
= bus_message_append_secret(m
, secret
);
639 return bus_log_create_error(r
);
641 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
643 r
= handle_generic_user_record_error(*i
, secret
, &error
, r
, /* emphasize_current_password= */ false);
658 static int deactivate_home(int argc
, char *argv
[], void *userdata
) {
659 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
662 r
= acquire_bus(&bus
);
666 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
667 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
668 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
670 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "DeactivateHome");
672 return bus_log_create_error(r
);
674 r
= sd_bus_message_append(m
, "s", *i
);
676 return bus_log_create_error(r
);
678 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
680 log_error_errno(r
, "Failed to deactivate user home: %s", bus_error_message(&error
, r
));
689 static void dump_home_record(UserRecord
*hr
) {
694 if (hr
->incomplete
) {
696 log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr
->user_name
);
699 if (!sd_json_format_enabled(arg_json_format_flags
))
700 user_record_show(hr
, true);
702 _cleanup_(user_record_unrefp
) UserRecord
*stripped
= NULL
;
704 if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
705 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_EMBEDDED
|USER_RECORD_PERMISSIVE
, &stripped
);
706 else if (arg_export_format
== EXPORT_FORMAT_MINIMAL
)
707 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_SIGNABLE
|USER_RECORD_PERMISSIVE
, &stripped
);
711 log_warning_errno(r
, "Failed to strip user record, ignoring: %m");
715 sd_json_variant_dump(hr
->json
, arg_json_format_flags
, stdout
, NULL
);
719 static int inspect_home(sd_bus
*bus
, const char *name
) {
720 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
721 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
722 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
723 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
729 r
= parse_uid(name
, &uid
);
731 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByName", &error
, &reply
, "s", name
);
733 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByUID", &error
, &reply
, "u", (uint32_t) uid
);
735 return log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
737 r
= sd_bus_message_read(reply
, "sbo", &json
, &incomplete
, NULL
);
739 return bus_log_parse_error(r
);
741 r
= sd_json_parse(json
, SD_JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
743 return log_error_errno(r
, "Failed to parse JSON identity: %m");
745 hr
= user_record_new();
749 r
= user_record_load(hr
, v
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_LOG
|USER_RECORD_PERMISSIVE
);
753 hr
->incomplete
= incomplete
;
754 dump_home_record(hr
);
758 static int inspect_homes(int argc
, char *argv
[], void *userdata
) {
759 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
762 r
= acquire_bus(&bus
);
766 pager_open(arg_pager_flags
);
768 char **args
= strv_skip(argv
, 1);
770 STRV_FOREACH(arg
, args
)
771 RET_GATHER(r
, inspect_home(bus
, *arg
));
774 _cleanup_free_
char *myself
= getusername_malloc();
778 return inspect_home(bus
, myself
);
782 static int authenticate_home(sd_bus
*bus
, const char *name
) {
783 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
786 r
= acquire_passed_secrets(name
, &secret
);
791 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
792 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
794 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "AuthenticateHome");
796 return bus_log_create_error(r
);
798 r
= sd_bus_message_append(m
, "s", name
);
800 return bus_log_create_error(r
);
802 r
= bus_message_append_secret(m
, secret
);
804 return bus_log_create_error(r
);
806 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
808 r
= handle_generic_user_record_error(name
, secret
, &error
, r
, false);
816 static int authenticate_homes(int argc
, char *argv
[], void *userdata
) {
817 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
820 r
= acquire_bus(&bus
);
824 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
826 char **args
= strv_skip(argv
, 1);
828 STRV_FOREACH(arg
, args
)
829 RET_GATHER(r
, authenticate_home(bus
, *arg
));
833 _cleanup_free_
char *myself
= getusername_malloc();
837 return authenticate_home(bus
, myself
);
841 static int update_last_change(sd_json_variant
**v
, bool with_password
, bool override
) {
848 n
= now(CLOCK_REALTIME
);
850 c
= sd_json_variant_by_key(*v
, "lastChangeUSec");
855 goto update_password
;
857 if (!sd_json_variant_is_unsigned(c
))
858 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec field is not an unsigned integer, refusing.");
860 u
= sd_json_variant_unsigned(c
);
862 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec is from the future, can't update.");
865 r
= sd_json_variant_set_field_unsigned(v
, "lastChangeUSec", n
);
867 return log_error_errno(r
, "Failed to update lastChangeUSec: %m");
873 c
= sd_json_variant_by_key(*v
, "lastPasswordChangeUSec");
880 if (!sd_json_variant_is_unsigned(c
))
881 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec field is not an unsigned integer, refusing.");
883 u
= sd_json_variant_unsigned(c
);
885 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec is from the future, can't update.");
888 r
= sd_json_variant_set_field_unsigned(v
, "lastPasswordChangeUSec", n
);
890 return log_error_errno(r
, "Failed to update lastPasswordChangeUSec: %m");
895 static int apply_identity_changes(sd_json_variant
**_v
) {
896 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
901 v
= sd_json_variant_ref(*_v
);
903 r
= sd_json_variant_filter(&v
, arg_identity_filter
);
905 return log_error_errno(r
, "Failed to filter identity: %m");
907 r
= sd_json_variant_merge_object(&v
, arg_identity_extra
);
909 return log_error_errno(r
, "Failed to merge identities: %m");
911 if (arg_identity_extra_this_machine
|| arg_identity_extra_other_machines
|| !strv_isempty(arg_identity_filter
)) {
912 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*per_machine
= NULL
, *mmid
= NULL
;
915 r
= sd_id128_get_machine(&mid
);
917 return log_error_errno(r
, "Failed to acquire machine ID: %m");
919 r
= sd_json_variant_new_string(&mmid
, SD_ID128_TO_STRING(mid
));
921 return log_error_errno(r
, "Failed to allocate matchMachineId object: %m");
923 per_machine
= sd_json_variant_ref(sd_json_variant_by_key(v
, "perMachine"));
925 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*npm
= NULL
, *positive
= NULL
, *negative
= NULL
;
926 _cleanup_free_ sd_json_variant
**array
= NULL
;
930 if (!sd_json_variant_is_array(per_machine
))
931 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine field is not an array, refusing.");
933 array
= new(sd_json_variant
*, sd_json_variant_elements(per_machine
) + 2);
937 JSON_VARIANT_ARRAY_FOREACH(z
, per_machine
) {
940 if (!sd_json_variant_is_object(z
))
941 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine entry is not an object, refusing.");
945 u
= sd_json_variant_by_key(z
, "matchMachineId");
946 if (u
&& sd_json_variant_equal(u
, mmid
))
947 r
= sd_json_variant_merge_object(&positive
, z
);
949 u
= sd_json_variant_by_key(z
, "matchNotMachineId");
950 if (!u
|| !sd_json_variant_equal(u
, mmid
))
953 r
= sd_json_variant_merge_object(&negative
, z
);
956 return log_error_errno(r
, "Failed to merge perMachine entry: %m");
961 r
= sd_json_variant_filter(&positive
, arg_identity_filter
);
963 return log_error_errno(r
, "Failed to filter perMachine: %m");
965 r
= sd_json_variant_filter(&negative
, arg_identity_filter
);
967 return log_error_errno(r
, "Failed to filter perMachine: %m");
969 r
= sd_json_variant_merge_object(&positive
, arg_identity_extra_this_machine
);
971 return log_error_errno(r
, "Failed to merge in perMachine fields: %m");
973 r
= sd_json_variant_merge_object(&negative
, arg_identity_extra_other_machines
);
975 return log_error_errno(r
, "Failed to merge in perMachine fields: %m");
977 if (arg_identity_filter_rlimits
|| arg_identity_extra_rlimits
) {
978 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*rlv
= NULL
;
980 rlv
= sd_json_variant_ref(sd_json_variant_by_key(positive
, "resourceLimits"));
982 r
= sd_json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
984 return log_error_errno(r
, "Failed to filter resource limits: %m");
986 r
= sd_json_variant_merge_object(&rlv
, arg_identity_extra_rlimits
);
988 return log_error_errno(r
, "Failed to set resource limits: %m");
990 if (sd_json_variant_is_blank_object(rlv
)) {
991 r
= sd_json_variant_filter(&positive
, STRV_MAKE("resourceLimits"));
993 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
995 r
= sd_json_variant_set_field(&positive
, "resourceLimits", rlv
);
997 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
1001 if (!sd_json_variant_is_blank_object(positive
)) {
1002 r
= sd_json_variant_set_field(&positive
, "matchMachineId", mmid
);
1004 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
1006 array
[i
++] = positive
;
1009 if (!sd_json_variant_is_blank_object(negative
)) {
1010 r
= sd_json_variant_set_field(&negative
, "matchNotMachineId", mmid
);
1012 return log_error_errno(r
, "Failed to set matchNotMachineId field: %m");
1014 array
[i
++] = negative
;
1017 r
= sd_json_variant_new_array(&npm
, array
, i
);
1019 return log_error_errno(r
, "Failed to allocate new perMachine array: %m");
1021 sd_json_variant_unref(per_machine
);
1022 per_machine
= TAKE_PTR(npm
);
1024 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*positive
= sd_json_variant_ref(arg_identity_extra_this_machine
),
1025 *negative
= sd_json_variant_ref(arg_identity_extra_other_machines
);
1027 if (arg_identity_extra_rlimits
) {
1028 r
= sd_json_variant_set_field(&positive
, "resourceLimits", arg_identity_extra_rlimits
);
1030 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
1034 r
= sd_json_variant_set_field(&positive
, "matchMachineId", mmid
);
1036 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
1038 r
= sd_json_variant_append_array(&per_machine
, positive
);
1040 return log_error_errno(r
, "Failed to append to perMachine array: %m");
1044 r
= sd_json_variant_set_field(&negative
, "matchNotMachineId", mmid
);
1046 return log_error_errno(r
, "Failed to set matchNotMachineId field: %m");
1048 r
= sd_json_variant_append_array(&per_machine
, negative
);
1050 return log_error_errno(r
, "Failed to append to perMachine array: %m");
1054 r
= sd_json_variant_set_field(&v
, "perMachine", per_machine
);
1056 return log_error_errno(r
, "Failed to update per machine record: %m");
1059 if (arg_identity_extra_privileged
|| arg_identity_filter
) {
1060 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*privileged
= NULL
;
1062 privileged
= sd_json_variant_ref(sd_json_variant_by_key(v
, "privileged"));
1064 r
= sd_json_variant_filter(&privileged
, arg_identity_filter
);
1066 return log_error_errno(r
, "Failed to filter identity (privileged part): %m");
1068 r
= sd_json_variant_merge_object(&privileged
, arg_identity_extra_privileged
);
1070 return log_error_errno(r
, "Failed to merge identities (privileged part): %m");
1072 if (sd_json_variant_is_blank_object(privileged
)) {
1073 r
= sd_json_variant_filter(&v
, STRV_MAKE("privileged"));
1075 return log_error_errno(r
, "Failed to drop privileged part from identity: %m");
1077 r
= sd_json_variant_set_field(&v
, "privileged", privileged
);
1079 return log_error_errno(r
, "Failed to update privileged part of identity: %m");
1083 if (arg_identity_filter_rlimits
) {
1084 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*rlv
= NULL
;
1086 rlv
= sd_json_variant_ref(sd_json_variant_by_key(v
, "resourceLimits"));
1088 r
= sd_json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
1090 return log_error_errno(r
, "Failed to filter resource limits: %m");
1092 /* Note that we only filter resource limits here, but don't apply them. We do that in the perMachine section */
1094 if (sd_json_variant_is_blank_object(rlv
)) {
1095 r
= sd_json_variant_filter(&v
, STRV_MAKE("resourceLimits"));
1097 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
1099 r
= sd_json_variant_set_field(&v
, "resourceLimits", rlv
);
1101 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
1105 sd_json_variant_unref(*_v
);
1111 static int add_disposition(sd_json_variant
**v
) {
1116 if (sd_json_variant_by_key(*v
, "disposition"))
1119 /* Set the disposition to regular, if not configured explicitly */
1120 r
= sd_json_variant_set_field_string(v
, "disposition", "regular");
1122 return log_error_errno(r
, "Failed to set disposition field: %m");
1127 static int acquire_new_home_record(sd_json_variant
*input
, UserRecord
**ret
) {
1128 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
1129 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1135 unsigned line
= 0, column
= 0;
1138 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Two identity records specified, refusing.");
1140 r
= sd_json_parse_file(
1141 streq(arg_identity
, "-") ? stdin
: NULL
,
1142 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, SD_JSON_PARSE_SENSITIVE
, &v
, &line
, &column
);
1144 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
1146 v
= sd_json_variant_ref(input
);
1148 r
= apply_identity_changes(&v
);
1152 r
= add_disposition(&v
);
1156 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
1157 r
= identity_add_pkcs11_key_data(&v
, *i
);
1162 STRV_FOREACH(i
, arg_fido2_device
) {
1163 r
= identity_add_fido2_parameters(&v
, *i
, arg_fido2_lock_with
, arg_fido2_cred_alg
);
1168 if (arg_recovery_key
) {
1169 r
= identity_add_recovery_key(&v
);
1174 r
= update_last_change(&v
, true, false);
1179 sd_json_variant_dump(v
, SD_JSON_FORMAT_PRETTY
, NULL
, NULL
);
1181 hr
= user_record_new();
1185 r
= user_record_load(
1188 USER_RECORD_REQUIRE_REGULAR
|
1189 USER_RECORD_ALLOW_SECRET
|
1190 USER_RECORD_ALLOW_PRIVILEGED
|
1191 USER_RECORD_ALLOW_PER_MACHINE
|
1192 USER_RECORD_STRIP_BINDING
|
1193 USER_RECORD_STRIP_STATUS
|
1194 (arg_seize
? USER_RECORD_STRIP_SIGNATURE
: USER_RECORD_ALLOW_SIGNATURE
) |
1196 USER_RECORD_PERMISSIVE
);
1200 *ret
= TAKE_PTR(hr
);
1204 static int acquire_new_password(
1205 const char *user_name
,
1210 _cleanup_(erase_and_freep
) char *envpw
= NULL
;
1217 r
= getenv_steal_erase("NEWPASSWORD", &envpw
);
1219 return log_error_errno(r
, "Failed to acquire password from environment: %m");
1221 /* As above, this is not for use, just for testing */
1223 r
= user_record_set_password(hr
, STRV_MAKE(envpw
), /* prepend = */ true);
1225 return log_error_errno(r
, "Failed to store password: %m");
1228 *ret
= TAKE_PTR(envpw
);
1234 (void) suggest_passwords();
1237 _cleanup_strv_free_erase_
char **first
= NULL
, **second
= NULL
;
1238 _cleanup_free_
char *question
= NULL
;
1241 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY
), "Too many attempts, giving up.");
1243 if (asprintf(&question
, "Please enter new password for user %s:", user_name
) < 0)
1246 AskPasswordRequest req
= {
1248 .message
= question
,
1249 .icon
= "user-home",
1250 .keyring
= "home-password",
1251 .credential
= "home.new-password",
1252 .until
= USEC_INFINITY
,
1256 r
= ask_password_auto(
1258 /* flags= */ 0, /* no caching, we want to collect a new password here after all */
1261 return log_error_errno(r
, "Failed to acquire password: %m");
1263 assert(!strv_isempty(first
));
1265 question
= mfree(question
);
1266 if (asprintf(&question
, "Please enter new password for user %s (repeat):", user_name
) < 0)
1269 req
.message
= question
;
1271 r
= ask_password_auto(
1273 /* flags= */ 0, /* no caching */
1276 return log_error_errno(r
, "Failed to acquire password: %m");
1278 if (strv_equal(first
, second
)) {
1279 _cleanup_(erase_and_freep
) char *copy
= NULL
;
1282 copy
= strdup(first
[0]);
1287 r
= user_record_set_password(hr
, first
, /* prepend = */ true);
1289 return log_error_errno(r
, "Failed to store password: %m");
1292 *ret
= TAKE_PTR(copy
);
1297 log_error("Password didn't match, try again.");
1301 static int acquire_merged_blob_dir(UserRecord
*hr
, bool existing
, Hashmap
**ret
) {
1302 _cleanup_free_
char *sys_blob_path
= NULL
;
1303 _cleanup_hashmap_free_ Hashmap
*blobs
= NULL
;
1304 _cleanup_closedir_
DIR *d
= NULL
;
1305 const char *src_blob_path
, *filename
;
1311 HASHMAP_FOREACH_KEY(fd_ptr
, filename
, arg_blob_files
) {
1312 _cleanup_free_
char *filename_dup
= NULL
;
1313 _cleanup_close_
int fd_dup
= -EBADF
;
1315 filename_dup
= strdup(filename
);
1319 if (PTR_TO_FD(fd_ptr
) != -EBADF
) {
1320 fd_dup
= fcntl(PTR_TO_FD(fd_ptr
), F_DUPFD_CLOEXEC
, 3);
1322 return log_error_errno(errno
, "Failed to duplicate fd of %s: %m", filename
);
1325 r
= hashmap_ensure_put(&blobs
, &blob_fd_hash_ops
, filename_dup
, FD_TO_PTR(fd_dup
));
1328 TAKE_PTR(filename_dup
); /* Ownership transferred to hashmap */
1333 src_blob_path
= arg_blob_dir
;
1334 else if (existing
&& !arg_blob_clear
) {
1335 if (hr
->blob_directory
)
1336 src_blob_path
= hr
->blob_directory
;
1338 /* This isn't technically a correct thing to do for generic user records,
1339 * so anyone looking at this code for reference shouldn't replicate it.
1340 * However, since homectl is tied to homed, this is OK. This adds robustness
1341 * for situations where the user record is coming directly from the CLI and
1342 * thus doesn't have a blobDirectory set */
1344 sys_blob_path
= path_join(home_system_blob_dir(), hr
->user_name
);
1348 src_blob_path
= sys_blob_path
;
1351 goto nodir
; /* Shortcut: no dir to merge with, so just return copy of arg_blob_files */
1353 d
= opendir(src_blob_path
);
1355 return log_error_errno(errno
, "Failed to open %s: %m", src_blob_path
);
1357 FOREACH_DIRENT_ALL(de
, d
, return log_error_errno(errno
, "Failed to read %s: %m", src_blob_path
)) {
1358 _cleanup_free_
char *name
= NULL
;
1359 _cleanup_close_
int fd
= -EBADF
;
1361 if (dot_or_dot_dot(de
->d_name
))
1364 if (hashmap_contains(blobs
, de
->d_name
))
1365 continue; /* arg_blob_files should override the base dir */
1367 if (!suitable_blob_filename(de
->d_name
)) {
1368 log_warning("File %s in blob directory %s has an invalid filename. Skipping.", de
->d_name
, src_blob_path
);
1372 name
= strdup(de
->d_name
);
1376 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1378 return log_error_errno(errno
, "Failed to open %s in %s: %m", de
->d_name
, src_blob_path
);
1380 r
= fd_verify_regular(fd
);
1382 log_warning_errno(r
, "Entry %s in blob directory %s is not a regular file. Skipping.", de
->d_name
, src_blob_path
);
1386 r
= hashmap_ensure_put(&blobs
, &blob_fd_hash_ops
, name
, FD_TO_PTR(fd
));
1389 TAKE_PTR(name
); /* Ownership transferred to hashmap */
1394 *ret
= TAKE_PTR(blobs
);
1398 static int bus_message_append_blobs(sd_bus_message
*m
, Hashmap
*blobs
) {
1399 const char *filename
;
1405 r
= sd_bus_message_open_container(m
, 'a', "{sh}");
1409 HASHMAP_FOREACH_KEY(fd_ptr
, filename
, blobs
) {
1410 int fd
= PTR_TO_FD(fd_ptr
);
1412 if (fd
== -EBADF
) /* File marked for deletion */
1415 r
= sd_bus_message_append(m
, "{sh}", filename
, fd
);
1420 return sd_bus_message_close_container(m
);
1423 static int create_home_common(sd_json_variant
*input
, bool show_enforce_password_policy_hint
) {
1424 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1425 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1426 _cleanup_hashmap_free_ Hashmap
*blobs
= NULL
;
1429 r
= acquire_new_home_record(input
, &hr
);
1433 r
= acquire_merged_blob_dir(hr
, false, &blobs
);
1437 /* If the JSON record carries no plain text password (besides the recovery key), then let's query it
1439 if (strv_length(hr
->password
) <= arg_recovery_key
) {
1441 if (strv_isempty(hr
->hashed_password
)) {
1442 _cleanup_(erase_and_freep
) char *new_password
= NULL
;
1444 /* No regular (i.e. non-PKCS#11) hashed passwords set in the record, let's fix that. */
1445 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ true, &new_password
);
1449 r
= user_record_make_hashed_password(hr
, STRV_MAKE(new_password
), /* extend = */ false);
1451 return log_error_errno(r
, "Failed to hash password: %m");
1453 /* There's a hash password set in the record, acquire the unhashed version of it. */
1454 r
= acquire_existing_password(
1457 /* emphasize_current_password= */ false,
1458 ASK_PASSWORD_ACCEPT_CACHED
| ASK_PASSWORD_PUSH_CACHE
);
1464 if (hr
->enforce_password_policy
== 0) {
1465 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1467 /* If password quality enforcement is disabled, let's at least warn client side */
1469 r
= user_record_check_password_quality(hr
, hr
, &error
);
1471 log_warning_errno(r
, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error
, r
));
1475 sd_json_variant_dump(hr
->json
, SD_JSON_FORMAT_COLOR_AUTO
|SD_JSON_FORMAT_PRETTY_AUTO
|SD_JSON_FORMAT_NEWLINE
, stderr
, /* prefix= */ NULL
);
1479 r
= acquire_bus(&bus
);
1483 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1486 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1487 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1488 _cleanup_(erase_and_freep
) char *formatted
= NULL
;
1490 r
= sd_json_variant_format(hr
->json
, 0, &formatted
);
1492 return log_error_errno(r
, "Failed to format user record: %m");
1494 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "CreateHomeEx");
1496 return bus_log_create_error(r
);
1498 (void) sd_bus_message_sensitive(m
);
1500 r
= sd_bus_message_append(m
, "s", formatted
);
1502 return bus_log_create_error(r
);
1504 r
= bus_message_append_blobs(m
, blobs
);
1506 return bus_log_create_error(r
);
1508 r
= sd_bus_message_append(m
, "t", UINT64_C(0));
1510 return bus_log_create_error(r
);
1512 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1514 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
1515 _cleanup_(erase_and_freep
) char *new_password
= NULL
;
1517 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
1518 if (show_enforce_password_policy_hint
)
1519 log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
1521 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ false, &new_password
);
1525 r
= user_record_make_hashed_password(hr
, STRV_MAKE(new_password
), /* extend = */ false);
1527 return log_error_errno(r
, "Failed to hash passwords: %m");
1529 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1540 static int create_home(int argc
, char *argv
[], void *userdata
) {
1544 /* If a username was specified, use it */
1546 if (valid_user_group_name(argv
[1], 0))
1547 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "userName", argv
[1]);
1549 _cleanup_free_
char *un
= NULL
, *rr
= NULL
;
1551 /* Before we consider the user name invalid, let's check if we can split it? */
1552 r
= split_user_name_realm(argv
[1], &un
, &rr
);
1554 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name '%s' is not valid.", argv
[1]);
1557 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "realm", rr
);
1559 return log_error_errno(r
, "Failed to set realm field: %m");
1562 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "userName", un
);
1565 return log_error_errno(r
, "Failed to set userName field: %m");
1567 /* If neither a username nor an identity have been specified we cannot operate. */
1569 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name required.");
1572 return create_home_common(/* input= */ NULL
, /* show_enforce_password_policy_hint= */ true);
1575 static int verb_adopt_home(int argc
, char *argv
[], void *userdata
) {
1578 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1579 r
= acquire_bus(&bus
);
1583 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1585 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1586 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1587 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "AdoptHome");
1589 return bus_log_create_error(r
);
1591 r
= sd_bus_message_append(m
, "st", *i
, UINT64_C(0));
1593 return bus_log_create_error(r
);
1595 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1596 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1598 log_error_errno(r
, "Failed to adopt home: %s", bus_error_message(&error
, r
));
1607 static int register_home_common(sd_bus
*bus
, sd_json_variant
*v
) {
1608 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*_bus
= NULL
;
1614 r
= acquire_bus(&_bus
);
1620 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1621 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "RegisterHome");
1623 return bus_log_create_error(r
);
1625 _cleanup_free_
char *formatted
= NULL
;
1626 r
= sd_json_variant_format(v
, /* flags= */ 0, &formatted
);
1628 return log_error_errno(r
, "Failed to format JSON record: %m");
1630 r
= sd_bus_message_append(m
, "s", formatted
);
1632 return bus_log_create_error(r
);
1634 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1635 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1637 return log_error_errno(r
, "Failed to register home: %s", bus_error_message(&error
, r
));
1642 static int register_home_one(sd_bus
*bus
, FILE *f
, const char *path
) {
1648 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
1649 unsigned line
= 0, column
= 0;
1650 r
= sd_json_parse_file(f
, path
, SD_JSON_PARSE_SENSITIVE
, &v
, &line
, &column
);
1652 return log_error_errno(r
, "[%s:%u:%u] Failed to parse user record: %m", path
, line
, column
);
1654 return register_home_common(bus
, v
);
1657 static int verb_register_home(int argc
, char *argv
[], void *userdata
) {
1660 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1661 r
= acquire_bus(&bus
);
1665 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1669 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Not accepting an arguments if --identity= is specified, refusing.");
1671 return register_home_one(bus
, /* f= */ NULL
, arg_identity
);
1674 if (argc
== 1 || (argc
== 2 && streq(argv
[1], "-")))
1675 return register_home_one(bus
, /* f= */ stdin
, "<stdio>");
1678 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1680 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Refusing reading from standard input if multiple user records are specified.");
1682 RET_GATHER(r
, register_home_one(bus
, /* f= */ NULL
, *i
));
1688 static int verb_unregister_home(int argc
, char *argv
[], void *userdata
) {
1691 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1692 r
= acquire_bus(&bus
);
1696 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1699 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1700 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1701 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "UnregisterHome");
1703 return bus_log_create_error(r
);
1705 r
= sd_bus_message_append(m
, "s", *i
);
1707 return bus_log_create_error(r
);
1709 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1710 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, /* ret_reply= */ NULL
);
1712 RET_GATHER(ret
, log_error_errno(r
, "Failed to unregister home: %s", bus_error_message(&error
, r
)));
1718 static int remove_home(int argc
, char *argv
[], void *userdata
) {
1719 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1722 r
= acquire_bus(&bus
);
1726 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1728 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1729 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1730 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1732 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "RemoveHome");
1734 return bus_log_create_error(r
);
1736 r
= sd_bus_message_append(m
, "s", *i
);
1738 return bus_log_create_error(r
);
1740 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1742 log_error_errno(r
, "Failed to remove home: %s", bus_error_message(&error
, r
));
1751 static int acquire_updated_home_record(
1753 const char *username
,
1756 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*json
= NULL
;
1757 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1763 unsigned line
= 0, column
= 0;
1764 sd_json_variant
*un
;
1766 r
= sd_json_parse_file(
1767 streq(arg_identity
, "-") ? stdin
: NULL
,
1768 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, SD_JSON_PARSE_SENSITIVE
, &json
, &line
, &column
);
1770 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
1772 un
= sd_json_variant_by_key(json
, "userName");
1774 if (!sd_json_variant_is_string(un
) || (username
&& !streq(sd_json_variant_string(un
), username
)))
1775 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name specified on command line and in JSON record do not match.");
1778 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No username specified.");
1780 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "userName", username
);
1782 return log_error_errno(r
, "Failed to set userName field: %m");
1786 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1787 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1791 if (!identity_properties_specified())
1792 return log_error_errno(SYNTHETIC_ERRNO(EALREADY
), "No field to change specified.");
1794 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByName", &error
, &reply
, "s", username
);
1796 return log_error_errno(r
, "Failed to acquire user home record: %s", bus_error_message(&error
, r
));
1798 r
= sd_bus_message_read(reply
, "sbo", &text
, &incomplete
, NULL
);
1800 return bus_log_parse_error(r
);
1803 return log_error_errno(SYNTHETIC_ERRNO(EACCES
), "Lacking rights to acquire user record including privileged metadata, can't update record.");
1805 r
= sd_json_parse(text
, SD_JSON_PARSE_SENSITIVE
, &json
, NULL
, NULL
);
1807 return log_error_errno(r
, "Failed to parse JSON identity: %m");
1809 reply
= sd_bus_message_unref(reply
);
1811 r
= sd_json_variant_filter(&json
, STRV_MAKE("binding", "status", "signature", "blobManifest"));
1813 return log_error_errno(r
, "Failed to strip binding and status from record to update: %m");
1816 r
= apply_identity_changes(&json
);
1820 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
1821 r
= identity_add_pkcs11_key_data(&json
, *i
);
1826 STRV_FOREACH(i
, arg_fido2_device
) {
1827 r
= identity_add_fido2_parameters(&json
, *i
, arg_fido2_lock_with
, arg_fido2_cred_alg
);
1832 if (arg_recovery_key
) {
1833 r
= identity_add_recovery_key(&json
);
1838 /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
1840 r
= update_last_change(&json
, arg_pkcs11_token_uri
|| arg_fido2_device
|| arg_recovery_key
, !arg_identity
);
1845 sd_json_variant_dump(json
, SD_JSON_FORMAT_PRETTY
, NULL
, NULL
);
1847 hr
= user_record_new();
1851 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
);
1855 *ret
= TAKE_PTR(hr
);
1859 static int home_record_reset_human_interaction_permission(UserRecord
*hr
) {
1864 /* When we execute multiple operations one after the other, let's reset the permission to ask the
1865 * user each time, so that if interaction is necessary we will be told so again and thus can print a
1866 * nice message to the user, telling the user so. */
1868 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, -1);
1870 return log_error_errno(r
, "Failed to reset PKCS#11 protected authentication path permission flag: %m");
1872 r
= user_record_set_fido2_user_presence_permitted(hr
, -1);
1874 return log_error_errno(r
, "Failed to reset FIDO2 user presence permission flag: %m");
1876 r
= user_record_set_fido2_user_verification_permitted(hr
, -1);
1878 return log_error_errno(r
, "Failed to reset FIDO2 user verification permission flag: %m");
1883 static int update_home(int argc
, char *argv
[], void *userdata
) {
1884 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1885 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
, *secret
= NULL
;
1886 _cleanup_free_
char *buffer
= NULL
;
1887 _cleanup_hashmap_free_ Hashmap
*blobs
= NULL
;
1888 const char *username
;
1894 else if (!arg_identity
) {
1895 buffer
= getusername_malloc();
1903 r
= acquire_bus(&bus
);
1907 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1909 r
= acquire_updated_home_record(bus
, username
, &hr
);
1913 /* Add in all secrets we can acquire cheaply */
1914 r
= acquire_passed_secrets(username
, &secret
);
1918 r
= user_record_merge_secret(hr
, secret
);
1922 r
= acquire_merged_blob_dir(hr
, true, &blobs
);
1927 sd_json_variant_dump(hr
->json
, SD_JSON_FORMAT_COLOR_AUTO
|SD_JSON_FORMAT_PRETTY_AUTO
|SD_JSON_FORMAT_NEWLINE
, stderr
, /* prefix= */ NULL
);
1931 /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
1932 * authentication might be confusing. */
1934 if (arg_and_resize
|| arg_and_change_password
)
1935 log_info("Updating home directory.");
1938 flags
|= SD_HOMED_UPDATE_OFFLINE
;
1941 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1942 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1943 _cleanup_free_
char *formatted
= NULL
;
1945 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "UpdateHomeEx");
1947 return bus_log_create_error(r
);
1949 r
= sd_json_variant_format(hr
->json
, 0, &formatted
);
1951 return log_error_errno(r
, "Failed to format user record: %m");
1953 (void) sd_bus_message_sensitive(m
);
1955 r
= sd_bus_message_append(m
, "s", formatted
);
1957 return bus_log_create_error(r
);
1959 r
= bus_message_append_blobs(m
, blobs
);
1961 return bus_log_create_error(r
);
1963 r
= sd_bus_message_append(m
, "t", flags
);
1965 return bus_log_create_error(r
);
1967 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1969 if (arg_and_change_password
&&
1970 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1971 /* In the generic handler we'd ask for a password in this case, but when
1972 * changing passwords that's not sufficient, as we need to acquire all keys
1974 return log_error_errno(r
, "Security token not inserted, refusing.");
1976 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1984 log_info("Resizing home.");
1986 (void) home_record_reset_human_interaction_permission(hr
);
1988 /* Also sync down disk size to underlying LUKS/fscrypt/quota */
1989 while (arg_and_resize
) {
1990 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1991 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1993 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ResizeHome");
1995 return bus_log_create_error(r
);
1997 /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
1998 r
= sd_bus_message_append(m
, "st", hr
->user_name
, UINT64_MAX
);
2000 return bus_log_create_error(r
);
2002 r
= bus_message_append_secret(m
, hr
);
2004 return bus_log_create_error(r
);
2006 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2008 if (arg_and_change_password
&&
2009 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
2010 return log_error_errno(r
, "Security token not inserted, refusing.");
2012 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
2019 if (arg_and_change_password
)
2020 log_info("Synchronizing passwords and encryption keys.");
2022 (void) home_record_reset_human_interaction_permission(hr
);
2024 /* Also sync down passwords to underlying LUKS/fscrypt */
2025 while (arg_and_change_password
) {
2026 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2027 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2029 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ChangePasswordHome");
2031 return bus_log_create_error(r
);
2033 /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
2034 r
= sd_bus_message_append(m
, "ss", hr
->user_name
, "{}");
2036 return bus_log_create_error(r
);
2038 r
= bus_message_append_secret(m
, hr
);
2040 return bus_log_create_error(r
);
2042 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2044 if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
2045 return log_error_errno(r
, "Security token not inserted, refusing.");
2047 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
2057 static int passwd_home(int argc
, char *argv
[], void *userdata
) {
2058 _cleanup_(user_record_unrefp
) UserRecord
*old_secret
= NULL
, *new_secret
= NULL
;
2059 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2060 _cleanup_free_
char *buffer
= NULL
;
2061 const char *username
;
2064 if (arg_pkcs11_token_uri
)
2065 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2066 "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=%s'.",
2067 glyph(GLYPH_ELLIPSIS
));
2068 if (arg_fido2_device
)
2069 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2070 "To change the FIDO2 security token use 'homectl update --fido2-device=%s'.",
2071 glyph(GLYPH_ELLIPSIS
));
2072 if (identity_properties_specified())
2073 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "The 'passwd' verb does not permit changing other record properties at the same time.");
2078 buffer
= getusername_malloc();
2085 r
= acquire_bus(&bus
);
2089 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2091 r
= acquire_passed_secrets(username
, &old_secret
);
2095 new_secret
= user_record_new();
2099 r
= acquire_new_password(username
, new_secret
, /* suggest = */ true, NULL
);
2104 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2105 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2107 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ChangePasswordHome");
2109 return bus_log_create_error(r
);
2111 r
= sd_bus_message_append(m
, "s", username
);
2113 return bus_log_create_error(r
);
2115 r
= bus_message_append_secret(m
, new_secret
);
2117 return bus_log_create_error(r
);
2119 r
= bus_message_append_secret(m
, old_secret
);
2121 return bus_log_create_error(r
);
2123 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2125 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
2127 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
2129 r
= acquire_new_password(username
, new_secret
, /* suggest = */ false, NULL
);
2131 } else if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
2133 /* In the generic handler we'd ask for a password in this case, but when
2134 * changing passwords that's not sufficeint, as we need to acquire all keys
2136 return log_error_errno(r
, "Security token not inserted, refusing.");
2138 r
= handle_generic_user_record_error(username
, old_secret
, &error
, r
, true);
2148 static int parse_disk_size(const char *t
, uint64_t *ret
) {
2154 if (streq(t
, "min"))
2156 else if (streq(t
, "max"))
2157 *ret
= UINT64_MAX
-1; /* Largest size that isn't UINT64_MAX special marker */
2161 r
= parse_size(t
, 1024, &ds
);
2163 return log_error_errno(r
, "Failed to parse disk size parameter: %s", t
);
2165 if (ds
>= UINT64_MAX
) /* UINT64_MAX has special meaning for us ("dont change"), refuse */
2166 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
), "Disk size out of range: %s", t
);
2174 static int resize_home(int argc
, char *argv
[], void *userdata
) {
2175 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2176 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
2177 uint64_t ds
= UINT64_MAX
;
2180 r
= acquire_bus(&bus
);
2184 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2186 if (arg_disk_size_relative
!= UINT64_MAX
||
2187 (argc
> 2 && parse_permyriad(argv
[2]) >= 0))
2188 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
2189 "Relative disk size specification currently not supported when resizing.");
2192 r
= parse_disk_size(argv
[2], &ds
);
2197 if (arg_disk_size
!= UINT64_MAX
) {
2198 if (ds
!= UINT64_MAX
&& ds
!= arg_disk_size
)
2199 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Disk size specified twice and doesn't match, refusing.");
2204 r
= acquire_passed_secrets(argv
[1], &secret
);
2209 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2210 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2212 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ResizeHome");
2214 return bus_log_create_error(r
);
2216 r
= sd_bus_message_append(m
, "st", argv
[1], ds
);
2218 return bus_log_create_error(r
);
2220 r
= bus_message_append_secret(m
, secret
);
2222 return bus_log_create_error(r
);
2224 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2226 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2236 static int lock_home(int argc
, char *argv
[], void *userdata
) {
2237 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2240 r
= acquire_bus(&bus
);
2244 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
2245 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2246 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2248 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "LockHome");
2250 return bus_log_create_error(r
);
2252 r
= sd_bus_message_append(m
, "s", *i
);
2254 return bus_log_create_error(r
);
2256 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2258 log_error_errno(r
, "Failed to lock home: %s", bus_error_message(&error
, r
));
2267 static int unlock_home(int argc
, char *argv
[], void *userdata
) {
2268 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2271 r
= acquire_bus(&bus
);
2275 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
2276 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
2278 r
= acquire_passed_secrets(*i
, &secret
);
2283 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2284 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2286 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "UnlockHome");
2288 return bus_log_create_error(r
);
2290 r
= sd_bus_message_append(m
, "s", *i
);
2292 return bus_log_create_error(r
);
2294 r
= bus_message_append_secret(m
, secret
);
2296 return bus_log_create_error(r
);
2298 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2300 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2315 static int with_home(int argc
, char *argv
[], void *userdata
) {
2316 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2317 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
2318 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2319 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
2320 _cleanup_close_
int acquired_fd
= -EBADF
;
2321 _cleanup_strv_free_
char **cmdline
= NULL
;
2326 r
= acquire_bus(&bus
);
2331 _cleanup_free_
char *shell
= NULL
;
2333 /* If no command is specified, spawn a shell */
2334 r
= get_shell(&shell
);
2336 return log_error_errno(r
, "Failed to acquire shell: %m");
2338 cmdline
= strv_new(shell
);
2340 cmdline
= strv_copy(argv
+ 2);
2344 r
= acquire_passed_secrets(argv
[1], &secret
);
2349 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "AcquireHome");
2351 return bus_log_create_error(r
);
2353 r
= sd_bus_message_append(m
, "s", argv
[1]);
2355 return bus_log_create_error(r
);
2357 r
= bus_message_append_secret(m
, secret
);
2359 return bus_log_create_error(r
);
2361 r
= sd_bus_message_append(m
, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
2363 return bus_log_create_error(r
);
2365 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
2366 m
= sd_bus_message_unref(m
);
2368 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2372 sd_bus_error_free(&error
);
2376 r
= sd_bus_message_read(reply
, "h", &fd
);
2378 return bus_log_parse_error(r
);
2380 acquired_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
2381 if (acquired_fd
< 0)
2382 return log_error_errno(errno
, "Failed to duplicate acquired fd: %m");
2384 reply
= sd_bus_message_unref(reply
);
2389 r
= bus_call_method(bus
, bus_mgr
, "GetHomeByName", &error
, &reply
, "s", argv
[1]);
2391 return log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
2393 r
= sd_bus_message_read(reply
, "usussso", NULL
, NULL
, NULL
, NULL
, &home
, NULL
, NULL
);
2395 return bus_log_parse_error(r
);
2397 r
= safe_fork("(with)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG_SIGTERM
|FORK_LOG
|FORK_RLIMIT_NOFILE_SAFE
|FORK_REOPEN_LOG
, &pid
);
2401 if (chdir(home
) < 0) {
2402 log_error_errno(errno
, "Failed to change to directory %s: %m", home
);
2406 execvp(cmdline
[0], cmdline
);
2407 log_error_errno(errno
, "Failed to execute %s: %m", cmdline
[0]);
2411 ret
= wait_for_terminate_and_check(cmdline
[0], pid
, WAIT_LOG_ABNORMAL
);
2413 /* Close the fd that pings the home now. */
2414 acquired_fd
= safe_close(acquired_fd
);
2416 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ReleaseHome");
2418 return bus_log_create_error(r
);
2420 r
= sd_bus_message_append(m
, "s", argv
[1]);
2422 return bus_log_create_error(r
);
2424 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2426 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_BUSY
))
2427 log_notice("Not deactivating home directory of %s, as it is still used.", argv
[1]);
2429 return log_error_errno(r
, "Failed to release user home: %s", bus_error_message(&error
, r
));
2435 static int lock_all_homes(int argc
, char *argv
[], void *userdata
) {
2436 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2437 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2438 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2441 r
= acquire_bus(&bus
);
2445 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "LockAllHomes");
2447 return bus_log_create_error(r
);
2449 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2451 return log_error_errno(r
, "Failed to lock all homes: %s", bus_error_message(&error
, r
));
2456 static int deactivate_all_homes(int argc
, char *argv
[], void *userdata
) {
2457 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2458 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2459 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2462 r
= acquire_bus(&bus
);
2466 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "DeactivateAllHomes");
2468 return bus_log_create_error(r
);
2470 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2472 return log_error_errno(r
, "Failed to deactivate all homes: %s", bus_error_message(&error
, r
));
2477 static int rebalance(int argc
, char *argv
[], void *userdata
) {
2478 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2479 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2480 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2483 r
= acquire_bus(&bus
);
2487 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "Rebalance");
2489 return bus_log_create_error(r
);
2491 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2493 if (sd_bus_error_has_name(&error
, BUS_ERROR_REBALANCE_NOT_NEEDED
))
2494 log_info("No homes needed rebalancing.");
2496 return log_error_errno(r
, "Failed to rebalance: %s", bus_error_message(&error
, r
));
2498 log_info("Completed rebalancing.");
2503 static int create_or_register_from_credentials(void) {
2506 _cleanup_close_
int fd
= open_credentials_dir();
2507 if (IN_SET(fd
, -ENXIO
, -ENOENT
)) /* Credential env var not set, or dir doesn't exist. */
2510 return log_error_errno(fd
, "Failed to open credentials directory: %m");
2512 _cleanup_free_ DirectoryEntries
*des
= NULL
;
2513 r
= readdir_all(fd
, RECURSE_DIR_SORT
|RECURSE_DIR_IGNORE_DOT
|RECURSE_DIR_ENSURE_TYPE
, &des
);
2515 return log_error_errno(r
, "Failed to enumerate credentials: %m");
2517 int ret
= 0, n_processed
= 0;
2518 FOREACH_ARRAY(i
, des
->entries
, des
->n_entries
) {
2519 struct dirent
*de
= *i
;
2520 if (de
->d_type
!= DT_REG
)
2528 if ((e
= startswith(de
->d_name
, "home.create.")))
2529 op
= OPERATION_CREATE
;
2530 else if ((e
= startswith(de
->d_name
, "home.register.")))
2531 op
= OPERATION_REGISTER
;
2535 if (!valid_user_group_name(e
, 0)) {
2536 log_notice("Skipping over credential with name that is not a suitable user name: %s", de
->d_name
);
2540 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*identity
= NULL
;
2541 unsigned line
= 0, column
= 0;
2542 r
= sd_json_parse_file_at(
2551 log_warning_errno(r
, "[%s:%u:%u] Failed to parse user record in credential, ignoring: %m", de
->d_name
, line
, column
);
2555 sd_json_variant
*un
= sd_json_variant_by_key(identity
, "userName");
2557 if (!sd_json_variant_is_string(un
)) {
2558 log_warning("User record from credential '%s' contains 'userName' field of invalid type, ignoring.", de
->d_name
);
2562 if (!streq(sd_json_variant_string(un
), e
)) {
2563 log_warning("User record from credential '%s' contains 'userName' field (%s) that doesn't match credential name (%s), ignoring.", de
->d_name
, sd_json_variant_string(un
), e
);
2567 r
= sd_json_variant_set_field_string(&identity
, "userName", e
);
2569 return log_warning_errno(r
, "Failed to set userName field: %m");
2572 log_notice("Processing user '%s' from credentials.", e
);
2574 if (op
== OPERATION_CREATE
)
2575 r
= create_home_common(identity
, /* show_enforce_password_policy_hint= */ false);
2577 r
= register_home_common(/* bus= */ NULL
, identity
);
2584 return ret
< 0 ? ret
: n_processed
;
2587 static int has_regular_user(void) {
2588 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
2589 UserDBMatch match
= USERDB_MATCH_NULL
;
2592 match
.disposition_mask
= INDEX_TO_MASK(uint64_t, USER_REGULAR
);
2594 r
= userdb_all(&match
, USERDB_SUPPRESS_SHADOW
, &iterator
);
2596 return log_error_errno(r
, "Failed to create user enumerator: %m");
2598 r
= userdb_iterator_get(iterator
, &match
, /* ret= */ NULL
);
2602 return log_error_errno(r
, "Failed to enumerate users: %m");
2607 static int acquire_group_list(char ***ret
) {
2608 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
2609 _cleanup_strv_free_
char **groups
= NULL
;
2610 UserDBMatch match
= USERDB_MATCH_NULL
;
2615 match
.disposition_mask
= INDEXES_TO_MASK(uint64_t, USER_REGULAR
, USER_SYSTEM
);
2617 r
= groupdb_all(&match
, USERDB_SUPPRESS_SHADOW
, &iterator
);
2619 log_debug_errno(r
, "No groups found. (Didn't check via Varlink.)");
2620 else if (r
== -ESRCH
)
2621 log_debug_errno(r
, "No groups found.");
2623 return log_debug_errno(r
, "Failed to enumerate groups, ignoring: %m");
2626 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
2628 r
= groupdb_iterator_get(iterator
, &match
, &gr
);
2632 return log_debug_errno(r
, "Failed to acquire next group: %m");
2634 if (group_record_disposition(gr
) == USER_REGULAR
) {
2635 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
2637 /* Filter groups here that belong to a specific user, and are named like them */
2639 UserDBMatch user_match
= USERDB_MATCH_NULL
;
2640 user_match
.disposition_mask
= INDEX_TO_MASK(uint64_t, USER_REGULAR
);
2642 r
= userdb_by_name(gr
->group_name
, &user_match
, USERDB_SUPPRESS_SHADOW
, &ur
);
2643 if (r
< 0 && r
!= -ESRCH
)
2644 return log_debug_errno(r
, "Failed to check if matching user exists for group '%s': %m", gr
->group_name
);
2646 if (r
>= 0 && user_record_gid(ur
) == gr
->gid
)
2650 r
= strv_extend(&groups
, gr
->group_name
);
2657 *ret
= TAKE_PTR(groups
);
2661 static int group_completion_callback(const char *key
, char ***ret_list
, void *userdata
) {
2662 char ***available
= userdata
;
2666 r
= acquire_group_list(available
);
2668 log_debug_errno(r
, "Failed to enumerate available groups, ignoring: %m");
2671 _cleanup_strv_free_
char **l
= strv_copy(*available
);
2675 r
= strv_extend(&l
, "list");
2679 *ret_list
= TAKE_PTR(l
);
2683 static int create_interactively(void) {
2684 _cleanup_free_
char *username
= NULL
;
2687 if (!arg_prompt_new_user
) {
2688 log_debug("Prompting for user creation was not requested.");
2693 if (emoji_enabled()) {
2694 fputs(glyph(GLYPH_HOME
), stdout
);
2697 printf("Please create your user account!\n");
2699 if (!any_key_to_proceed()) {
2700 log_notice("Skipping.");
2704 (void) terminal_reset_defensive_locked(STDOUT_FILENO
, /* flags= */ 0);
2707 username
= mfree(username
);
2709 r
= ask_string(&username
,
2710 "%s Please enter user name to create (empty to skip): ",
2711 glyph(GLYPH_TRIANGULAR_BULLET
));
2713 return log_error_errno(r
, "Failed to query user for username: %m");
2715 if (isempty(username
)) {
2716 log_info("No data entered, skipping.");
2720 if (!valid_user_group_name(username
, /* flags= */ 0)) {
2721 log_notice("Specified user name is not a valid UNIX user name, try again: %s", username
);
2725 r
= userdb_by_name(username
, /* match= */ NULL
, USERDB_SUPPRESS_SHADOW
, /* ret= */ NULL
);
2729 return log_error_errno(r
, "Failed to check if specified user '%s' already exists: %m", username
);
2731 log_notice("Specified user '%s' exists already, try again.", username
);
2734 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "userName", username
);
2736 return log_error_errno(r
, "Failed to set userName field: %m");
2738 /* Let's not insist on a strong password in the firstboot interactive interface. Insisting on this is
2739 * really annoying, as the user cannot just invoke the tool again with "--enforce-password-policy=no"
2740 * because after all the tool is called from the boot process, and not from an interactive
2741 * shell. Moreover, when setting up an initial system we can assume the user owns it, and hence we
2742 * don't need to hard enforce some policy on password strength some organization or OS vendor
2743 * requires. Note that this just disables the *strict* enforcement of the password policy. Even with
2744 * this disabled we'll still tell the user in the UI that the password is too weak and suggest better
2745 * ones, even if we then accept the weak ones if the user insists, by repeating it. */
2746 r
= sd_json_variant_set_field_boolean(&arg_identity_extra
, "enforcePasswordPolicy", false);
2748 return log_error_errno(r
, "Failed to set enforcePasswordPolicy field: %m");
2750 _cleanup_strv_free_
char **available
= NULL
, **groups
= NULL
;
2752 _cleanup_free_
char *s
= NULL
;
2754 strv_sort_uniq(groups
);
2756 if (!strv_isempty(groups
)) {
2757 _cleanup_free_
char *j
= strv_join(groups
, ", ");
2761 log_info("Currently selected groups: %s", j
);
2764 r
= ask_string_full(&s
,
2765 group_completion_callback
, &available
,
2766 "%s Please enter an auxiliary group for user %s (empty to continue, \"list\" to list available groups): ",
2767 glyph(GLYPH_TRIANGULAR_BULLET
), username
);
2769 return log_error_errno(r
, "Failed to query user for auxiliary group: %m");
2774 if (streq(s
, "list")) {
2776 r
= acquire_group_list(&available
);
2778 log_warning_errno(r
, "Failed to enumerate available groups, ignoring: %m");
2780 log_notice("Did not find any available groups");
2785 r
= show_menu(available
,
2787 /* column_width= */ 20,
2788 /* ellipsize_percentage= */ 60,
2789 /* grey_prefix= */ NULL
,
2790 /* with_numbers= */ true);
2792 return log_error_errno(r
, "Failed to show menu: %m");
2798 if (!strv_isempty(available
)) {
2800 r
= safe_atou(s
, &u
);
2802 if (u
<= 0 || u
> strv_length(available
)) {
2803 log_error("Specified entry number out of range.");
2807 log_info("Selected '%s'.", available
[u
-1]);
2809 r
= strv_extend(&groups
, available
[u
-1]);
2817 if (!valid_user_group_name(s
, /* flags= */ 0)) {
2818 log_notice("Specified group name is not a valid UNIX group name, try again: %s", s
);
2822 r
= groupdb_by_name(s
, /* match= */ NULL
, USERDB_SUPPRESS_SHADOW
|USERDB_EXCLUDE_DYNAMIC_USER
, /*ret=*/ NULL
);
2824 log_notice("Specified auxiliary group does not exist, try again: %s", s
);
2828 return log_error_errno(r
, "Failed to check if specified group '%s' already exists: %m", s
);
2830 log_info("Selected '%s'.", s
);
2832 r
= strv_extend(&groups
, s
);
2837 if (!strv_isempty(groups
)) {
2838 strv_sort_uniq(groups
);
2840 r
= sd_json_variant_set_field_strv(&arg_identity_extra
, "memberOf", groups
);
2842 return log_error_errno(r
, "Failed to set memberOf field: %m");
2845 _cleanup_free_
char *shell
= NULL
;
2848 shell
= mfree(shell
);
2850 r
= ask_string(&shell
,
2851 "%s Please enter the shell to use for user %s (empty for default): ",
2852 glyph(GLYPH_TRIANGULAR_BULLET
), username
);
2854 return log_error_errno(r
, "Failed to query user for username: %m");
2856 if (isempty(shell
)) {
2857 log_info("No data entered, leaving at default.");
2861 if (!valid_shell(shell
)) {
2862 log_notice("Specified shell is not a valid UNIX shell path, try again: %s", shell
);
2866 r
= RET_NERRNO(access(shell
, X_OK
));
2871 return log_error_errno(r
, "Failed to check if shell %s exists: %m", shell
);
2873 log_notice("Specified shell '%s' is not installed, try another one.", shell
);
2876 if (!isempty(shell
)) {
2877 log_info("Selected %s as the shell for user %s", shell
, username
);
2879 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "shell", shell
);
2881 return log_error_errno(r
, "Failed to set shell field: %m");
2884 return create_home_common(/* input= */ NULL
, /* show_enforce_password_policy_hint= */ false);
2887 static int add_signing_keys_from_credentials(void);
2889 static int verb_firstboot(int argc
, char *argv
[], void *userdata
) {
2892 /* Let's honour the systemd.firstboot kernel command line option, just like the systemd-firstboot
2896 r
= proc_cmdline_get_bool("systemd.firstboot", /* flags = */ 0, &enabled
);
2898 return log_error_errno(r
, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
2899 if (r
> 0 && !enabled
) {
2900 log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
2901 arg_prompt_new_user
= false;
2906 RET_GATHER(ret
, add_signing_keys_from_credentials());
2908 r
= create_or_register_from_credentials();
2910 bool existing_users
= r
> 0;
2912 r
= getenv_bool("SYSTEMD_HOME_FIRSTBOOT_OVERRIDE");
2917 log_warning_errno(r
, "Failed to parse $SYSTEMD_HOME_FIRSTBOOT_OVERRIDE, ignoring: %m");
2919 if (!existing_users
) {
2920 r
= has_regular_user();
2924 existing_users
= r
> 0;
2926 if (existing_users
) {
2927 log_info("Regular user already present in user database, skipping interactive user creation.");
2932 RET_GATHER(ret
, create_interactively());
2936 static int drop_from_identity(const char *field
) {
2941 /* If we are called to update an identity record and drop some field, let's keep track of what to
2942 * remove from the old record */
2943 r
= strv_extend(&arg_identity_filter
, field
);
2947 /* Let's also drop the field if it was previously set to a new value on the same command line */
2948 r
= sd_json_variant_filter(&arg_identity_extra
, STRV_MAKE(field
));
2950 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2952 r
= sd_json_variant_filter(&arg_identity_extra_this_machine
, STRV_MAKE(field
));
2954 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2956 r
= sd_json_variant_filter(&arg_identity_extra_privileged
, STRV_MAKE(field
));
2958 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2963 static int help(int argc
, char *argv
[], void *userdata
) {
2964 _cleanup_free_
char *link
= NULL
;
2967 pager_open(arg_pager_flags
);
2969 r
= terminal_urlify_man("homectl", "1", &link
);
2973 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
2974 "%2$sCreate, manipulate or inspect home directories.%3$s\n"
2975 "\n%4$sBasic User Manipulation Commands:%5$s\n"
2976 " list List home areas\n"
2977 " inspect USER… Inspect a home area\n"
2978 " create USER Create a home area\n"
2979 " update USER Update a home area\n"
2980 " passwd USER Change password of a home area\n"
2981 " resize USER SIZE Resize a home area\n"
2982 " remove USER… Remove a home area\n"
2983 "\n%4$sAdvanced User Manipulation Commands:%5$s\n"
2984 " activate USER… Activate a home area\n"
2985 " deactivate USER… Deactivate a home area\n"
2986 " deactivate-all Deactivate all active home areas\n"
2987 " with USER [COMMAND…] Run shell or command with access to a home area\n"
2988 " authenticate USER… Authenticate a home area\n"
2989 "\n%4$sUser Migration Commands:%5$s\n"
2990 " adopt PATH… Add an existing home area on this system\n"
2991 " register PATH… Register a user record locally\n"
2992 " unregister USER… Unregister a user record locally\n"
2993 "\n%4$sSigning Keys Commands:%5$s\n"
2994 " list-signing-keys List home signing keys\n"
2995 " get-signing-key [NAME…] Get a named home signing key\n"
2996 " add-signing-key FILE… Add home signing key\n"
2997 " remove-signing-key NAME… Remove home signing key\n"
2998 "\n%4$sLock/Unlock Commands:%5$s\n"
2999 " lock USER… Temporarily lock an active home area\n"
3000 " unlock USER… Unlock a temporarily locked home area\n"
3001 " lock-all Lock all suitable home areas\n"
3002 "\n%4$sOther Commands:%5$s\n"
3003 " rebalance Rebalance free space between home areas\n"
3004 " firstboot Run first-boot home area creation wizard\n"
3005 "\n%4$sOptions:%5$s\n"
3006 " -h --help Show this help\n"
3007 " --version Show package version\n"
3008 " --no-pager Do not pipe output into a pager\n"
3009 " --no-legend Do not show the headers and footers\n"
3010 " --no-ask-password Do not ask for system passwords\n"
3011 " --offline Don't update record embedded in home directory\n"
3012 " -H --host=[USER@]HOST Operate on remote host\n"
3013 " -M --machine=CONTAINER Operate on local container\n"
3014 " --identity=PATH Read JSON identity from file\n"
3015 " --json=FORMAT Output inspection data in JSON (takes one of\n"
3016 " pretty, short, off)\n"
3017 " -j Equivalent to --json=pretty (on TTY) or\n"
3018 " --json=short (otherwise)\n"
3019 " --export-format= Strip JSON inspection data (full, stripped,\n"
3021 " -E When specified once equals -j --export-format=\n"
3022 " stripped, when specified twice equals\n"
3023 " -j --export-format=minimal\n"
3024 " --prompt-new-user firstboot: Query user interactively for user\n"
3026 " --key-name=NAME Key name when adding a signing key\n"
3027 " --seize=no Do not strip existing signatures of user record\n"
3029 "\n%4$sGeneral User Record Properties:%5$s\n"
3030 " -c --real-name=REALNAME Real name for user\n"
3031 " --realm=REALM Realm to create user in\n"
3032 " --alias=ALIAS Define alias usernames for this account\n"
3033 " --email-address=EMAIL Email address for user\n"
3034 " --location=LOCATION Set location of user on earth\n"
3035 " --icon-name=NAME Icon name for user\n"
3036 " -d --home-dir=PATH Home directory\n"
3037 " -u --uid=UID Numeric UID for user\n"
3038 " -G --member-of=GROUP Add user to group\n"
3039 " --capability-bounding-set=CAPS\n"
3040 " Bounding POSIX capability set\n"
3041 " --capability-ambient-set=CAPS\n"
3042 " Ambient POSIX capability set\n"
3043 " --access-mode=MODE User home directory access mode\n"
3044 " --umask=MODE Umask for user when logging in\n"
3045 " --skel=PATH Skeleton directory to use\n"
3046 " --shell=PATH Shell for account\n"
3047 " --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n"
3048 " --timezone=TIMEZONE Set a time-zone\n"
3049 " --language=LOCALE Set preferred languages\n"
3050 " --default-area=AREA Select default area\n"
3051 "\n%4$sAuthentication User Record Properties:%5$s\n"
3052 " --ssh-authorized-keys=KEYS\n"
3053 " Specify SSH public keys\n"
3054 " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
3055 " private key and matching X.509 certificate\n"
3056 " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
3058 " --fido2-with-client-pin=BOOL\n"
3059 " Whether to require entering a PIN to unlock the\n"
3061 " --fido2-with-user-presence=BOOL\n"
3062 " Whether to require user presence to unlock the\n"
3064 " --fido2-with-user-verification=BOOL\n"
3065 " Whether to require user verification to unlock\n"
3067 " --recovery-key=BOOL Add a recovery key\n"
3068 "\n%4$sBlob Directory User Record Properties:%5$s\n"
3069 " -b --blob=[FILENAME=]PATH\n"
3070 " Path to a replacement blob directory, or replace\n"
3071 " an individual files in the blob directory.\n"
3072 " --avatar=PATH Path to user avatar picture\n"
3073 " --login-background=PATH Path to user login background picture\n"
3074 "\n%4$sAccount Management User Record Properties:%5$s\n"
3075 " --locked=BOOL Set locked account state\n"
3076 " --not-before=TIMESTAMP Do not allow logins before\n"
3077 " --not-after=TIMESTAMP Do not allow logins after\n"
3078 " --rate-limit-interval=SECS\n"
3079 " Login rate-limit interval in seconds\n"
3080 " --rate-limit-burst=NUMBER\n"
3081 " Login rate-limit attempts per interval\n"
3082 "\n%4$sPassword Policy User Record Properties:%5$s\n"
3083 " --password-hint=HINT Set Password hint\n"
3084 " --enforce-password-policy=BOOL\n"
3085 " Control whether to enforce system's password\n"
3086 " policy for this user\n"
3087 " -P Same as --enforce-password-policy=no\n"
3088 " --password-change-now=BOOL\n"
3089 " Require the password to be changed on next login\n"
3090 " --password-change-min=TIME\n"
3091 " Require minimum time between password changes\n"
3092 " --password-change-max=TIME\n"
3093 " Require maximum time between password changes\n"
3094 " --password-change-warn=TIME\n"
3095 " How much time to warn before password expiry\n"
3096 " --password-change-inactive=TIME\n"
3097 " How much time to block password after expiry\n"
3098 "\n%4$sResource Management User Record Properties:%5$s\n"
3099 " --disk-size=BYTES Size to assign the user on disk\n"
3100 " --nice=NICE Nice level for user\n"
3101 " --rlimit=LIMIT=VALUE[:VALUE]\n"
3102 " Set resource limits\n"
3103 " --tasks-max=MAX Set maximum number of per-user tasks\n"
3104 " --memory-high=BYTES Set high memory threshold in bytes\n"
3105 " --memory-max=BYTES Set maximum memory limit\n"
3106 " --cpu-weight=WEIGHT Set CPU weight\n"
3107 " --io-weight=WEIGHT Set IO weight\n"
3108 " --tmp-limit=BYTES|PERCENT Set limit on /tmp/\n"
3109 " --dev-shm-limit=BYTES|PERCENT\n"
3110 " Set limit on /dev/shm/\n"
3111 "\n%4$sStorage User Record Properties:%5$s\n"
3112 " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
3113 " subvolume, cifs)\n"
3114 " --image-path=PATH Path to image file/directory\n"
3115 " --drop-caches=BOOL Whether to automatically drop caches on logout\n"
3116 "\n%4$sLUKS Storage User Record Properties:%5$s\n"
3117 " --fs-type=TYPE File system type to use in case of luks\n"
3118 " storage (btrfs, ext4, xfs)\n"
3119 " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
3120 " when activated (mounted)\n"
3121 " --luks-offline-discard=BOOL\n"
3122 " Whether to trim file on logout\n"
3123 " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
3124 " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
3125 " --luks-volume-key-size=BITS\n"
3126 " Volume key size to use for LUKS encryption\n"
3127 " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
3128 " --luks-pbkdf-hash-algorithm=ALGORITHM\n"
3129 " PBKDF hash algorithm to use\n"
3130 " --luks-pbkdf-time-cost=SECS\n"
3131 " Time cost for PBKDF in seconds\n"
3132 " --luks-pbkdf-memory-cost=BYTES\n"
3133 " Memory cost for PBKDF in bytes\n"
3134 " --luks-pbkdf-parallel-threads=NUMBER\n"
3135 " Number of parallel threads for PKBDF\n"
3136 " --luks-sector-size=BYTES\n"
3137 " Sector size for LUKS encryption in bytes\n"
3138 " --luks-extra-mount-options=OPTIONS\n"
3139 " LUKS extra mount options\n"
3140 " --auto-resize-mode=MODE Automatically grow/shrink home on login/logout\n"
3141 " --rebalance-weight=WEIGHT Weight while rebalancing\n"
3142 "\n%4$sMounting User Record Properties:%5$s\n"
3143 " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
3144 " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
3145 " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
3146 "\n%4$sCIFS User Record Properties:%5$s\n"
3147 " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
3148 " --cifs-user-name=USER CIFS (Windows) user name\n"
3149 " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
3150 " --cifs-extra-mount-options=OPTIONS\n"
3151 " CIFS (Windows) extra mount options\n"
3152 "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
3153 " --stop-delay=SECS How long to leave user services running after\n"
3155 " --kill-processes=BOOL Whether to kill user processes when sessions\n"
3157 " --auto-login=BOOL Try to log this user in automatically\n"
3158 " --session-launcher=LAUNCHER\n"
3159 " Preferred session launcher file\n"
3160 " --session-type=TYPE Preferred session type\n"
3161 "\nSee the %6$s for details.\n",
3162 program_invocation_short_name
,
3172 static int parse_argv(int argc
, char *argv
[]) {
3173 _cleanup_strv_free_
char **arg_languages
= NULL
;
3176 ARG_VERSION
= 0x100,
3179 ARG_NO_ASK_PASSWORD
,
3191 ARG_LUKS_OFFLINE_DISCARD
,
3197 ARG_SSH_AUTHORIZED_KEYS
,
3206 ARG_LUKS_CIPHER_MODE
,
3207 ARG_LUKS_VOLUME_KEY_SIZE
,
3214 ARG_CIFS_EXTRA_MOUNT_OPTIONS
,
3220 ARG_LUKS_PBKDF_TYPE
,
3221 ARG_LUKS_PBKDF_HASH_ALGORITHM
,
3222 ARG_LUKS_PBKDF_FORCE_ITERATIONS
,
3223 ARG_LUKS_PBKDF_TIME_COST
,
3224 ARG_LUKS_PBKDF_MEMORY_COST
,
3225 ARG_LUKS_PBKDF_PARALLEL_THREADS
,
3226 ARG_LUKS_SECTOR_SIZE
,
3227 ARG_RATE_LIMIT_INTERVAL
,
3228 ARG_RATE_LIMIT_BURST
,
3231 ARG_ENFORCE_PASSWORD_POLICY
,
3232 ARG_PASSWORD_CHANGE_NOW
,
3233 ARG_PASSWORD_CHANGE_MIN
,
3234 ARG_PASSWORD_CHANGE_MAX
,
3235 ARG_PASSWORD_CHANGE_WARN
,
3236 ARG_PASSWORD_CHANGE_INACTIVE
,
3239 ARG_SESSION_LAUNCHER
,
3241 ARG_PKCS11_TOKEN_URI
,
3248 ARG_AND_CHANGE_PASSWORD
,
3250 ARG_LUKS_EXTRA_MOUNT_OPTIONS
,
3251 ARG_AUTO_RESIZE_MODE
,
3252 ARG_REBALANCE_WEIGHT
,
3254 ARG_CAPABILITY_BOUNDING_SET
,
3255 ARG_CAPABILITY_AMBIENT_SET
,
3256 ARG_PROMPT_NEW_USER
,
3258 ARG_LOGIN_BACKGROUND
,
3267 static const struct option options
[] = {
3268 { "help", no_argument
, NULL
, 'h' },
3269 { "version", no_argument
, NULL
, ARG_VERSION
},
3270 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
3271 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
3272 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
3273 { "offline", no_argument
, NULL
, ARG_OFFLINE
},
3274 { "host", required_argument
, NULL
, 'H' },
3275 { "machine", required_argument
, NULL
, 'M' },
3276 { "identity", required_argument
, NULL
, 'I' },
3277 { "real-name", required_argument
, NULL
, 'c' },
3278 { "comment", required_argument
, NULL
, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
3279 { "realm", required_argument
, NULL
, ARG_REALM
},
3280 { "alias", required_argument
, NULL
, ARG_ALIAS
},
3281 { "email-address", required_argument
, NULL
, ARG_EMAIL_ADDRESS
},
3282 { "location", required_argument
, NULL
, ARG_LOCATION
},
3283 { "password-hint", required_argument
, NULL
, ARG_PASSWORD_HINT
},
3284 { "icon-name", required_argument
, NULL
, ARG_ICON_NAME
},
3285 { "home-dir", required_argument
, NULL
, 'd' }, /* Compatible with useradd(8) */
3286 { "uid", required_argument
, NULL
, 'u' }, /* Compatible with useradd(8) */
3287 { "member-of", required_argument
, NULL
, 'G' },
3288 { "groups", required_argument
, NULL
, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
3289 { "skel", required_argument
, NULL
, 'k' }, /* Compatible with useradd(8) */
3290 { "shell", required_argument
, NULL
, 's' }, /* Compatible with useradd(8) */
3291 { "setenv", required_argument
, NULL
, ARG_SETENV
},
3292 { "timezone", required_argument
, NULL
, ARG_TIMEZONE
},
3293 { "language", required_argument
, NULL
, ARG_LANGUAGE
},
3294 { "locked", required_argument
, NULL
, ARG_LOCKED
},
3295 { "not-before", required_argument
, NULL
, ARG_NOT_BEFORE
},
3296 { "not-after", required_argument
, NULL
, ARG_NOT_AFTER
},
3297 { "expiredate", required_argument
, NULL
, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
3298 { "ssh-authorized-keys", required_argument
, NULL
, ARG_SSH_AUTHORIZED_KEYS
},
3299 { "disk-size", required_argument
, NULL
, ARG_DISK_SIZE
},
3300 { "access-mode", required_argument
, NULL
, ARG_ACCESS_MODE
},
3301 { "umask", required_argument
, NULL
, ARG_UMASK
},
3302 { "nice", required_argument
, NULL
, ARG_NICE
},
3303 { "rlimit", required_argument
, NULL
, ARG_RLIMIT
},
3304 { "tasks-max", required_argument
, NULL
, ARG_TASKS_MAX
},
3305 { "memory-high", required_argument
, NULL
, ARG_MEMORY_HIGH
},
3306 { "memory-max", required_argument
, NULL
, ARG_MEMORY_MAX
},
3307 { "cpu-weight", required_argument
, NULL
, ARG_CPU_WEIGHT
},
3308 { "io-weight", required_argument
, NULL
, ARG_IO_WEIGHT
},
3309 { "storage", required_argument
, NULL
, ARG_STORAGE
},
3310 { "image-path", required_argument
, NULL
, ARG_IMAGE_PATH
},
3311 { "fs-type", required_argument
, NULL
, ARG_FS_TYPE
},
3312 { "luks-discard", required_argument
, NULL
, ARG_LUKS_DISCARD
},
3313 { "luks-offline-discard", required_argument
, NULL
, ARG_LUKS_OFFLINE_DISCARD
},
3314 { "luks-cipher", required_argument
, NULL
, ARG_LUKS_CIPHER
},
3315 { "luks-cipher-mode", required_argument
, NULL
, ARG_LUKS_CIPHER_MODE
},
3316 { "luks-volume-key-size", required_argument
, NULL
, ARG_LUKS_VOLUME_KEY_SIZE
},
3317 { "luks-pbkdf-type", required_argument
, NULL
, ARG_LUKS_PBKDF_TYPE
},
3318 { "luks-pbkdf-hash-algorithm", required_argument
, NULL
, ARG_LUKS_PBKDF_HASH_ALGORITHM
},
3319 { "luks-pbkdf-force-iterations", required_argument
, NULL
, ARG_LUKS_PBKDF_FORCE_ITERATIONS
},
3320 { "luks-pbkdf-time-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_TIME_COST
},
3321 { "luks-pbkdf-memory-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_MEMORY_COST
},
3322 { "luks-pbkdf-parallel-threads", required_argument
, NULL
, ARG_LUKS_PBKDF_PARALLEL_THREADS
},
3323 { "luks-sector-size", required_argument
, NULL
, ARG_LUKS_SECTOR_SIZE
},
3324 { "nosuid", required_argument
, NULL
, ARG_NOSUID
},
3325 { "nodev", required_argument
, NULL
, ARG_NODEV
},
3326 { "noexec", required_argument
, NULL
, ARG_NOEXEC
},
3327 { "cifs-user-name", required_argument
, NULL
, ARG_CIFS_USER_NAME
},
3328 { "cifs-domain", required_argument
, NULL
, ARG_CIFS_DOMAIN
},
3329 { "cifs-service", required_argument
, NULL
, ARG_CIFS_SERVICE
},
3330 { "cifs-extra-mount-options", required_argument
, NULL
, ARG_CIFS_EXTRA_MOUNT_OPTIONS
},
3331 { "rate-limit-interval", required_argument
, NULL
, ARG_RATE_LIMIT_INTERVAL
},
3332 { "rate-limit-burst", required_argument
, NULL
, ARG_RATE_LIMIT_BURST
},
3333 { "stop-delay", required_argument
, NULL
, ARG_STOP_DELAY
},
3334 { "kill-processes", required_argument
, NULL
, ARG_KILL_PROCESSES
},
3335 { "enforce-password-policy", required_argument
, NULL
, ARG_ENFORCE_PASSWORD_POLICY
},
3336 { "password-change-now", required_argument
, NULL
, ARG_PASSWORD_CHANGE_NOW
},
3337 { "password-change-min", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MIN
},
3338 { "password-change-max", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MAX
},
3339 { "password-change-warn", required_argument
, NULL
, ARG_PASSWORD_CHANGE_WARN
},
3340 { "password-change-inactive", required_argument
, NULL
, ARG_PASSWORD_CHANGE_INACTIVE
},
3341 { "auto-login", required_argument
, NULL
, ARG_AUTO_LOGIN
},
3342 { "session-launcher", required_argument
, NULL
, ARG_SESSION_LAUNCHER
, },
3343 { "session-type", required_argument
, NULL
, ARG_SESSION_TYPE
, },
3344 { "json", required_argument
, NULL
, ARG_JSON
},
3345 { "export-format", required_argument
, NULL
, ARG_EXPORT_FORMAT
},
3346 { "pkcs11-token-uri", required_argument
, NULL
, ARG_PKCS11_TOKEN_URI
},
3347 { "fido2-credential-algorithm", required_argument
, NULL
, ARG_FIDO2_CRED_ALG
},
3348 { "fido2-device", required_argument
, NULL
, ARG_FIDO2_DEVICE
},
3349 { "fido2-with-client-pin", required_argument
, NULL
, ARG_FIDO2_WITH_PIN
},
3350 { "fido2-with-user-presence", required_argument
, NULL
, ARG_FIDO2_WITH_UP
},
3351 { "fido2-with-user-verification", required_argument
, NULL
, ARG_FIDO2_WITH_UV
},
3352 { "recovery-key", required_argument
, NULL
, ARG_RECOVERY_KEY
},
3353 { "and-resize", required_argument
, NULL
, ARG_AND_RESIZE
},
3354 { "and-change-password", required_argument
, NULL
, ARG_AND_CHANGE_PASSWORD
},
3355 { "drop-caches", required_argument
, NULL
, ARG_DROP_CACHES
},
3356 { "luks-extra-mount-options", required_argument
, NULL
, ARG_LUKS_EXTRA_MOUNT_OPTIONS
},
3357 { "auto-resize-mode", required_argument
, NULL
, ARG_AUTO_RESIZE_MODE
},
3358 { "rebalance-weight", required_argument
, NULL
, ARG_REBALANCE_WEIGHT
},
3359 { "capability-bounding-set", required_argument
, NULL
, ARG_CAPABILITY_BOUNDING_SET
},
3360 { "capability-ambient-set", required_argument
, NULL
, ARG_CAPABILITY_AMBIENT_SET
},
3361 { "prompt-new-user", no_argument
, NULL
, ARG_PROMPT_NEW_USER
},
3362 { "blob", required_argument
, NULL
, 'b' },
3363 { "avatar", required_argument
, NULL
, ARG_AVATAR
},
3364 { "login-background", required_argument
, NULL
, ARG_LOGIN_BACKGROUND
},
3365 { "tmp-limit", required_argument
, NULL
, ARG_TMP_LIMIT
},
3366 { "dev-shm-limit", required_argument
, NULL
, ARG_DEV_SHM_LIMIT
},
3367 { "default-area", required_argument
, NULL
, ARG_DEFAULT_AREA
},
3368 { "key-name", required_argument
, NULL
, ARG_KEY_NAME
},
3369 { "seize", required_argument
, NULL
, ARG_SEIZE
},
3370 { "match", required_argument
, NULL
, ARG_MATCH
},
3376 /* This points to one of arg_identity_extra, arg_identity_extra_this_machine,
3377 * arg_identity_extra_other_machines, in order to redirect changes on the next property being set to
3378 * this part of the identity, instead of the default. */
3379 sd_json_variant
**match_identity
= NULL
;
3384 /* Eventually we should probably turn this into a proper --dry-run option, but as long as it is not hooked up everywhere let's make it an environment variable only. */
3385 r
= getenv_bool("SYSTEMD_HOME_DRY_RUN");
3388 else if (r
!= -ENXIO
)
3389 log_debug_errno(r
, "Unable to parse $SYSTEMD_HOME_DRY_RUN, ignoring: %m");
3394 c
= getopt_long(argc
, argv
, "hH:M:I:c:d:u:G:k:s:e:b:jPENAT", options
, NULL
);
3401 return help(0, NULL
, NULL
);
3407 arg_pager_flags
|= PAGER_DISABLE
;
3414 case ARG_NO_ASK_PASSWORD
:
3415 arg_ask_password
= false;
3423 arg_transport
= BUS_TRANSPORT_REMOTE
;
3428 r
= parse_machine_argument(optarg
, &arg_host
, &arg_transport
);
3434 arg_identity
= optarg
;
3438 if (isempty(optarg
)) {
3439 r
= drop_from_identity("realName");
3446 if (!valid_gecos(optarg
))
3447 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Real name '%s' not a valid GECOS field.", optarg
);
3449 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "realName", optarg
);
3451 return log_error_errno(r
, "Failed to set realName field: %m");
3456 if (isempty(optarg
)) {
3457 r
= drop_from_identity("aliases");
3463 for (const char *p
= optarg
;;) {
3464 _cleanup_free_
char *word
= NULL
;
3466 r
= extract_first_word(&p
, &word
, ",", 0);
3468 return log_error_errno(r
, "Failed to parse alias list: %m");
3472 if (!valid_user_group_name(word
, 0))
3473 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid alias user name %s.", word
);
3475 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*av
=
3476 sd_json_variant_ref(sd_json_variant_by_key(arg_identity_extra
, "aliases"));
3478 _cleanup_strv_free_
char **list
= NULL
;
3479 r
= sd_json_variant_strv(av
, &list
);
3481 return log_error_errno(r
, "Failed to parse group list: %m");
3483 r
= strv_extend(&list
, word
);
3487 strv_sort_uniq(list
);
3489 av
= sd_json_variant_unref(av
);
3490 r
= sd_json_variant_new_array_strv(&av
, list
);
3492 return log_error_errno(r
, "Failed to create alias list JSON: %m");
3494 r
= sd_json_variant_set_field(&arg_identity_extra
, "aliases", av
);
3496 return log_error_errno(r
, "Failed to update alias list: %m");
3503 _cleanup_free_
char *hd
= NULL
;
3505 if (isempty(optarg
)) {
3506 r
= drop_from_identity("homeDirectory");
3513 r
= parse_path_argument(optarg
, false, &hd
);
3517 if (!valid_home(hd
))
3518 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Home directory '%s' not valid.", hd
);
3520 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "homeDirectory", hd
);
3522 return log_error_errno(r
, "Failed to set homeDirectory field: %m");
3528 if (isempty(optarg
)) {
3529 r
= drop_from_identity("realm");
3536 r
= dns_name_is_valid(optarg
);
3538 return log_error_errno(r
, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg
);
3540 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Realm '%s' is not a valid DNS domain.", optarg
);
3542 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "realm", optarg
);
3544 return log_error_errno(r
, "Failed to set realm field: %m");
3547 case ARG_EMAIL_ADDRESS
:
3550 case ARG_CIFS_USER_NAME
:
3551 case ARG_CIFS_DOMAIN
:
3552 case ARG_CIFS_EXTRA_MOUNT_OPTIONS
:
3553 case ARG_LUKS_EXTRA_MOUNT_OPTIONS
:
3554 case ARG_SESSION_LAUNCHER
:
3555 case ARG_SESSION_TYPE
: {
3558 c
== ARG_EMAIL_ADDRESS
? "emailAddress" :
3559 c
== ARG_LOCATION
? "location" :
3560 c
== ARG_ICON_NAME
? "iconName" :
3561 c
== ARG_CIFS_USER_NAME
? "cifsUserName" :
3562 c
== ARG_CIFS_DOMAIN
? "cifsDomain" :
3563 c
== ARG_CIFS_EXTRA_MOUNT_OPTIONS
? "cifsExtraMountOptions" :
3564 c
== ARG_LUKS_EXTRA_MOUNT_OPTIONS
? "luksExtraMountOptions" :
3565 c
== ARG_SESSION_LAUNCHER
? "preferredSessionLauncher" :
3566 c
== ARG_SESSION_TYPE
? "preferredSessionType" :
3571 if (isempty(optarg
)) {
3572 r
= drop_from_identity(field
);
3579 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, field
, optarg
);
3581 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3586 case ARG_CIFS_SERVICE
:
3587 if (isempty(optarg
)) {
3588 r
= drop_from_identity("cifsService");
3595 r
= parse_cifs_service(optarg
, NULL
, NULL
, NULL
);
3597 return log_error_errno(r
, "Failed to validate CIFS service name: %s", optarg
);
3599 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "cifsService", optarg
);
3601 return log_error_errno(r
, "Failed to set cifsService field: %m");
3605 case ARG_PASSWORD_HINT
:
3606 if (isempty(optarg
)) {
3607 r
= drop_from_identity("passwordHint");
3614 r
= sd_json_variant_set_field_string(&arg_identity_extra_privileged
, "passwordHint", optarg
);
3616 return log_error_errno(r
, "Failed to set passwordHint field: %m");
3618 string_erase(optarg
);
3624 if (isempty(optarg
)) {
3625 r
= drop_from_identity("niceLevel");
3631 r
= parse_nice(optarg
, &nc
);
3633 return log_error_errno(r
, "Failed to parse nice level: %s", optarg
);
3635 r
= sd_json_variant_set_field_integer(match_identity
?: &arg_identity_extra
, "niceLevel", nc
);
3637 return log_error_errno(r
, "Failed to set niceLevel field: %m");
3643 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*jcur
= NULL
, *jmax
= NULL
;
3644 _cleanup_free_
char *field
= NULL
, *t
= NULL
;
3649 if (isempty(optarg
)) {
3650 /* Remove all resource limits */
3652 r
= drop_from_identity("resourceLimits");
3656 arg_identity_filter_rlimits
= strv_free(arg_identity_filter_rlimits
);
3657 arg_identity_extra_rlimits
= sd_json_variant_unref(arg_identity_extra_rlimits
);
3661 eq
= strchr(optarg
, '=');
3663 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Can't parse resource limit assignment: %s", optarg
);
3665 field
= strndup(optarg
, eq
- optarg
);
3669 l
= rlimit_from_string_harder(field
);
3671 return log_error_errno(l
, "Unknown resource limit type: %s", field
);
3673 if (isempty(eq
+ 1)) {
3674 /* Remove only the specific rlimit */
3676 r
= strv_extend(&arg_identity_filter_rlimits
, rlimit_to_string(l
));
3680 r
= sd_json_variant_filter(&arg_identity_extra_rlimits
, STRV_MAKE(field
));
3682 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
3687 r
= rlimit_parse(l
, eq
+ 1, &rl
);
3689 return log_error_errno(r
, "Failed to parse resource limit value: %s", eq
+ 1);
3691 r
= rl
.rlim_cur
== RLIM_INFINITY
? sd_json_variant_new_null(&jcur
) : sd_json_variant_new_unsigned(&jcur
, rl
.rlim_cur
);
3693 return log_error_errno(r
, "Failed to allocate current integer: %m");
3695 r
= rl
.rlim_max
== RLIM_INFINITY
? sd_json_variant_new_null(&jmax
) : sd_json_variant_new_unsigned(&jmax
, rl
.rlim_max
);
3697 return log_error_errno(r
, "Failed to allocate maximum integer: %m");
3699 t
= strjoin("RLIMIT_", rlimit_to_string(l
));
3703 r
= sd_json_variant_set_fieldbo(
3704 &arg_identity_extra_rlimits
, t
,
3705 SD_JSON_BUILD_PAIR("cur", SD_JSON_BUILD_VARIANT(jcur
)),
3706 SD_JSON_BUILD_PAIR("max", SD_JSON_BUILD_VARIANT(jmax
)));
3708 return log_error_errno(r
, "Failed to set %s field: %m", rlimit_to_string(l
));
3716 if (isempty(optarg
)) {
3717 r
= drop_from_identity("uid");
3724 r
= parse_uid(optarg
, &uid
);
3726 return log_error_errno(r
, "Failed to parse UID '%s'.", optarg
);
3728 if (uid_is_system(uid
))
3729 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in system range, refusing.", uid
);
3730 if (uid_is_greeter(uid
))
3731 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in greeter range, refusing.", uid
);
3732 if (uid_is_dynamic(uid
))
3733 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in dynamic range, refusing.", uid
);
3734 if (uid
== UID_NOBODY
)
3735 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is nobody UID, refusing.", uid
);
3737 r
= sd_json_variant_set_field_unsigned(&arg_identity_extra
, "uid", uid
);
3739 return log_error_errno(r
, "Failed to set realm field: %m");
3745 case ARG_IMAGE_PATH
: {
3746 const char *field
= c
== 'k' ? "skeletonDirectory" : "imagePath";
3747 _cleanup_free_
char *v
= NULL
;
3749 if (isempty(optarg
)) {
3750 r
= drop_from_identity(field
);
3757 r
= parse_path_argument(optarg
, false, &v
);
3761 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra_this_machine
, field
, v
);
3763 return log_error_errno(r
, "Failed to set %s field: %m", v
);
3769 if (isempty(optarg
)) {
3770 r
= drop_from_identity("shell");
3777 if (!valid_shell(optarg
))
3778 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Shell '%s' not valid.", optarg
);
3780 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "shell", optarg
);
3782 return log_error_errno(r
, "Failed to set shell field: %m");
3787 _cleanup_free_
char **l
= NULL
;
3788 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*ne
= NULL
;
3791 if (isempty(optarg
)) {
3792 r
= drop_from_identity("environment");
3799 e
= sd_json_variant_by_key(match_identity
? *match_identity
: arg_identity_extra
, "environment");
3801 r
= sd_json_variant_strv(e
, &l
);
3803 return log_error_errno(r
, "Failed to parse JSON environment field: %m");
3806 r
= strv_env_replace_strdup_passthrough(&l
, optarg
);
3808 return log_error_errno(r
, "Cannot assign environment variable %s: %m", optarg
);
3812 r
= sd_json_variant_new_array_strv(&ne
, l
);
3814 return log_error_errno(r
, "Failed to allocate environment list JSON: %m");
3816 r
= sd_json_variant_set_field(match_identity
?: &arg_identity_extra
, "environment", ne
);
3818 return log_error_errno(r
, "Failed to set environment list: %m");
3824 if (isempty(optarg
)) {
3825 r
= drop_from_identity("timeZone");
3832 if (!timezone_is_valid(optarg
, LOG_DEBUG
))
3833 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Timezone '%s' is not valid.", optarg
);
3835 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "timeZone", optarg
);
3837 return log_error_errno(r
, "Failed to set timezone field: %m");
3841 case ARG_LANGUAGE
: {
3842 const char *p
= optarg
;
3845 r
= drop_from_identity("preferredLanguage");
3849 r
= drop_from_identity("additionalLanguages");
3853 arg_languages
= strv_free(arg_languages
);
3858 _cleanup_free_
char *word
= NULL
;
3860 r
= extract_first_word(&p
, &word
, ",:", 0);
3862 return log_error_errno(r
, "Failed to parse locale list: %m");
3866 if (!locale_is_valid(word
))
3867 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Locale '%s' is not valid.", word
);
3869 if (locale_is_installed(word
) <= 0)
3870 log_warning("Locale '%s' is not installed, accepting anyway.", word
);
3872 r
= strv_consume(&arg_languages
, TAKE_PTR(word
));
3876 strv_uniq(arg_languages
);
3886 case ARG_KILL_PROCESSES
:
3887 case ARG_ENFORCE_PASSWORD_POLICY
:
3888 case ARG_AUTO_LOGIN
:
3889 case ARG_PASSWORD_CHANGE_NOW
: {
3891 c
== ARG_LOCKED
? "locked" :
3892 c
== ARG_NOSUID
? "mountNoSuid" :
3893 c
== ARG_NODEV
? "mountNoDevices" :
3894 c
== ARG_NOEXEC
? "mountNoExecute" :
3895 c
== ARG_KILL_PROCESSES
? "killProcesses" :
3896 c
== ARG_ENFORCE_PASSWORD_POLICY
? "enforcePasswordPolicy" :
3897 c
== ARG_AUTO_LOGIN
? "autoLogin" :
3898 c
== ARG_PASSWORD_CHANGE_NOW
? "passwordChangeNow" :
3903 if (isempty(optarg
)) {
3904 r
= drop_from_identity(field
);
3911 r
= parse_boolean(optarg
);
3913 return log_error_errno(r
, "Failed to parse %s boolean: %m", field
);
3915 r
= sd_json_variant_set_field_boolean(match_identity
?: &arg_identity_extra
, field
, r
> 0);
3917 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3923 r
= sd_json_variant_set_field_boolean(&arg_identity_extra
, "enforcePasswordPolicy", false);
3925 return log_error_errno(r
, "Failed to set enforcePasswordPolicy field: %m");
3930 if (isempty(optarg
)) {
3931 FOREACH_STRING(prop
, "diskSize", "diskSizeRelative", "rebalanceWeight") {
3932 r
= drop_from_identity(prop
);
3937 arg_disk_size
= arg_disk_size_relative
= UINT64_MAX
;
3941 r
= parse_permyriad(optarg
);
3943 r
= parse_disk_size(optarg
, &arg_disk_size
);
3947 r
= drop_from_identity("diskSizeRelative");
3951 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra_this_machine
, "diskSize", arg_disk_size
);
3953 return log_error_errno(r
, "Failed to set diskSize field: %m");
3955 arg_disk_size_relative
= UINT64_MAX
;
3957 /* Normalize to UINT32_MAX == 100% */
3958 arg_disk_size_relative
= UINT32_SCALE_FROM_PERMYRIAD(r
);
3960 r
= drop_from_identity("diskSize");
3964 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra_this_machine
, "diskSizeRelative", arg_disk_size_relative
);
3966 return log_error_errno(r
, "Failed to set diskSizeRelative field: %m");
3968 arg_disk_size
= UINT64_MAX
;
3971 /* Automatically turn off the rebalance logic if user configured a size explicitly */
3972 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra_this_machine
, "rebalanceWeight", REBALANCE_WEIGHT_OFF
);
3974 return log_error_errno(r
, "Failed to set rebalanceWeight field: %m");
3978 case ARG_ACCESS_MODE
: {
3981 if (isempty(optarg
)) {
3982 r
= drop_from_identity("accessMode");
3989 r
= parse_mode(optarg
, &mode
);
3991 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Access mode '%s' not valid.", optarg
);
3993 r
= sd_json_variant_set_field_unsigned(&arg_identity_extra
, "accessMode", mode
);
3995 return log_error_errno(r
, "Failed to set access mode field: %m");
4000 case ARG_LUKS_DISCARD
:
4001 if (isempty(optarg
)) {
4002 r
= drop_from_identity("luksDiscard");
4009 r
= parse_boolean(optarg
);
4011 return log_error_errno(r
, "Failed to parse --luks-discard= parameter: %s", optarg
);
4013 r
= sd_json_variant_set_field_boolean(match_identity
?: &arg_identity_extra
, "luksDiscard", r
);
4015 return log_error_errno(r
, "Failed to set discard field: %m");
4019 case ARG_LUKS_OFFLINE_DISCARD
:
4020 if (isempty(optarg
)) {
4021 r
= drop_from_identity("luksOfflineDiscard");
4028 r
= parse_boolean(optarg
);
4030 return log_error_errno(r
, "Failed to parse --luks-offline-discard= parameter: %s", optarg
);
4032 r
= sd_json_variant_set_field_boolean(match_identity
?: &arg_identity_extra
, "luksOfflineDiscard", r
);
4034 return log_error_errno(r
, "Failed to set offline discard field: %m");
4038 case ARG_LUKS_VOLUME_KEY_SIZE
:
4039 case ARG_LUKS_PBKDF_FORCE_ITERATIONS
:
4040 case ARG_LUKS_PBKDF_PARALLEL_THREADS
:
4041 case ARG_RATE_LIMIT_BURST
: {
4043 c
== ARG_LUKS_VOLUME_KEY_SIZE
? "luksVolumeKeySize" :
4044 c
== ARG_LUKS_PBKDF_FORCE_ITERATIONS
? "luksPbkdfForceIterations" :
4045 c
== ARG_LUKS_PBKDF_PARALLEL_THREADS
? "luksPbkdfParallelThreads" :
4046 c
== ARG_RATE_LIMIT_BURST
? "rateLimitBurst" : NULL
;
4051 if (isempty(optarg
)) {
4052 r
= drop_from_identity(field
);
4057 r
= safe_atou(optarg
, &n
);
4059 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
4061 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, n
);
4063 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4068 case ARG_LUKS_SECTOR_SIZE
: {
4071 if (isempty(optarg
)) {
4072 r
= drop_from_identity("luksSectorSize");
4079 r
= parse_sector_size(optarg
, &ss
);
4083 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, "luksSectorSize", ss
);
4085 return log_error_errno(r
, "Failed to set sector size field: %m");
4093 if (isempty(optarg
)) {
4094 r
= drop_from_identity("umask");
4101 r
= parse_mode(optarg
, &m
);
4103 return log_error_errno(r
, "Failed to parse umask: %m");
4105 r
= sd_json_variant_set_field_integer(match_identity
?: &arg_identity_extra
, "umask", m
);
4107 return log_error_errno(r
, "Failed to set umask field: %m");
4112 case ARG_SSH_AUTHORIZED_KEYS
: {
4113 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
4114 _cleanup_strv_free_
char **l
= NULL
, **add
= NULL
;
4116 if (isempty(optarg
)) {
4117 r
= drop_from_identity("sshAuthorizedKeys");
4124 if (optarg
[0] == '@') {
4125 _cleanup_fclose_
FILE *f
= NULL
;
4127 /* If prefixed with '@' read from a file */
4129 f
= fopen(optarg
+1, "re");
4131 return log_error_errno(errno
, "Failed to open '%s': %m", optarg
+1);
4134 _cleanup_free_
char *line
= NULL
;
4136 r
= read_line(f
, LONG_LINE_MAX
, &line
);
4138 return log_error_errno(r
, "Failed to read from '%s': %m", optarg
+1);
4148 r
= strv_consume(&add
, TAKE_PTR(line
));
4153 /* Otherwise, assume it's a literal key. Let's do some superficial checks
4154 * before accept it though. */
4156 if (string_has_cc(optarg
, NULL
))
4157 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Authorized key contains control characters, refusing.");
4158 if (optarg
[0] == '#')
4159 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specified key is a comment?");
4161 add
= strv_new(optarg
);
4166 v
= sd_json_variant_ref(sd_json_variant_by_key(arg_identity_extra_privileged
, "sshAuthorizedKeys"));
4168 r
= sd_json_variant_strv(v
, &l
);
4170 return log_error_errno(r
, "Failed to parse SSH authorized keys list: %m");
4173 r
= strv_extend_strv_consume(&l
, TAKE_PTR(add
), /* filter_duplicates = */ true);
4177 v
= sd_json_variant_unref(v
);
4179 r
= sd_json_variant_new_array_strv(&v
, l
);
4183 r
= sd_json_variant_set_field(&arg_identity_extra_privileged
, "sshAuthorizedKeys", v
);
4185 return log_error_errno(r
, "Failed to set authorized keys: %m");
4190 case ARG_NOT_BEFORE
:
4196 field
= c
== ARG_NOT_BEFORE
? "notBeforeUSec" :
4197 IN_SET(c
, ARG_NOT_AFTER
, 'e') ? "notAfterUSec" : NULL
;
4201 if (isempty(optarg
)) {
4202 r
= drop_from_identity(field
);
4209 /* Note the minor discrepancy regarding -e parsing here: we support that for compat
4210 * reasons, and in the original useradd(8) implementation it accepts dates in the
4211 * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
4212 * with greater precision. */
4213 r
= parse_timestamp(optarg
, &n
);
4215 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
4217 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, n
);
4219 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4223 case ARG_PASSWORD_CHANGE_MIN
:
4224 case ARG_PASSWORD_CHANGE_MAX
:
4225 case ARG_PASSWORD_CHANGE_WARN
:
4226 case ARG_PASSWORD_CHANGE_INACTIVE
: {
4230 field
= c
== ARG_PASSWORD_CHANGE_MIN
? "passwordChangeMinUSec" :
4231 c
== ARG_PASSWORD_CHANGE_MAX
? "passwordChangeMaxUSec" :
4232 c
== ARG_PASSWORD_CHANGE_WARN
? "passwordChangeWarnUSec" :
4233 c
== ARG_PASSWORD_CHANGE_INACTIVE
? "passwordChangeInactiveUSec" :
4238 if (isempty(optarg
)) {
4239 r
= drop_from_identity(field
);
4246 r
= parse_sec(optarg
, &n
);
4248 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
4250 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, n
);
4252 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4258 case ARG_LUKS_CIPHER
:
4259 case ARG_LUKS_CIPHER_MODE
:
4260 case ARG_LUKS_PBKDF_TYPE
:
4261 case ARG_LUKS_PBKDF_HASH_ALGORITHM
: {
4264 c
== ARG_STORAGE
? "storage" :
4265 c
== ARG_FS_TYPE
? "fileSystemType" :
4266 c
== ARG_LUKS_CIPHER
? "luksCipher" :
4267 c
== ARG_LUKS_CIPHER_MODE
? "luksCipherMode" :
4268 c
== ARG_LUKS_PBKDF_TYPE
? "luksPbkdfType" :
4269 c
== ARG_LUKS_PBKDF_HASH_ALGORITHM
? "luksPbkdfHashAlgorithm" : NULL
;
4273 if (isempty(optarg
)) {
4274 r
= drop_from_identity(field
);
4281 if (!string_is_safe(optarg
))
4282 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Parameter for %s field not valid: %s", field
, optarg
);
4284 r
= sd_json_variant_set_field_string(
4285 match_identity
?: (IN_SET(c
, ARG_STORAGE
, ARG_FS_TYPE
) ?
4286 &arg_identity_extra_this_machine
:
4287 &arg_identity_extra
), field
, optarg
);
4289 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4294 case ARG_LUKS_PBKDF_TIME_COST
:
4295 case ARG_RATE_LIMIT_INTERVAL
:
4296 case ARG_STOP_DELAY
: {
4298 c
== ARG_LUKS_PBKDF_TIME_COST
? "luksPbkdfTimeCostUSec" :
4299 c
== ARG_RATE_LIMIT_INTERVAL
? "rateLimitIntervalUSec" :
4300 c
== ARG_STOP_DELAY
? "stopDelayUSec" :
4306 if (isempty(optarg
)) {
4307 r
= drop_from_identity(field
);
4314 r
= parse_sec(optarg
, &t
);
4316 return log_error_errno(r
, "Failed to parse %s field: %s", field
, optarg
);
4318 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, t
);
4320 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4326 const char *p
= optarg
;
4329 r
= drop_from_identity("memberOf");
4337 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*mo
= NULL
;
4338 _cleanup_strv_free_
char **list
= NULL
;
4339 _cleanup_free_
char *word
= NULL
;
4341 r
= extract_first_word(&p
, &word
, ",", 0);
4343 return log_error_errno(r
, "Failed to parse group list: %m");
4347 if (!valid_user_group_name(word
, 0))
4348 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid group name %s.", word
);
4350 mo
= sd_json_variant_ref(sd_json_variant_by_key(arg_identity_extra
, "memberOf"));
4352 r
= sd_json_variant_strv(mo
, &list
);
4354 return log_error_errno(r
, "Failed to parse group list: %m");
4356 r
= strv_extend(&list
, word
);
4360 strv_sort_uniq(list
);
4362 mo
= sd_json_variant_unref(mo
);
4363 r
= sd_json_variant_new_array_strv(&mo
, list
);
4365 return log_error_errno(r
, "Failed to create group list JSON: %m");
4367 r
= sd_json_variant_set_field(match_identity
?: &arg_identity_extra
, "memberOf", mo
);
4369 return log_error_errno(r
, "Failed to update group list: %m");
4375 case ARG_TASKS_MAX
: {
4378 if (isempty(optarg
)) {
4379 r
= drop_from_identity("tasksMax");
4385 r
= safe_atou64(optarg
, &u
);
4387 return log_error_errno(r
, "Failed to parse --tasks-max= parameter: %s", optarg
);
4389 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, "tasksMax", u
);
4391 return log_error_errno(r
, "Failed to set tasksMax field: %m");
4396 case ARG_MEMORY_MAX
:
4397 case ARG_MEMORY_HIGH
:
4398 case ARG_LUKS_PBKDF_MEMORY_COST
: {
4400 c
== ARG_MEMORY_MAX
? "memoryMax" :
4401 c
== ARG_MEMORY_HIGH
? "memoryHigh" :
4402 c
== ARG_LUKS_PBKDF_MEMORY_COST
? "luksPbkdfMemoryCost" : NULL
;
4408 if (isempty(optarg
)) {
4409 r
= drop_from_identity(field
);
4415 r
= parse_size(optarg
, 1024, &u
);
4417 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
4419 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra_this_machine
, field
, u
);
4421 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4426 case ARG_CPU_WEIGHT
:
4427 case ARG_IO_WEIGHT
: {
4428 const char *field
= c
== ARG_CPU_WEIGHT
? "cpuWeight" :
4429 c
== ARG_IO_WEIGHT
? "ioWeight" : NULL
;
4434 if (isempty(optarg
)) {
4435 r
= drop_from_identity(field
);
4441 r
= safe_atou64(optarg
, &u
);
4443 return log_error_errno(r
, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg
);
4445 if (!CGROUP_WEIGHT_IS_OK(u
))
4446 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Weight %" PRIu64
" is out of valid weight range.", u
);
4448 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, u
);
4450 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4455 case ARG_PKCS11_TOKEN_URI
:
4456 if (streq(optarg
, "list"))
4457 return pkcs11_list_tokens();
4459 /* If --pkcs11-token-uri= is specified we always drop everything old */
4460 FOREACH_STRING(p
, "pkcs11TokenUri", "pkcs11EncryptedKey") {
4461 r
= drop_from_identity(p
);
4466 if (isempty(optarg
)) {
4467 arg_pkcs11_token_uri
= strv_free(arg_pkcs11_token_uri
);
4471 if (streq(optarg
, "auto")) {
4472 _cleanup_free_
char *found
= NULL
;
4474 r
= pkcs11_find_token_auto(&found
);
4477 r
= strv_consume(&arg_pkcs11_token_uri
, TAKE_PTR(found
));
4479 if (!pkcs11_uri_valid(optarg
))
4480 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Not a valid PKCS#11 URI: %s", optarg
);
4482 r
= strv_extend(&arg_pkcs11_token_uri
, optarg
);
4487 strv_uniq(arg_pkcs11_token_uri
);
4490 case ARG_FIDO2_CRED_ALG
:
4491 r
= parse_fido2_algorithm(optarg
, &arg_fido2_cred_alg
);
4493 return log_error_errno(r
, "Failed to parse COSE algorithm: %s", optarg
);
4496 case ARG_FIDO2_DEVICE
:
4497 if (streq(optarg
, "list"))
4498 return fido2_list_devices();
4500 FOREACH_STRING(p
, "fido2HmacCredential", "fido2HmacSalt") {
4501 r
= drop_from_identity(p
);
4506 if (isempty(optarg
)) {
4507 arg_fido2_device
= strv_free(arg_fido2_device
);
4511 if (streq(optarg
, "auto")) {
4512 _cleanup_free_
char *found
= NULL
;
4514 r
= fido2_find_device_auto(&found
);
4518 r
= strv_consume(&arg_fido2_device
, TAKE_PTR(found
));
4520 r
= strv_extend(&arg_fido2_device
, optarg
);
4524 strv_uniq(arg_fido2_device
);
4527 case ARG_FIDO2_WITH_PIN
:
4528 r
= parse_boolean_argument("--fido2-with-client-pin=", optarg
, NULL
);
4532 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_PIN
, r
);
4535 case ARG_FIDO2_WITH_UP
:
4536 r
= parse_boolean_argument("--fido2-with-user-presence=", optarg
, NULL
);
4540 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UP
, r
);
4543 case ARG_FIDO2_WITH_UV
:
4544 r
= parse_boolean_argument("--fido2-with-user-verification=", optarg
, NULL
);
4548 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UV
, r
);
4551 case ARG_RECOVERY_KEY
:
4552 r
= parse_boolean(optarg
);
4554 return log_error_errno(r
, "Failed to parse --recovery-key= argument: %s", optarg
);
4556 arg_recovery_key
= r
;
4558 FOREACH_STRING(p
, "recoveryKey", "recoveryKeyType") {
4559 r
= drop_from_identity(p
);
4566 case ARG_AUTO_RESIZE_MODE
:
4567 if (isempty(optarg
)) {
4568 r
= drop_from_identity("autoResizeMode");
4575 r
= auto_resize_mode_from_string(optarg
);
4577 return log_error_errno(r
, "Failed to parse --auto-resize-mode= argument: %s", optarg
);
4579 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "autoResizeMode", auto_resize_mode_to_string(r
));
4581 return log_error_errno(r
, "Failed to set autoResizeMode field: %m");
4585 case ARG_REBALANCE_WEIGHT
: {
4588 if (isempty(optarg
)) {
4589 r
= drop_from_identity("rebalanceWeight");
4595 if (streq(optarg
, "off"))
4596 u
= REBALANCE_WEIGHT_OFF
;
4598 r
= safe_atou64(optarg
, &u
);
4600 return log_error_errno(r
, "Failed to parse --rebalance-weight= argument: %s", optarg
);
4602 if (u
< REBALANCE_WEIGHT_MIN
|| u
> REBALANCE_WEIGHT_MAX
)
4603 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
), "Rebalancing weight out of valid range %" PRIu64
"%s%" PRIu64
": %s",
4604 REBALANCE_WEIGHT_MIN
, glyph(GLYPH_ELLIPSIS
), REBALANCE_WEIGHT_MAX
, optarg
);
4607 /* Drop from per machine stuff and everywhere */
4608 r
= drop_from_identity("rebalanceWeight");
4612 /* Add to main identity */
4613 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, "rebalanceWeight", u
);
4615 return log_error_errno(r
, "Failed to set rebalanceWeight field: %m");
4621 arg_json_format_flags
= SD_JSON_FORMAT_PRETTY_AUTO
|SD_JSON_FORMAT_COLOR_AUTO
;
4625 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
4632 if (arg_export_format
== EXPORT_FORMAT_FULL
)
4633 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
4634 else if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
4635 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
4637 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specifying -E more than twice is not supported.");
4639 arg_json_format_flags
&= ~SD_JSON_FORMAT_OFF
;
4640 if (arg_json_format_flags
== 0)
4641 arg_json_format_flags
= SD_JSON_FORMAT_PRETTY_AUTO
|SD_JSON_FORMAT_COLOR_AUTO
;
4644 case ARG_EXPORT_FORMAT
:
4645 if (streq(optarg
, "full"))
4646 arg_export_format
= EXPORT_FORMAT_FULL
;
4647 else if (streq(optarg
, "stripped"))
4648 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
4649 else if (streq(optarg
, "minimal"))
4650 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
4651 else if (streq(optarg
, "help")) {
4660 case ARG_AND_RESIZE
:
4661 arg_and_resize
= true;
4664 case ARG_AND_CHANGE_PASSWORD
:
4665 arg_and_change_password
= true;
4668 case ARG_DROP_CACHES
: {
4669 if (isempty(optarg
)) {
4670 r
= drop_from_identity("dropCaches");
4676 r
= parse_boolean_argument("--drop-caches=", optarg
, NULL
);
4680 r
= sd_json_variant_set_field_boolean(match_identity
?: &arg_identity_extra
, "dropCaches", r
);
4682 return log_error_errno(r
, "Failed to set drop caches field: %m");
4687 case ARG_CAPABILITY_AMBIENT_SET
:
4688 case ARG_CAPABILITY_BOUNDING_SET
: {
4689 _cleanup_strv_free_
char **l
= NULL
;
4690 bool subtract
= false;
4691 uint64_t parsed
, *which
, updated
;
4692 const char *p
, *field
;
4694 if (c
== ARG_CAPABILITY_AMBIENT_SET
) {
4695 which
= &arg_capability_ambient_set
;
4696 field
= "capabilityAmbientSet";
4698 assert(c
== ARG_CAPABILITY_BOUNDING_SET
);
4699 which
= &arg_capability_bounding_set
;
4700 field
= "capabilityBoundingSet";
4703 if (isempty(optarg
)) {
4704 r
= drop_from_identity(field
);
4708 *which
= UINT64_MAX
;
4718 r
= capability_set_from_string(p
, &parsed
);
4720 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid capabilities in capability string '%s'.", p
);
4722 return log_error_errno(r
, "Failed to parse capability string '%s': %m", p
);
4724 if (*which
== UINT64_MAX
)
4725 updated
= subtract
? all_capabilities() & ~parsed
: parsed
;
4727 updated
= *which
& ~parsed
;
4729 updated
= *which
| parsed
;
4731 if (capability_set_to_strv(updated
, &l
) < 0)
4734 r
= sd_json_variant_set_field_strv(match_identity
?: &arg_identity_extra
, field
, l
);
4736 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4742 case ARG_PROMPT_NEW_USER
:
4743 arg_prompt_new_user
= true;
4748 case ARG_LOGIN_BACKGROUND
: {
4749 _cleanup_close_
int fd
= -EBADF
;
4750 _cleanup_free_
char *path
= NULL
, *filename
= NULL
;
4755 if (isempty(optarg
)) { /* --blob= deletes everything, including existing blob dirs */
4756 hashmap_clear(arg_blob_files
);
4757 arg_blob_dir
= mfree(arg_blob_dir
);
4758 arg_blob_clear
= true;
4762 eq
= strrchr(optarg
, '=');
4763 if (!eq
) { /* --blob=/some/path replaces the blob dir */
4764 r
= parse_path_argument(optarg
, false, &arg_blob_dir
);
4766 return log_error_errno(r
, "Failed to parse path %s: %m", optarg
);
4770 /* --blob=filename=/some/path replaces the file "filename" with /some/path */
4771 filename
= strndup(optarg
, eq
- optarg
);
4775 if (isempty(filename
))
4776 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Can't parse blob file assignment: %s", optarg
);
4777 if (!suitable_blob_filename(filename
))
4778 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid blob filename: %s", filename
);
4780 r
= parse_path_argument(eq
+ 1, false, &path
);
4782 return log_error_errno(r
, "Failed to parse path %s: %m", eq
+ 1);
4784 const char *well_known_filename
=
4785 c
== ARG_AVATAR
? "avatar" :
4786 c
== ARG_LOGIN_BACKGROUND
? "login-background" :
4788 assert(well_known_filename
);
4790 filename
= strdup(well_known_filename
);
4794 r
= parse_path_argument(optarg
, false, &path
);
4796 return log_error_errno(r
, "Failed to parse path %s: %m", optarg
);
4800 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
4802 return log_error_errno(errno
, "Failed to open %s: %m", path
);
4804 if (fd_verify_regular(fd
) < 0)
4805 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Provided blob is not a regular file: %s", path
);
4807 fd
= -EBADF
; /* Delete the file */
4809 r
= hashmap_ensure_put(&arg_blob_files
, &blob_fd_hash_ops
, filename
, FD_TO_PTR(fd
));
4811 return log_error_errno(r
, "Failed to map %s to %s in blob directory: %m", path
, filename
);
4812 TAKE_PTR(filename
); /* hashmap takes ownership */
4819 case ARG_DEV_SHM_LIMIT
: {
4821 c
== ARG_TMP_LIMIT
? "tmpLimit" :
4822 c
== ARG_DEV_SHM_LIMIT
? "devShmLimit" : NULL
;
4823 const char *field_scale
=
4824 c
== ARG_TMP_LIMIT
? "tmpLimitScale" :
4825 c
== ARG_DEV_SHM_LIMIT
? "devShmLimitScale" : NULL
;
4828 assert(field_scale
);
4830 if (isempty(optarg
)) {
4831 r
= drop_from_identity(field
);
4834 r
= drop_from_identity(field_scale
);
4840 r
= parse_permyriad(optarg
);
4844 r
= parse_size(optarg
, 1024, &u
);
4846 return log_error_errno(r
, "Failed to parse %s/%s parameter: %s", field
, field_scale
, optarg
);
4848 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, u
);
4850 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4852 r
= drop_from_identity(field_scale
);
4856 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field_scale
, UINT32_SCALE_FROM_PERMYRIAD(r
));
4858 return log_error_errno(r
, "Failed to set %s field: %m", field_scale
);
4860 r
= drop_from_identity(field
);
4868 case ARG_DEFAULT_AREA
:
4869 if (isempty(optarg
)) {
4870 r
= drop_from_identity("defaultArea");
4877 if (!filename_is_valid(optarg
))
4878 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Parameter for default area field not valid: %s", optarg
);
4880 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "defaultArea", optarg
);
4882 return log_error_errno(r
, "Failed to set default area field: %m");
4887 if (isempty(optarg
)) {
4888 arg_key_name
= mfree(arg_key_name
);
4892 if (!filename_is_valid(optarg
))
4893 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specified key name not valid: %s", optarg
);
4895 r
= free_and_strdup_warn(&arg_key_name
, optarg
);
4902 r
= parse_boolean_argument("--seize=", optarg
, &arg_seize
);
4908 if (streq(optarg
, "any"))
4909 match_identity
= &arg_identity_extra
;
4910 else if (streq(optarg
, "this"))
4911 match_identity
= &arg_identity_extra_this_machine
;
4912 else if (streq(optarg
, "other"))
4913 match_identity
= &arg_identity_extra_other_machines
;
4914 else if (streq(optarg
, "auto"))
4915 match_identity
= NULL
;
4917 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "--machine= argument not understood. Refusing.");
4921 match_identity
= &arg_identity_extra
;
4924 match_identity
= &arg_identity_extra_this_machine
;
4927 match_identity
= &arg_identity_extra_other_machines
;
4934 assert_not_reached();
4938 if (!strv_isempty(arg_pkcs11_token_uri
) || !strv_isempty(arg_fido2_device
))
4939 arg_and_change_password
= true;
4941 if (arg_disk_size
!= UINT64_MAX
|| arg_disk_size_relative
!= UINT64_MAX
)
4942 arg_and_resize
= true;
4944 if (!strv_isempty(arg_languages
)) {
4947 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "preferredLanguage", arg_languages
[0]);
4949 return log_error_errno(r
, "Failed to update preferred language: %m");
4951 additional
= strv_skip(arg_languages
, 1);
4952 if (!strv_isempty(additional
)) {
4953 r
= sd_json_variant_set_field_strv(&arg_identity_extra
, "additionalLanguages", additional
);
4955 return log_error_errno(r
, "Failed to update additional language list: %m");
4957 r
= drop_from_identity("additionalLanguages");
4966 static int redirect_bus_mgr(void) {
4969 /* Talk to a different service if that's requested. (The same env var is also understood by homed, so
4970 * that it is relatively easily possible to invoke a second instance of homed for debug purposes and
4971 * have homectl talk to it, without colliding with the host version. This is handy when operating
4972 * from a homed-managed account.) */
4974 suffix
= getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
4976 static BusLocator locator
= {
4977 .path
= "/org/freedesktop/home1",
4978 .interface
= "org.freedesktop.home1.Manager",
4981 /* Yes, we leak this memory, but there's little point to collect this, given that we only do
4982 * this in a debug environment, do it only once, and the string shall live for out entire
4983 * process runtime. */
4985 locator
.destination
= strjoin("org.freedesktop.home1.", suffix
);
4986 if (!locator
.destination
)
4991 bus_mgr
= bus_home_mgr
;
4996 static bool is_fallback_shell(const char *p
) {
5003 /* Skip over login shell dash */
5006 if (streq(p
, "ystemd-home-fallback-shell")) /* maybe the dash was used to override the binary name? */
5010 q
= strrchr(p
, '/'); /* Skip over path */
5014 return streq(p
, "systemd-home-fallback-shell");
5017 static int fallback_shell(int argc
, char *argv
[]) {
5018 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
, *hr
= NULL
;
5019 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5020 _cleanup_strv_free_
char **l
= NULL
;
5021 _cleanup_free_
char *argv0
= NULL
;
5022 const char *json
, *hd
, *shell
;
5025 /* So here's the deal: if users log into a system via ssh, and their homed-managed home directory
5026 * wasn't activated yet, SSH will permit the access but the home directory isn't actually available
5027 * yet. SSH doesn't allow us to ask authentication questions from the PAM session stack, and doesn't
5028 * run the PAM authentication stack (because it authenticates via its own key management, after
5029 * all). So here's our way to support this: homectl can be invoked as a multi-call binary under the
5030 * name "systemd-home-fallback-shell". If so, it will chainload a login shell, but first try to
5031 * unlock the home directory of the user it is invoked as. systemd-homed will then override the shell
5032 * listed in user records whose home directory is not activated yet with this pseudo-shell. Net
5033 * effect: one SSH auth succeeds this pseudo shell gets invoked, which will unlock the homedir
5034 * (possibly asking for a passphrase) and then chainload the regular shell. Once the login is
5035 * complete the user record will look like any other. */
5037 r
= acquire_bus(&bus
);
5041 for (unsigned n_tries
= 0;; n_tries
++) {
5042 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5043 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
5044 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
5047 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
5048 "Failed to activate home dir, even after %u tries.", n_tries
);
5050 /* Let's start by checking if this all is even necessary, i.e. if the useFallback boolean field is actually set. */
5051 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByName", &error
, &reply
, "s", NULL
); /* empty user string means: our calling user */
5053 return log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
5055 r
= sd_bus_message_read(reply
, "sbo", &json
, NULL
, NULL
);
5057 return bus_log_parse_error(r
);
5059 r
= sd_json_parse(json
, SD_JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
5061 return log_error_errno(r
, "Failed to parse JSON identity: %m");
5063 hr
= user_record_new();
5067 r
= user_record_load(hr
, v
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_LOG
|USER_RECORD_PERMISSIVE
);
5071 if (!hr
->use_fallback
) /* Nice! We are done, fallback logic not necessary */
5075 r
= acquire_passed_secrets(hr
->user_name
, &secret
);
5081 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
5083 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ActivateHomeIfReferenced");
5085 return bus_log_create_error(r
);
5087 r
= sd_bus_message_append(m
, "s", NULL
); /* empty user string means: our calling user */
5089 return bus_log_create_error(r
);
5091 r
= bus_message_append_secret(m
, secret
);
5093 return bus_log_create_error(r
);
5095 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
5097 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_NOT_REFERENCED
))
5098 return log_error_errno(r
, "Called without reference on home taken, can't operate.");
5100 r
= handle_generic_user_record_error(hr
->user_name
, secret
, &error
, r
, false);
5104 sd_bus_error_free(&error
);
5110 hr
= user_record_unref(hr
);
5113 incomplete
= getenv_bool("XDG_SESSION_INCOMPLETE"); /* pam_systemd_home reports this state via an environment variable to us. */
5114 if (incomplete
< 0 && incomplete
!= -ENXIO
)
5115 return log_error_errno(incomplete
, "Failed to parse $XDG_SESSION_INCOMPLETE environment variable: %m");
5116 if (incomplete
> 0) {
5117 /* We are still in an "incomplete" session here. Now upgrade it to a full one. This will make logind
5118 * start the user@.service instance for us. */
5119 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5120 r
= sd_bus_call_method(
5122 "org.freedesktop.login1",
5123 "/org/freedesktop/login1/session/self",
5124 "org.freedesktop.login1.Session",
5127 /* ret_reply= */ NULL
,
5131 return log_error_errno(r
, "Failed to upgrade session: %s", bus_error_message(&error
, r
));
5133 if (setenv("XDG_SESSION_CLASS", "user", /* overwrite= */ true) < 0) /* Update the XDG_SESSION_CLASS environment variable to match the above */
5134 return log_error_errno(errno
, "Failed to set $XDG_SESSION_CLASS: %m");
5136 if (unsetenv("XDG_SESSION_INCOMPLETE") < 0) /* Unset the 'incomplete' env var */
5137 return log_error_errno(errno
, "Failed to unset $XDG_SESSION_INCOMPLETE: %m");
5140 /* We are going to invoke execv() soon. Let's be extra accurate and flush/close our bus connection
5141 * first, just to make sure anything queued is flushed out (though there shouldn't be anything) */
5142 bus
= sd_bus_flush_close_unref(bus
);
5144 assert(!hr
->use_fallback
);
5145 assert_se(shell
= user_record_shell(hr
));
5146 assert_se(hd
= user_record_home_directory(hr
));
5148 /* Extra protection: avoid loops */
5149 if (is_fallback_shell(shell
))
5150 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Primary shell of '%s' is fallback shell, refusing loop.", hr
->user_name
);
5153 return log_error_errno(errno
, "Failed to change directory to home directory '%s': %m", hd
);
5155 if (setenv("SHELL", shell
, /* overwrite= */ true) < 0)
5156 return log_error_errno(errno
, "Failed to set $SHELL: %m");
5158 if (setenv("HOME", hd
, /* overwrite= */ true) < 0)
5159 return log_error_errno(errno
, "Failed to set $HOME: %m");
5161 /* Paranoia: in case the client passed some passwords to us to help us unlock, unlock things now */
5162 FOREACH_STRING(ue
, "PASSWORD", "NEWPASSWORD", "PIN")
5163 if (unsetenv(ue
) < 0)
5164 return log_error_errno(errno
, "Failed to unset $%s: %m", ue
);
5166 r
= path_extract_filename(shell
, &argv0
);
5168 return log_error_errno(r
, "Unable to extract file name from '%s': %m", shell
);
5169 if (r
== O_DIRECTORY
)
5170 return log_error_errno(SYNTHETIC_ERRNO(EISDIR
), "Shell '%s' is a path to a directory, refusing.", shell
);
5172 /* Invoke this as login shell, by setting argv[0][0] to '-' (unless we ourselves weren't called as login shell) */
5173 if (!argv
|| isempty(argv
[0]) || argv
[0][0] == '-') {
5174 _cleanup_free_
char *prefixed
= strjoin("-", argv0
);
5178 free_and_replace(argv0
, prefixed
);
5181 l
= strv_new(argv0
);
5185 if (strv_extend_strv(&l
, strv_skip(argv
, 1), /* filter_duplicates= */ false) < 0)
5189 return log_error_errno(errno
, "Failed to execute shell '%s': %m", shell
);
5192 static int verb_list_signing_keys(int argc
, char *argv
[], void *userdata
) {
5195 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5196 r
= acquire_bus(&bus
);
5200 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5201 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
5202 r
= bus_call_method(bus
, bus_mgr
, "ListSigningKeys", &error
, &reply
, NULL
);
5204 return log_error_errno(r
, "Failed to list signing keys: %s", bus_error_message(&error
, r
));
5206 _cleanup_(table_unrefp
) Table
*table
= table_new("name", "key");
5210 r
= sd_bus_message_enter_container(reply
, 'a', "(sst)");
5212 return bus_log_parse_error(r
);
5215 const char *name
, *pem
;
5217 r
= sd_bus_message_read(reply
, "(sst)", &name
, &pem
, NULL
);
5219 return bus_log_parse_error(r
);
5223 _cleanup_free_
char *h
= NULL
;
5224 if (!sd_json_format_enabled(arg_json_format_flags
)) {
5225 /* Let's decode the PEM key to DER (so that we lose prefix/suffix), then truncate it
5226 * for display reasons. */
5228 _cleanup_(EVP_PKEY_freep
) EVP_PKEY
*key
= NULL
;
5229 r
= openssl_pubkey_from_pem(pem
, SIZE_MAX
, &key
);
5231 return log_error_errno(r
, "Failed to parse PEM: %m");
5233 _cleanup_free_
void *der
= NULL
;
5234 int n
= i2d_PUBKEY(key
, (unsigned char**) &der
);
5236 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to encode key as DER.");
5238 ssize_t m
= base64mem(der
, MIN(n
, 64), &h
);
5241 if (n
> 64) /* check if we truncated the original version */
5242 if (!strextend(&h
, glyph(GLYPH_ELLIPSIS
)))
5249 TABLE_STRING
, h
?: pem
);
5251 return table_log_add_error(r
);
5254 r
= sd_bus_message_exit_container(reply
);
5256 return bus_log_parse_error(r
);
5258 if (!table_isempty(table
) || sd_json_format_enabled(arg_json_format_flags
)) {
5259 r
= table_set_sort(table
, (size_t) 0);
5261 return table_log_sort_error(r
);
5263 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, arg_legend
);
5268 if (arg_legend
&& !sd_json_format_enabled(arg_json_format_flags
)) {
5269 if (table_isempty(table
))
5270 printf("No signing keys.\n");
5272 printf("\n%zu signing keys listed.\n", table_get_rows(table
) - 1);
5278 static int verb_get_signing_key(int argc
, char *argv
[], void *userdata
) {
5281 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5282 r
= acquire_bus(&bus
);
5286 char **keys
= argc
>= 2 ? strv_skip(argv
, 1) : STRV_MAKE("local.public");
5288 STRV_FOREACH(k
, keys
) {
5289 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5290 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
5291 r
= bus_call_method(bus
, bus_mgr
, "GetSigningKey", &error
, &reply
, "s", *k
);
5293 RET_GATHER(ret
, log_error_errno(r
, "Failed to get signing key '%s': %s", *k
, bus_error_message(&error
, r
)));
5298 r
= sd_bus_message_read(reply
, "st", &pem
, NULL
);
5300 RET_GATHER(ret
, bus_log_parse_error(r
));
5305 if (!endswith(pem
, "\n"))
5306 fputc('\n', stdout
);
5314 static int add_signing_key_one(sd_bus
*bus
, const char *fn
, FILE *key
) {
5321 _cleanup_free_
char *pem
= NULL
;
5322 r
= read_full_stream(key
, &pem
, /* ret_size= */ NULL
);
5324 return log_error_errno(r
, "Failed to read key '%s': %m", fn
);
5326 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5327 r
= bus_call_method(bus
, bus_mgr
, "AddSigningKey", &error
, /* ret_reply= */ NULL
, "sst", fn
, pem
, UINT64_C(0));
5329 return log_error_errno(r
, "Failed to add signing key '%s': %s", fn
, bus_error_message(&error
, r
));
5334 static int verb_add_signing_key(int argc
, char *argv
[], void *userdata
) {
5337 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5338 r
= acquire_bus(&bus
);
5342 int ret
= EXIT_SUCCESS
;
5343 if (argc
< 2 || streq(argv
[1], "-")) {
5345 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Key name must be specified via --key-name= when reading key from standard input, refusing.");
5347 RET_GATHER(ret
, add_signing_key_one(bus
, arg_key_name
, stdin
));
5349 /* Refuse if more han one key is specified in combination with --key-name= */
5350 if (argc
>= 3 && arg_key_name
)
5351 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "--key-name= is not supported if multiple signing keys are specified, refusing.");
5353 STRV_FOREACH(k
, strv_skip(argv
, 1)) {
5356 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Refusing to read from standard input if multiple keys are specified.");
5358 _cleanup_free_
char *fn
= NULL
;
5359 if (!arg_key_name
) {
5360 r
= path_extract_filename(*k
, &fn
);
5362 RET_GATHER(ret
, log_error_errno(r
, "Failed to extract filename from path '%s': %m", *k
));
5367 _cleanup_fclose_
FILE *f
= fopen(*k
, "re");
5369 RET_GATHER(ret
, log_error_errno(errno
, "Failed to open '%s': %m", *k
));
5373 RET_GATHER(ret
, add_signing_key_one(bus
, fn
?: arg_key_name
, f
));
5380 static int add_signing_keys_from_credentials(void) {
5383 _cleanup_close_
int fd
= open_credentials_dir();
5384 if (IN_SET(fd
, -ENXIO
, -ENOENT
)) /* Credential env var not set, or dir doesn't exist. */
5387 return log_error_errno(fd
, "Failed to open credentials directory: %m");
5389 _cleanup_free_ DirectoryEntries
*des
= NULL
;
5390 r
= readdir_all(fd
, RECURSE_DIR_SORT
|RECURSE_DIR_IGNORE_DOT
|RECURSE_DIR_ENSURE_TYPE
, &des
);
5392 return log_error_errno(r
, "Failed to enumerate credentials: %m");
5395 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5396 FOREACH_ARRAY(i
, des
->entries
, des
->n_entries
) {
5397 struct dirent
*de
= *i
;
5398 if (de
->d_type
!= DT_REG
)
5401 const char *e
= startswith(de
->d_name
, "home.add-signing-key.");
5405 if (!filename_is_valid(e
))
5409 r
= acquire_bus(&bus
);
5414 _cleanup_fclose_
FILE *f
= NULL
;
5415 r
= xfopenat(fd
, de
->d_name
, "re", O_NOFOLLOW
, &f
);
5417 RET_GATHER(ret
, log_error_errno(r
, "Failed to open credential '%s': %m", de
->d_name
));
5421 RET_GATHER(ret
, add_signing_key_one(bus
, e
, f
));
5427 static int remove_signing_key_one(sd_bus
*bus
, const char *fn
) {
5433 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5434 r
= bus_call_method(bus
, bus_mgr
, "RemoveSigningKey", &error
, /* ret_reply= */ NULL
, "st", fn
, UINT64_C(0));
5436 return log_error_errno(r
, "Failed to remove signing key '%s': %s", fn
, bus_error_message(&error
, r
));
5441 static int verb_remove_signing_key(int argc
, char *argv
[], void *userdata
) {
5444 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5445 r
= acquire_bus(&bus
);
5450 STRV_FOREACH(k
, strv_skip(argv
, 1))
5451 RET_GATHER(r
, remove_signing_key_one(bus
, *k
));
5456 static int run(int argc
, char *argv
[]) {
5457 static const Verb verbs
[] = {
5458 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
5459 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_homes
},
5460 { "activate", 2, VERB_ANY
, 0, activate_home
},
5461 { "deactivate", 2, VERB_ANY
, 0, deactivate_home
},
5462 { "inspect", VERB_ANY
, VERB_ANY
, 0, inspect_homes
},
5463 { "authenticate", VERB_ANY
, VERB_ANY
, 0, authenticate_homes
},
5464 { "create", VERB_ANY
, 2, 0, create_home
},
5465 { "adopt", VERB_ANY
, VERB_ANY
, 0, verb_adopt_home
},
5466 { "register", VERB_ANY
, VERB_ANY
, 0, verb_register_home
},
5467 { "unregister", 2, VERB_ANY
, 0, verb_unregister_home
},
5468 { "remove", 2, VERB_ANY
, 0, remove_home
},
5469 { "update", VERB_ANY
, 2, 0, update_home
},
5470 { "passwd", VERB_ANY
, 2, 0, passwd_home
},
5471 { "resize", 2, 3, 0, resize_home
},
5472 { "lock", 2, VERB_ANY
, 0, lock_home
},
5473 { "unlock", 2, VERB_ANY
, 0, unlock_home
},
5474 { "with", 2, VERB_ANY
, 0, with_home
},
5475 { "lock-all", VERB_ANY
, 1, 0, lock_all_homes
},
5476 { "deactivate-all", VERB_ANY
, 1, 0, deactivate_all_homes
},
5477 { "rebalance", VERB_ANY
, 1, 0, rebalance
},
5478 { "firstboot", VERB_ANY
, 1, 0, verb_firstboot
},
5479 { "list-signing-keys", VERB_ANY
, 1, 0, verb_list_signing_keys
},
5480 { "get-signing-key", VERB_ANY
, VERB_ANY
, 0, verb_get_signing_key
},
5481 { "add-signing-key", VERB_ANY
, VERB_ANY
, 0, verb_add_signing_key
},
5482 { "remove-signing-key", 2, VERB_ANY
, 0, verb_remove_signing_key
},
5490 r
= redirect_bus_mgr();
5494 if (is_fallback_shell(argv
[0]))
5495 return fallback_shell(argc
, argv
);
5497 r
= parse_argv(argc
, argv
);
5501 return dispatch_verb(argc
, argv
, verbs
, NULL
);
5504 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);