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"
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 the user supplied a full record, then add in lastChange, but do not override. Otherwise always
1834 r
= update_last_change(&json
, arg_pkcs11_token_uri
|| arg_fido2_device
, !arg_identity
);
1839 sd_json_variant_dump(json
, SD_JSON_FORMAT_PRETTY
, NULL
, NULL
);
1841 hr
= user_record_new();
1845 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
);
1849 *ret
= TAKE_PTR(hr
);
1853 static int home_record_reset_human_interaction_permission(UserRecord
*hr
) {
1858 /* When we execute multiple operations one after the other, let's reset the permission to ask the
1859 * user each time, so that if interaction is necessary we will be told so again and thus can print a
1860 * nice message to the user, telling the user so. */
1862 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, -1);
1864 return log_error_errno(r
, "Failed to reset PKCS#11 protected authentication path permission flag: %m");
1866 r
= user_record_set_fido2_user_presence_permitted(hr
, -1);
1868 return log_error_errno(r
, "Failed to reset FIDO2 user presence permission flag: %m");
1870 r
= user_record_set_fido2_user_verification_permitted(hr
, -1);
1872 return log_error_errno(r
, "Failed to reset FIDO2 user verification permission flag: %m");
1877 static int update_home(int argc
, char *argv
[], void *userdata
) {
1878 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1879 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
, *secret
= NULL
;
1880 _cleanup_free_
char *buffer
= NULL
;
1881 _cleanup_hashmap_free_ Hashmap
*blobs
= NULL
;
1882 const char *username
;
1888 else if (!arg_identity
) {
1889 buffer
= getusername_malloc();
1897 r
= acquire_bus(&bus
);
1901 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1903 r
= acquire_updated_home_record(bus
, username
, &hr
);
1907 /* Add in all secrets we can acquire cheaply */
1908 r
= acquire_passed_secrets(username
, &secret
);
1912 r
= user_record_merge_secret(hr
, secret
);
1916 r
= acquire_merged_blob_dir(hr
, true, &blobs
);
1921 sd_json_variant_dump(hr
->json
, SD_JSON_FORMAT_COLOR_AUTO
|SD_JSON_FORMAT_PRETTY_AUTO
|SD_JSON_FORMAT_NEWLINE
, stderr
, /* prefix= */ NULL
);
1925 /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
1926 * authentication might be confusing. */
1928 if (arg_and_resize
|| arg_and_change_password
)
1929 log_info("Updating home directory.");
1932 flags
|= SD_HOMED_UPDATE_OFFLINE
;
1935 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1936 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1937 _cleanup_free_
char *formatted
= NULL
;
1939 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "UpdateHomeEx");
1941 return bus_log_create_error(r
);
1943 r
= sd_json_variant_format(hr
->json
, 0, &formatted
);
1945 return log_error_errno(r
, "Failed to format user record: %m");
1947 (void) sd_bus_message_sensitive(m
);
1949 r
= sd_bus_message_append(m
, "s", formatted
);
1951 return bus_log_create_error(r
);
1953 r
= bus_message_append_blobs(m
, blobs
);
1955 return bus_log_create_error(r
);
1957 r
= sd_bus_message_append(m
, "t", flags
);
1959 return bus_log_create_error(r
);
1961 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1963 if (arg_and_change_password
&&
1964 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1965 /* In the generic handler we'd ask for a password in this case, but when
1966 * changing passwords that's not sufficient, as we need to acquire all keys
1968 return log_error_errno(r
, "Security token not inserted, refusing.");
1970 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1978 log_info("Resizing home.");
1980 (void) home_record_reset_human_interaction_permission(hr
);
1982 /* Also sync down disk size to underlying LUKS/fscrypt/quota */
1983 while (arg_and_resize
) {
1984 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1985 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1987 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ResizeHome");
1989 return bus_log_create_error(r
);
1991 /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
1992 r
= sd_bus_message_append(m
, "st", hr
->user_name
, UINT64_MAX
);
1994 return bus_log_create_error(r
);
1996 r
= bus_message_append_secret(m
, hr
);
1998 return bus_log_create_error(r
);
2000 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2002 if (arg_and_change_password
&&
2003 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
2004 return log_error_errno(r
, "Security token not inserted, refusing.");
2006 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
2013 if (arg_and_change_password
)
2014 log_info("Synchronizing passwords and encryption keys.");
2016 (void) home_record_reset_human_interaction_permission(hr
);
2018 /* Also sync down passwords to underlying LUKS/fscrypt */
2019 while (arg_and_change_password
) {
2020 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2021 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2023 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ChangePasswordHome");
2025 return bus_log_create_error(r
);
2027 /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
2028 r
= sd_bus_message_append(m
, "ss", hr
->user_name
, "{}");
2030 return bus_log_create_error(r
);
2032 r
= bus_message_append_secret(m
, hr
);
2034 return bus_log_create_error(r
);
2036 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2038 if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
2039 return log_error_errno(r
, "Security token not inserted, refusing.");
2041 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
2051 static int passwd_home(int argc
, char *argv
[], void *userdata
) {
2052 _cleanup_(user_record_unrefp
) UserRecord
*old_secret
= NULL
, *new_secret
= NULL
;
2053 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2054 _cleanup_free_
char *buffer
= NULL
;
2055 const char *username
;
2058 if (arg_pkcs11_token_uri
)
2059 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2060 "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=%s'.",
2061 glyph(GLYPH_ELLIPSIS
));
2062 if (arg_fido2_device
)
2063 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2064 "To change the FIDO2 security token use 'homectl update --fido2-device=%s'.",
2065 glyph(GLYPH_ELLIPSIS
));
2066 if (identity_properties_specified())
2067 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "The 'passwd' verb does not permit changing other record properties at the same time.");
2072 buffer
= getusername_malloc();
2079 r
= acquire_bus(&bus
);
2083 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2085 r
= acquire_passed_secrets(username
, &old_secret
);
2089 new_secret
= user_record_new();
2093 r
= acquire_new_password(username
, new_secret
, /* suggest = */ true, NULL
);
2098 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2099 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2101 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ChangePasswordHome");
2103 return bus_log_create_error(r
);
2105 r
= sd_bus_message_append(m
, "s", username
);
2107 return bus_log_create_error(r
);
2109 r
= bus_message_append_secret(m
, new_secret
);
2111 return bus_log_create_error(r
);
2113 r
= bus_message_append_secret(m
, old_secret
);
2115 return bus_log_create_error(r
);
2117 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2119 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
2121 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
2123 r
= acquire_new_password(username
, new_secret
, /* suggest = */ false, NULL
);
2125 } else if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
2127 /* In the generic handler we'd ask for a password in this case, but when
2128 * changing passwords that's not sufficeint, as we need to acquire all keys
2130 return log_error_errno(r
, "Security token not inserted, refusing.");
2132 r
= handle_generic_user_record_error(username
, old_secret
, &error
, r
, true);
2142 static int parse_disk_size(const char *t
, uint64_t *ret
) {
2148 if (streq(t
, "min"))
2150 else if (streq(t
, "max"))
2151 *ret
= UINT64_MAX
-1; /* Largest size that isn't UINT64_MAX special marker */
2155 r
= parse_size(t
, 1024, &ds
);
2157 return log_error_errno(r
, "Failed to parse disk size parameter: %s", t
);
2159 if (ds
>= UINT64_MAX
) /* UINT64_MAX has special meaning for us ("dont change"), refuse */
2160 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
), "Disk size out of range: %s", t
);
2168 static int resize_home(int argc
, char *argv
[], void *userdata
) {
2169 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2170 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
2171 uint64_t ds
= UINT64_MAX
;
2174 r
= acquire_bus(&bus
);
2178 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2180 if (arg_disk_size_relative
!= UINT64_MAX
||
2181 (argc
> 2 && parse_permyriad(argv
[2]) >= 0))
2182 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
2183 "Relative disk size specification currently not supported when resizing.");
2186 r
= parse_disk_size(argv
[2], &ds
);
2191 if (arg_disk_size
!= UINT64_MAX
) {
2192 if (ds
!= UINT64_MAX
&& ds
!= arg_disk_size
)
2193 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Disk size specified twice and doesn't match, refusing.");
2198 r
= acquire_passed_secrets(argv
[1], &secret
);
2203 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2204 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2206 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ResizeHome");
2208 return bus_log_create_error(r
);
2210 r
= sd_bus_message_append(m
, "st", argv
[1], ds
);
2212 return bus_log_create_error(r
);
2214 r
= bus_message_append_secret(m
, secret
);
2216 return bus_log_create_error(r
);
2218 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2220 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2230 static int lock_home(int argc
, char *argv
[], void *userdata
) {
2231 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2234 r
= acquire_bus(&bus
);
2238 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
2239 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2240 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2242 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "LockHome");
2244 return bus_log_create_error(r
);
2246 r
= sd_bus_message_append(m
, "s", *i
);
2248 return bus_log_create_error(r
);
2250 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2252 log_error_errno(r
, "Failed to lock home: %s", bus_error_message(&error
, r
));
2261 static int unlock_home(int argc
, char *argv
[], void *userdata
) {
2262 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2265 r
= acquire_bus(&bus
);
2269 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
2270 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
2272 r
= acquire_passed_secrets(*i
, &secret
);
2277 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2278 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2280 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "UnlockHome");
2282 return bus_log_create_error(r
);
2284 r
= sd_bus_message_append(m
, "s", *i
);
2286 return bus_log_create_error(r
);
2288 r
= bus_message_append_secret(m
, secret
);
2290 return bus_log_create_error(r
);
2292 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2294 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2309 static int with_home(int argc
, char *argv
[], void *userdata
) {
2310 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2311 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
2312 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2313 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
2314 _cleanup_close_
int acquired_fd
= -EBADF
;
2315 _cleanup_strv_free_
char **cmdline
= NULL
;
2320 r
= acquire_bus(&bus
);
2325 _cleanup_free_
char *shell
= NULL
;
2327 /* If no command is specified, spawn a shell */
2328 r
= get_shell(&shell
);
2330 return log_error_errno(r
, "Failed to acquire shell: %m");
2332 cmdline
= strv_new(shell
);
2334 cmdline
= strv_copy(argv
+ 2);
2338 r
= acquire_passed_secrets(argv
[1], &secret
);
2343 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "AcquireHome");
2345 return bus_log_create_error(r
);
2347 r
= sd_bus_message_append(m
, "s", argv
[1]);
2349 return bus_log_create_error(r
);
2351 r
= bus_message_append_secret(m
, secret
);
2353 return bus_log_create_error(r
);
2355 r
= sd_bus_message_append(m
, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
2357 return bus_log_create_error(r
);
2359 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
2360 m
= sd_bus_message_unref(m
);
2362 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2366 sd_bus_error_free(&error
);
2370 r
= sd_bus_message_read(reply
, "h", &fd
);
2372 return bus_log_parse_error(r
);
2374 acquired_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
2375 if (acquired_fd
< 0)
2376 return log_error_errno(errno
, "Failed to duplicate acquired fd: %m");
2378 reply
= sd_bus_message_unref(reply
);
2383 r
= bus_call_method(bus
, bus_mgr
, "GetHomeByName", &error
, &reply
, "s", argv
[1]);
2385 return log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
2387 r
= sd_bus_message_read(reply
, "usussso", NULL
, NULL
, NULL
, NULL
, &home
, NULL
, NULL
);
2389 return bus_log_parse_error(r
);
2391 r
= safe_fork("(with)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG_SIGTERM
|FORK_LOG
|FORK_RLIMIT_NOFILE_SAFE
|FORK_REOPEN_LOG
, &pid
);
2395 if (chdir(home
) < 0) {
2396 log_error_errno(errno
, "Failed to change to directory %s: %m", home
);
2400 execvp(cmdline
[0], cmdline
);
2401 log_error_errno(errno
, "Failed to execute %s: %m", cmdline
[0]);
2405 ret
= wait_for_terminate_and_check(cmdline
[0], pid
, WAIT_LOG_ABNORMAL
);
2407 /* Close the fd that pings the home now. */
2408 acquired_fd
= safe_close(acquired_fd
);
2410 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ReleaseHome");
2412 return bus_log_create_error(r
);
2414 r
= sd_bus_message_append(m
, "s", argv
[1]);
2416 return bus_log_create_error(r
);
2418 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2420 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_BUSY
))
2421 log_notice("Not deactivating home directory of %s, as it is still used.", argv
[1]);
2423 return log_error_errno(r
, "Failed to release user home: %s", bus_error_message(&error
, r
));
2429 static int lock_all_homes(int argc
, char *argv
[], void *userdata
) {
2430 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2431 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2432 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2435 r
= acquire_bus(&bus
);
2439 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "LockAllHomes");
2441 return bus_log_create_error(r
);
2443 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2445 return log_error_errno(r
, "Failed to lock all homes: %s", bus_error_message(&error
, r
));
2450 static int deactivate_all_homes(int argc
, char *argv
[], void *userdata
) {
2451 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2452 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2453 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2456 r
= acquire_bus(&bus
);
2460 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "DeactivateAllHomes");
2462 return bus_log_create_error(r
);
2464 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2466 return log_error_errno(r
, "Failed to deactivate all homes: %s", bus_error_message(&error
, r
));
2471 static int rebalance(int argc
, char *argv
[], void *userdata
) {
2472 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2473 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2474 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2477 r
= acquire_bus(&bus
);
2481 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "Rebalance");
2483 return bus_log_create_error(r
);
2485 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2487 if (sd_bus_error_has_name(&error
, BUS_ERROR_REBALANCE_NOT_NEEDED
))
2488 log_info("No homes needed rebalancing.");
2490 return log_error_errno(r
, "Failed to rebalance: %s", bus_error_message(&error
, r
));
2492 log_info("Completed rebalancing.");
2497 static int create_or_register_from_credentials(void) {
2500 _cleanup_close_
int fd
= open_credentials_dir();
2501 if (IN_SET(fd
, -ENXIO
, -ENOENT
)) /* Credential env var not set, or dir doesn't exist. */
2504 return log_error_errno(fd
, "Failed to open credentials directory: %m");
2506 _cleanup_free_ DirectoryEntries
*des
= NULL
;
2507 r
= readdir_all(fd
, RECURSE_DIR_SORT
|RECURSE_DIR_IGNORE_DOT
|RECURSE_DIR_ENSURE_TYPE
, &des
);
2509 return log_error_errno(r
, "Failed to enumerate credentials: %m");
2511 int ret
= 0, n_processed
= 0;
2512 FOREACH_ARRAY(i
, des
->entries
, des
->n_entries
) {
2513 struct dirent
*de
= *i
;
2514 if (de
->d_type
!= DT_REG
)
2522 if ((e
= startswith(de
->d_name
, "home.create.")))
2523 op
= OPERATION_CREATE
;
2524 else if ((e
= startswith(de
->d_name
, "home.register.")))
2525 op
= OPERATION_REGISTER
;
2529 if (!valid_user_group_name(e
, 0)) {
2530 log_notice("Skipping over credential with name that is not a suitable user name: %s", de
->d_name
);
2534 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*identity
= NULL
;
2535 unsigned line
= 0, column
= 0;
2536 r
= sd_json_parse_file_at(
2545 log_warning_errno(r
, "[%s:%u:%u] Failed to parse user record in credential, ignoring: %m", de
->d_name
, line
, column
);
2549 sd_json_variant
*un
= sd_json_variant_by_key(identity
, "userName");
2551 if (!sd_json_variant_is_string(un
)) {
2552 log_warning("User record from credential '%s' contains 'userName' field of invalid type, ignoring.", de
->d_name
);
2556 if (!streq(sd_json_variant_string(un
), e
)) {
2557 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
);
2561 r
= sd_json_variant_set_field_string(&identity
, "userName", e
);
2563 return log_warning_errno(r
, "Failed to set userName field: %m");
2566 log_notice("Processing user '%s' from credentials.", e
);
2568 if (op
== OPERATION_CREATE
)
2569 r
= create_home_common(identity
, /* show_enforce_password_policy_hint= */ false);
2571 r
= register_home_common(/* bus= */ NULL
, identity
);
2578 return ret
< 0 ? ret
: n_processed
;
2581 static int has_regular_user(void) {
2582 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
2583 UserDBMatch match
= USERDB_MATCH_NULL
;
2586 match
.disposition_mask
= INDEX_TO_MASK(uint64_t, USER_REGULAR
);
2588 r
= userdb_all(&match
, USERDB_SUPPRESS_SHADOW
, &iterator
);
2590 return log_error_errno(r
, "Failed to create user enumerator: %m");
2592 r
= userdb_iterator_get(iterator
, &match
, /* ret= */ NULL
);
2596 return log_error_errno(r
, "Failed to enumerate users: %m");
2601 static int acquire_group_list(char ***ret
) {
2602 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
2603 _cleanup_strv_free_
char **groups
= NULL
;
2604 UserDBMatch match
= USERDB_MATCH_NULL
;
2609 match
.disposition_mask
= INDEXES_TO_MASK(uint64_t, USER_REGULAR
, USER_SYSTEM
);
2611 r
= groupdb_all(&match
, USERDB_SUPPRESS_SHADOW
, &iterator
);
2613 log_debug_errno(r
, "No groups found. (Didn't check via Varlink.)");
2614 else if (r
== -ESRCH
)
2615 log_debug_errno(r
, "No groups found.");
2617 return log_debug_errno(r
, "Failed to enumerate groups, ignoring: %m");
2620 _cleanup_(group_record_unrefp
) GroupRecord
*gr
= NULL
;
2622 r
= groupdb_iterator_get(iterator
, &match
, &gr
);
2626 return log_debug_errno(r
, "Failed acquire next group: %m");
2628 if (group_record_disposition(gr
) == USER_REGULAR
) {
2629 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
2631 /* Filter groups here that belong to a specific user, and are named like them */
2633 UserDBMatch user_match
= USERDB_MATCH_NULL
;
2634 user_match
.disposition_mask
= INDEX_TO_MASK(uint64_t, USER_REGULAR
);
2636 r
= userdb_by_name(gr
->group_name
, &user_match
, USERDB_SUPPRESS_SHADOW
, &ur
);
2637 if (r
< 0 && r
!= -ESRCH
)
2638 return log_debug_errno(r
, "Failed to check if matching user exists for group '%s': %m", gr
->group_name
);
2640 if (r
>= 0 && user_record_gid(ur
) == gr
->gid
)
2644 r
= strv_extend(&groups
, gr
->group_name
);
2651 *ret
= TAKE_PTR(groups
);
2655 static int group_completion_callback(const char *key
, char ***ret_list
, void *userdata
) {
2656 char ***available
= userdata
;
2660 r
= acquire_group_list(available
);
2662 log_debug_errno(r
, "Failed to enumerate available groups, ignoring: %m");
2665 _cleanup_strv_free_
char **l
= strv_copy(*available
);
2669 r
= strv_extend(&l
, "list");
2673 *ret_list
= TAKE_PTR(l
);
2677 static int create_interactively(void) {
2678 _cleanup_free_
char *username
= NULL
;
2681 if (!arg_prompt_new_user
) {
2682 log_debug("Prompting for user creation was not requested.");
2687 if (emoji_enabled()) {
2688 fputs(glyph(GLYPH_HOME
), stdout
);
2691 printf("Please create your user account!\n");
2693 if (!any_key_to_proceed()) {
2694 log_notice("Skipping.");
2698 (void) terminal_reset_defensive_locked(STDOUT_FILENO
, /* flags= */ 0);
2701 username
= mfree(username
);
2703 r
= ask_string(&username
,
2704 "%s Please enter user name to create (empty to skip): ",
2705 glyph(GLYPH_TRIANGULAR_BULLET
));
2707 return log_error_errno(r
, "Failed to query user for username: %m");
2709 if (isempty(username
)) {
2710 log_info("No data entered, skipping.");
2714 if (!valid_user_group_name(username
, /* flags= */ 0)) {
2715 log_notice("Specified user name is not a valid UNIX user name, try again: %s", username
);
2719 r
= userdb_by_name(username
, /* match= */ NULL
, USERDB_SUPPRESS_SHADOW
, /* ret= */ NULL
);
2723 return log_error_errno(r
, "Failed to check if specified user '%s' already exists: %m", username
);
2725 log_notice("Specified user '%s' exists already, try again.", username
);
2728 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "userName", username
);
2730 return log_error_errno(r
, "Failed to set userName field: %m");
2732 /* Let's not insist on a strong password in the firstboot interactive interface. Insisting on this is
2733 * really annoying, as the user cannot just invoke the tool again with "--enforce-password-policy=no"
2734 * because after all the tool is called from the boot process, and not from an interactive
2735 * shell. Moreover, when setting up an initial system we can assume the user owns it, and hence we
2736 * don't need to hard enforce some policy on password strength some organization or OS vendor
2737 * requires. Note that this just disables the *strict* enforcement of the password policy. Even with
2738 * this disabled we'll still tell the user in the UI that the password is too weak and suggest better
2739 * ones, even if we then accept the weak ones if the user insists, by repeating it. */
2740 r
= sd_json_variant_set_field_boolean(&arg_identity_extra
, "enforcePasswordPolicy", false);
2742 return log_error_errno(r
, "Failed to set enforcePasswordPolicy field: %m");
2744 _cleanup_strv_free_
char **available
= NULL
, **groups
= NULL
;
2746 _cleanup_free_
char *s
= NULL
;
2748 strv_sort_uniq(groups
);
2750 if (!strv_isempty(groups
)) {
2751 _cleanup_free_
char *j
= strv_join(groups
, ", ");
2755 log_info("Currently selected groups: %s", j
);
2758 r
= ask_string_full(&s
,
2759 group_completion_callback
, &available
,
2760 "%s Please enter an auxiliary group for user %s (empty to continue, \"list\" to list available groups): ",
2761 glyph(GLYPH_TRIANGULAR_BULLET
), username
);
2763 return log_error_errno(r
, "Failed to query user for auxiliary group: %m");
2768 if (streq(s
, "list")) {
2770 r
= acquire_group_list(&available
);
2772 log_warning_errno(r
, "Failed to enumerate available groups, ignoring: %m");
2774 log_notice("Did not find any available groups");
2779 r
= show_menu(available
,
2781 /* column_width= */ 20,
2782 /* ellipsize_percentage= */ 60,
2783 /* grey_prefix= */ NULL
,
2784 /* with_numbers= */ true);
2786 return log_error_errno(r
, "Failed to show menu: %m");
2792 if (!strv_isempty(available
)) {
2794 r
= safe_atou(s
, &u
);
2796 if (u
<= 0 || u
> strv_length(available
)) {
2797 log_error("Specified entry number out of range.");
2801 log_info("Selected '%s'.", available
[u
-1]);
2803 r
= strv_extend(&groups
, available
[u
-1]);
2811 if (!valid_user_group_name(s
, /* flags= */ 0)) {
2812 log_notice("Specified group name is not a valid UNIX group name, try again: %s", s
);
2816 r
= groupdb_by_name(s
, /* match= */ NULL
, USERDB_SUPPRESS_SHADOW
|USERDB_EXCLUDE_DYNAMIC_USER
, /*ret=*/ NULL
);
2818 log_notice("Specified auxiliary group does not exist, try again: %s", s
);
2822 return log_error_errno(r
, "Failed to check if specified group '%s' already exists: %m", s
);
2824 log_info("Selected '%s'.", s
);
2826 r
= strv_extend(&groups
, s
);
2831 if (!strv_isempty(groups
)) {
2832 strv_sort_uniq(groups
);
2834 r
= sd_json_variant_set_field_strv(&arg_identity_extra
, "memberOf", groups
);
2836 return log_error_errno(r
, "Failed to set memberOf field: %m");
2839 _cleanup_free_
char *shell
= NULL
;
2842 shell
= mfree(shell
);
2844 r
= ask_string(&shell
,
2845 "%s Please enter the shell to use for user %s (empty for default): ",
2846 glyph(GLYPH_TRIANGULAR_BULLET
), username
);
2848 return log_error_errno(r
, "Failed to query user for username: %m");
2850 if (isempty(shell
)) {
2851 log_info("No data entered, leaving at default.");
2855 if (!valid_shell(shell
)) {
2856 log_notice("Specified shell is not a valid UNIX shell path, try again: %s", shell
);
2860 r
= RET_NERRNO(access(shell
, X_OK
));
2865 return log_error_errno(r
, "Failed to check if shell %s exists: %m", shell
);
2867 log_notice("Specified shell '%s' is not installed, try another one.", shell
);
2870 if (!isempty(shell
)) {
2871 log_info("Selected %s as the shell for user %s", shell
, username
);
2873 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "shell", shell
);
2875 return log_error_errno(r
, "Failed to set shell field: %m");
2878 return create_home_common(/* input= */ NULL
, /* show_enforce_password_policy_hint= */ false);
2881 static int add_signing_keys_from_credentials(void);
2883 static int verb_firstboot(int argc
, char *argv
[], void *userdata
) {
2886 /* Let's honour the systemd.firstboot kernel command line option, just like the systemd-firstboot
2890 r
= proc_cmdline_get_bool("systemd.firstboot", /* flags = */ 0, &enabled
);
2892 return log_error_errno(r
, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
2893 if (r
> 0 && !enabled
) {
2894 log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
2895 arg_prompt_new_user
= false;
2900 RET_GATHER(ret
, add_signing_keys_from_credentials());
2902 r
= create_or_register_from_credentials();
2904 bool existing_users
= r
> 0;
2906 r
= getenv_bool("SYSTEMD_HOME_FIRSTBOOT_OVERRIDE");
2911 log_warning_errno(r
, "Failed to parse $SYSTEMD_HOME_FIRSTBOOT_OVERRIDE, ignoring: %m");
2913 if (!existing_users
) {
2914 r
= has_regular_user();
2918 existing_users
= r
> 0;
2920 if (existing_users
) {
2921 log_info("Regular user already present in user database, skipping interactive user creation.");
2926 RET_GATHER(ret
, create_interactively());
2930 static int drop_from_identity(const char *field
) {
2935 /* If we are called to update an identity record and drop some field, let's keep track of what to
2936 * remove from the old record */
2937 r
= strv_extend(&arg_identity_filter
, field
);
2941 /* Let's also drop the field if it was previously set to a new value on the same command line */
2942 r
= sd_json_variant_filter(&arg_identity_extra
, STRV_MAKE(field
));
2944 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2946 r
= sd_json_variant_filter(&arg_identity_extra_this_machine
, STRV_MAKE(field
));
2948 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2950 r
= sd_json_variant_filter(&arg_identity_extra_privileged
, STRV_MAKE(field
));
2952 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2957 static int help(int argc
, char *argv
[], void *userdata
) {
2958 _cleanup_free_
char *link
= NULL
;
2961 pager_open(arg_pager_flags
);
2963 r
= terminal_urlify_man("homectl", "1", &link
);
2967 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
2968 "%2$sCreate, manipulate or inspect home directories.%3$s\n"
2969 "\n%4$sBasic User Manipulation Commands:%5$s\n"
2970 " list List home areas\n"
2971 " inspect USER… Inspect a home area\n"
2972 " create USER Create a home area\n"
2973 " update USER Update a home area\n"
2974 " passwd USER Change password of a home area\n"
2975 " resize USER SIZE Resize a home area\n"
2976 " remove USER… Remove a home area\n"
2977 "\n%4$sAdvanced User Manipulation Commands:%5$s\n"
2978 " activate USER… Activate a home area\n"
2979 " deactivate USER… Deactivate a home area\n"
2980 " deactivate-all Deactivate all active home areas\n"
2981 " with USER [COMMAND…] Run shell or command with access to a home area\n"
2982 " authenticate USER… Authenticate a home area\n"
2983 "\n%4$sUser Migration Commands:%5$s\n"
2984 " adopt PATH… Add an existing home area on this system\n"
2985 " register PATH… Register a user record locally\n"
2986 " unregister USER… Unregister a user record locally\n"
2987 "\n%4$sSigning Keys Commands:%5$s\n"
2988 " list-signing-keys List home signing keys\n"
2989 " get-signing-key [NAME…] Get a named home signing key\n"
2990 " add-signing-key FILE… Add home signing key\n"
2991 " remove-signing-key NAME… Remove home signing key\n"
2992 "\n%4$sLock/Unlock Commands:%5$s\n"
2993 " lock USER… Temporarily lock an active home area\n"
2994 " unlock USER… Unlock a temporarily locked home area\n"
2995 " lock-all Lock all suitable home areas\n"
2996 "\n%4$sOther Commands:%5$s\n"
2997 " rebalance Rebalance free space between home areas\n"
2998 " firstboot Run first-boot home area creation wizard\n"
2999 "\n%4$sOptions:%5$s\n"
3000 " -h --help Show this help\n"
3001 " --version Show package version\n"
3002 " --no-pager Do not pipe output into a pager\n"
3003 " --no-legend Do not show the headers and footers\n"
3004 " --no-ask-password Do not ask for system passwords\n"
3005 " --offline Don't update record embedded in home directory\n"
3006 " -H --host=[USER@]HOST Operate on remote host\n"
3007 " -M --machine=CONTAINER Operate on local container\n"
3008 " --identity=PATH Read JSON identity from file\n"
3009 " --json=FORMAT Output inspection data in JSON (takes one of\n"
3010 " pretty, short, off)\n"
3011 " -j Equivalent to --json=pretty (on TTY) or\n"
3012 " --json=short (otherwise)\n"
3013 " --export-format= Strip JSON inspection data (full, stripped,\n"
3015 " -E When specified once equals -j --export-format=\n"
3016 " stripped, when specified twice equals\n"
3017 " -j --export-format=minimal\n"
3018 " --prompt-new-user firstboot: Query user interactively for user\n"
3020 " --key-name=NAME Key name when adding a signing key\n"
3021 " --seize=no Do not strip existing signatures of user record\n"
3023 "\n%4$sGeneral User Record Properties:%5$s\n"
3024 " -c --real-name=REALNAME Real name for user\n"
3025 " --realm=REALM Realm to create user in\n"
3026 " --alias=ALIAS Define alias usernames for this account\n"
3027 " --email-address=EMAIL Email address for user\n"
3028 " --location=LOCATION Set location of user on earth\n"
3029 " --icon-name=NAME Icon name for user\n"
3030 " -d --home-dir=PATH Home directory\n"
3031 " -u --uid=UID Numeric UID for user\n"
3032 " -G --member-of=GROUP Add user to group\n"
3033 " --capability-bounding-set=CAPS\n"
3034 " Bounding POSIX capability set\n"
3035 " --capability-ambient-set=CAPS\n"
3036 " Ambient POSIX capability set\n"
3037 " --access-mode=MODE User home directory access mode\n"
3038 " --umask=MODE Umask for user when logging in\n"
3039 " --skel=PATH Skeleton directory to use\n"
3040 " --shell=PATH Shell for account\n"
3041 " --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n"
3042 " --timezone=TIMEZONE Set a time-zone\n"
3043 " --language=LOCALE Set preferred languages\n"
3044 " --default-area=AREA Select default area\n"
3045 "\n%4$sAuthentication User Record Properties:%5$s\n"
3046 " --ssh-authorized-keys=KEYS\n"
3047 " Specify SSH public keys\n"
3048 " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
3049 " private key and matching X.509 certificate\n"
3050 " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
3052 " --fido2-with-client-pin=BOOL\n"
3053 " Whether to require entering a PIN to unlock the\n"
3055 " --fido2-with-user-presence=BOOL\n"
3056 " Whether to require user presence to unlock the\n"
3058 " --fido2-with-user-verification=BOOL\n"
3059 " Whether to require user verification to unlock\n"
3061 " --recovery-key=BOOL Add a recovery key\n"
3062 "\n%4$sBlob Directory User Record Properties:%5$s\n"
3063 " -b --blob=[FILENAME=]PATH\n"
3064 " Path to a replacement blob directory, or replace\n"
3065 " an individual files in the blob directory.\n"
3066 " --avatar=PATH Path to user avatar picture\n"
3067 " --login-background=PATH Path to user login background picture\n"
3068 "\n%4$sAccount Management User Record Properties:%5$s\n"
3069 " --locked=BOOL Set locked account state\n"
3070 " --not-before=TIMESTAMP Do not allow logins before\n"
3071 " --not-after=TIMESTAMP Do not allow logins after\n"
3072 " --rate-limit-interval=SECS\n"
3073 " Login rate-limit interval in seconds\n"
3074 " --rate-limit-burst=NUMBER\n"
3075 " Login rate-limit attempts per interval\n"
3076 "\n%4$sPassword Policy User Record Properties:%5$s\n"
3077 " --password-hint=HINT Set Password hint\n"
3078 " --enforce-password-policy=BOOL\n"
3079 " Control whether to enforce system's password\n"
3080 " policy for this user\n"
3081 " -P Same as --enforce-password-policy=no\n"
3082 " --password-change-now=BOOL\n"
3083 " Require the password to be changed on next login\n"
3084 " --password-change-min=TIME\n"
3085 " Require minimum time between password changes\n"
3086 " --password-change-max=TIME\n"
3087 " Require maximum time between password changes\n"
3088 " --password-change-warn=TIME\n"
3089 " How much time to warn before password expiry\n"
3090 " --password-change-inactive=TIME\n"
3091 " How much time to block password after expiry\n"
3092 "\n%4$sResource Management User Record Properties:%5$s\n"
3093 " --disk-size=BYTES Size to assign the user on disk\n"
3094 " --nice=NICE Nice level for user\n"
3095 " --rlimit=LIMIT=VALUE[:VALUE]\n"
3096 " Set resource limits\n"
3097 " --tasks-max=MAX Set maximum number of per-user tasks\n"
3098 " --memory-high=BYTES Set high memory threshold in bytes\n"
3099 " --memory-max=BYTES Set maximum memory limit\n"
3100 " --cpu-weight=WEIGHT Set CPU weight\n"
3101 " --io-weight=WEIGHT Set IO weight\n"
3102 " --tmp-limit=BYTES|PERCENT Set limit on /tmp/\n"
3103 " --dev-shm-limit=BYTES|PERCENT\n"
3104 " Set limit on /dev/shm/\n"
3105 "\n%4$sStorage User Record Properties:%5$s\n"
3106 " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
3107 " subvolume, cifs)\n"
3108 " --image-path=PATH Path to image file/directory\n"
3109 " --drop-caches=BOOL Whether to automatically drop caches on logout\n"
3110 "\n%4$sLUKS Storage User Record Properties:%5$s\n"
3111 " --fs-type=TYPE File system type to use in case of luks\n"
3112 " storage (btrfs, ext4, xfs)\n"
3113 " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
3114 " when activated (mounted)\n"
3115 " --luks-offline-discard=BOOL\n"
3116 " Whether to trim file on logout\n"
3117 " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
3118 " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
3119 " --luks-volume-key-size=BITS\n"
3120 " Volume key size to use for LUKS encryption\n"
3121 " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
3122 " --luks-pbkdf-hash-algorithm=ALGORITHM\n"
3123 " PBKDF hash algorithm to use\n"
3124 " --luks-pbkdf-time-cost=SECS\n"
3125 " Time cost for PBKDF in seconds\n"
3126 " --luks-pbkdf-memory-cost=BYTES\n"
3127 " Memory cost for PBKDF in bytes\n"
3128 " --luks-pbkdf-parallel-threads=NUMBER\n"
3129 " Number of parallel threads for PKBDF\n"
3130 " --luks-sector-size=BYTES\n"
3131 " Sector size for LUKS encryption in bytes\n"
3132 " --luks-extra-mount-options=OPTIONS\n"
3133 " LUKS extra mount options\n"
3134 " --auto-resize-mode=MODE Automatically grow/shrink home on login/logout\n"
3135 " --rebalance-weight=WEIGHT Weight while rebalancing\n"
3136 "\n%4$sMounting User Record Properties:%5$s\n"
3137 " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
3138 " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
3139 " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
3140 "\n%4$sCIFS User Record Properties:%5$s\n"
3141 " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
3142 " --cifs-user-name=USER CIFS (Windows) user name\n"
3143 " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
3144 " --cifs-extra-mount-options=OPTIONS\n"
3145 " CIFS (Windows) extra mount options\n"
3146 "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
3147 " --stop-delay=SECS How long to leave user services running after\n"
3149 " --kill-processes=BOOL Whether to kill user processes when sessions\n"
3151 " --auto-login=BOOL Try to log this user in automatically\n"
3152 " --session-launcher=LAUNCHER\n"
3153 " Preferred session launcher file\n"
3154 " --session-type=TYPE Preferred session type\n"
3155 "\nSee the %6$s for details.\n",
3156 program_invocation_short_name
,
3166 static int parse_argv(int argc
, char *argv
[]) {
3167 _cleanup_strv_free_
char **arg_languages
= NULL
;
3170 ARG_VERSION
= 0x100,
3173 ARG_NO_ASK_PASSWORD
,
3185 ARG_LUKS_OFFLINE_DISCARD
,
3191 ARG_SSH_AUTHORIZED_KEYS
,
3200 ARG_LUKS_CIPHER_MODE
,
3201 ARG_LUKS_VOLUME_KEY_SIZE
,
3208 ARG_CIFS_EXTRA_MOUNT_OPTIONS
,
3214 ARG_LUKS_PBKDF_TYPE
,
3215 ARG_LUKS_PBKDF_HASH_ALGORITHM
,
3216 ARG_LUKS_PBKDF_FORCE_ITERATIONS
,
3217 ARG_LUKS_PBKDF_TIME_COST
,
3218 ARG_LUKS_PBKDF_MEMORY_COST
,
3219 ARG_LUKS_PBKDF_PARALLEL_THREADS
,
3220 ARG_LUKS_SECTOR_SIZE
,
3221 ARG_RATE_LIMIT_INTERVAL
,
3222 ARG_RATE_LIMIT_BURST
,
3225 ARG_ENFORCE_PASSWORD_POLICY
,
3226 ARG_PASSWORD_CHANGE_NOW
,
3227 ARG_PASSWORD_CHANGE_MIN
,
3228 ARG_PASSWORD_CHANGE_MAX
,
3229 ARG_PASSWORD_CHANGE_WARN
,
3230 ARG_PASSWORD_CHANGE_INACTIVE
,
3233 ARG_SESSION_LAUNCHER
,
3235 ARG_PKCS11_TOKEN_URI
,
3242 ARG_AND_CHANGE_PASSWORD
,
3244 ARG_LUKS_EXTRA_MOUNT_OPTIONS
,
3245 ARG_AUTO_RESIZE_MODE
,
3246 ARG_REBALANCE_WEIGHT
,
3248 ARG_CAPABILITY_BOUNDING_SET
,
3249 ARG_CAPABILITY_AMBIENT_SET
,
3250 ARG_PROMPT_NEW_USER
,
3252 ARG_LOGIN_BACKGROUND
,
3261 static const struct option options
[] = {
3262 { "help", no_argument
, NULL
, 'h' },
3263 { "version", no_argument
, NULL
, ARG_VERSION
},
3264 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
3265 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
3266 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
3267 { "offline", no_argument
, NULL
, ARG_OFFLINE
},
3268 { "host", required_argument
, NULL
, 'H' },
3269 { "machine", required_argument
, NULL
, 'M' },
3270 { "identity", required_argument
, NULL
, 'I' },
3271 { "real-name", required_argument
, NULL
, 'c' },
3272 { "comment", required_argument
, NULL
, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
3273 { "realm", required_argument
, NULL
, ARG_REALM
},
3274 { "alias", required_argument
, NULL
, ARG_ALIAS
},
3275 { "email-address", required_argument
, NULL
, ARG_EMAIL_ADDRESS
},
3276 { "location", required_argument
, NULL
, ARG_LOCATION
},
3277 { "password-hint", required_argument
, NULL
, ARG_PASSWORD_HINT
},
3278 { "icon-name", required_argument
, NULL
, ARG_ICON_NAME
},
3279 { "home-dir", required_argument
, NULL
, 'd' }, /* Compatible with useradd(8) */
3280 { "uid", required_argument
, NULL
, 'u' }, /* Compatible with useradd(8) */
3281 { "member-of", required_argument
, NULL
, 'G' },
3282 { "groups", required_argument
, NULL
, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
3283 { "skel", required_argument
, NULL
, 'k' }, /* Compatible with useradd(8) */
3284 { "shell", required_argument
, NULL
, 's' }, /* Compatible with useradd(8) */
3285 { "setenv", required_argument
, NULL
, ARG_SETENV
},
3286 { "timezone", required_argument
, NULL
, ARG_TIMEZONE
},
3287 { "language", required_argument
, NULL
, ARG_LANGUAGE
},
3288 { "locked", required_argument
, NULL
, ARG_LOCKED
},
3289 { "not-before", required_argument
, NULL
, ARG_NOT_BEFORE
},
3290 { "not-after", required_argument
, NULL
, ARG_NOT_AFTER
},
3291 { "expiredate", required_argument
, NULL
, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
3292 { "ssh-authorized-keys", required_argument
, NULL
, ARG_SSH_AUTHORIZED_KEYS
},
3293 { "disk-size", required_argument
, NULL
, ARG_DISK_SIZE
},
3294 { "access-mode", required_argument
, NULL
, ARG_ACCESS_MODE
},
3295 { "umask", required_argument
, NULL
, ARG_UMASK
},
3296 { "nice", required_argument
, NULL
, ARG_NICE
},
3297 { "rlimit", required_argument
, NULL
, ARG_RLIMIT
},
3298 { "tasks-max", required_argument
, NULL
, ARG_TASKS_MAX
},
3299 { "memory-high", required_argument
, NULL
, ARG_MEMORY_HIGH
},
3300 { "memory-max", required_argument
, NULL
, ARG_MEMORY_MAX
},
3301 { "cpu-weight", required_argument
, NULL
, ARG_CPU_WEIGHT
},
3302 { "io-weight", required_argument
, NULL
, ARG_IO_WEIGHT
},
3303 { "storage", required_argument
, NULL
, ARG_STORAGE
},
3304 { "image-path", required_argument
, NULL
, ARG_IMAGE_PATH
},
3305 { "fs-type", required_argument
, NULL
, ARG_FS_TYPE
},
3306 { "luks-discard", required_argument
, NULL
, ARG_LUKS_DISCARD
},
3307 { "luks-offline-discard", required_argument
, NULL
, ARG_LUKS_OFFLINE_DISCARD
},
3308 { "luks-cipher", required_argument
, NULL
, ARG_LUKS_CIPHER
},
3309 { "luks-cipher-mode", required_argument
, NULL
, ARG_LUKS_CIPHER_MODE
},
3310 { "luks-volume-key-size", required_argument
, NULL
, ARG_LUKS_VOLUME_KEY_SIZE
},
3311 { "luks-pbkdf-type", required_argument
, NULL
, ARG_LUKS_PBKDF_TYPE
},
3312 { "luks-pbkdf-hash-algorithm", required_argument
, NULL
, ARG_LUKS_PBKDF_HASH_ALGORITHM
},
3313 { "luks-pbkdf-force-iterations", required_argument
, NULL
, ARG_LUKS_PBKDF_FORCE_ITERATIONS
},
3314 { "luks-pbkdf-time-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_TIME_COST
},
3315 { "luks-pbkdf-memory-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_MEMORY_COST
},
3316 { "luks-pbkdf-parallel-threads", required_argument
, NULL
, ARG_LUKS_PBKDF_PARALLEL_THREADS
},
3317 { "luks-sector-size", required_argument
, NULL
, ARG_LUKS_SECTOR_SIZE
},
3318 { "nosuid", required_argument
, NULL
, ARG_NOSUID
},
3319 { "nodev", required_argument
, NULL
, ARG_NODEV
},
3320 { "noexec", required_argument
, NULL
, ARG_NOEXEC
},
3321 { "cifs-user-name", required_argument
, NULL
, ARG_CIFS_USER_NAME
},
3322 { "cifs-domain", required_argument
, NULL
, ARG_CIFS_DOMAIN
},
3323 { "cifs-service", required_argument
, NULL
, ARG_CIFS_SERVICE
},
3324 { "cifs-extra-mount-options", required_argument
, NULL
, ARG_CIFS_EXTRA_MOUNT_OPTIONS
},
3325 { "rate-limit-interval", required_argument
, NULL
, ARG_RATE_LIMIT_INTERVAL
},
3326 { "rate-limit-burst", required_argument
, NULL
, ARG_RATE_LIMIT_BURST
},
3327 { "stop-delay", required_argument
, NULL
, ARG_STOP_DELAY
},
3328 { "kill-processes", required_argument
, NULL
, ARG_KILL_PROCESSES
},
3329 { "enforce-password-policy", required_argument
, NULL
, ARG_ENFORCE_PASSWORD_POLICY
},
3330 { "password-change-now", required_argument
, NULL
, ARG_PASSWORD_CHANGE_NOW
},
3331 { "password-change-min", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MIN
},
3332 { "password-change-max", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MAX
},
3333 { "password-change-warn", required_argument
, NULL
, ARG_PASSWORD_CHANGE_WARN
},
3334 { "password-change-inactive", required_argument
, NULL
, ARG_PASSWORD_CHANGE_INACTIVE
},
3335 { "auto-login", required_argument
, NULL
, ARG_AUTO_LOGIN
},
3336 { "session-launcher", required_argument
, NULL
, ARG_SESSION_LAUNCHER
, },
3337 { "session-type", required_argument
, NULL
, ARG_SESSION_TYPE
, },
3338 { "json", required_argument
, NULL
, ARG_JSON
},
3339 { "export-format", required_argument
, NULL
, ARG_EXPORT_FORMAT
},
3340 { "pkcs11-token-uri", required_argument
, NULL
, ARG_PKCS11_TOKEN_URI
},
3341 { "fido2-credential-algorithm", required_argument
, NULL
, ARG_FIDO2_CRED_ALG
},
3342 { "fido2-device", required_argument
, NULL
, ARG_FIDO2_DEVICE
},
3343 { "fido2-with-client-pin", required_argument
, NULL
, ARG_FIDO2_WITH_PIN
},
3344 { "fido2-with-user-presence", required_argument
, NULL
, ARG_FIDO2_WITH_UP
},
3345 { "fido2-with-user-verification", required_argument
, NULL
, ARG_FIDO2_WITH_UV
},
3346 { "recovery-key", required_argument
, NULL
, ARG_RECOVERY_KEY
},
3347 { "and-resize", required_argument
, NULL
, ARG_AND_RESIZE
},
3348 { "and-change-password", required_argument
, NULL
, ARG_AND_CHANGE_PASSWORD
},
3349 { "drop-caches", required_argument
, NULL
, ARG_DROP_CACHES
},
3350 { "luks-extra-mount-options", required_argument
, NULL
, ARG_LUKS_EXTRA_MOUNT_OPTIONS
},
3351 { "auto-resize-mode", required_argument
, NULL
, ARG_AUTO_RESIZE_MODE
},
3352 { "rebalance-weight", required_argument
, NULL
, ARG_REBALANCE_WEIGHT
},
3353 { "capability-bounding-set", required_argument
, NULL
, ARG_CAPABILITY_BOUNDING_SET
},
3354 { "capability-ambient-set", required_argument
, NULL
, ARG_CAPABILITY_AMBIENT_SET
},
3355 { "prompt-new-user", no_argument
, NULL
, ARG_PROMPT_NEW_USER
},
3356 { "blob", required_argument
, NULL
, 'b' },
3357 { "avatar", required_argument
, NULL
, ARG_AVATAR
},
3358 { "login-background", required_argument
, NULL
, ARG_LOGIN_BACKGROUND
},
3359 { "tmp-limit", required_argument
, NULL
, ARG_TMP_LIMIT
},
3360 { "dev-shm-limit", required_argument
, NULL
, ARG_DEV_SHM_LIMIT
},
3361 { "default-area", required_argument
, NULL
, ARG_DEFAULT_AREA
},
3362 { "key-name", required_argument
, NULL
, ARG_KEY_NAME
},
3363 { "seize", required_argument
, NULL
, ARG_SEIZE
},
3364 { "match", required_argument
, NULL
, ARG_MATCH
},
3370 /* This points to one of arg_identity_extra, arg_identity_extra_this_machine,
3371 * arg_identity_extra_other_machines, in order to redirect changes on the next property being set to
3372 * this part of the identity, instead of the default. */
3373 sd_json_variant
**match_identity
= NULL
;
3378 /* 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. */
3379 r
= getenv_bool("SYSTEMD_HOME_DRY_RUN");
3382 else if (r
!= -ENXIO
)
3383 log_debug_errno(r
, "Unable to parse $SYSTEMD_HOME_DRY_RUN, ignoring: %m");
3388 c
= getopt_long(argc
, argv
, "hH:M:I:c:d:u:G:k:s:e:b:jPENAT", options
, NULL
);
3395 return help(0, NULL
, NULL
);
3401 arg_pager_flags
|= PAGER_DISABLE
;
3408 case ARG_NO_ASK_PASSWORD
:
3409 arg_ask_password
= false;
3417 arg_transport
= BUS_TRANSPORT_REMOTE
;
3422 r
= parse_machine_argument(optarg
, &arg_host
, &arg_transport
);
3428 arg_identity
= optarg
;
3432 if (isempty(optarg
)) {
3433 r
= drop_from_identity("realName");
3440 if (!valid_gecos(optarg
))
3441 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Real name '%s' not a valid GECOS field.", optarg
);
3443 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "realName", optarg
);
3445 return log_error_errno(r
, "Failed to set realName field: %m");
3450 if (isempty(optarg
)) {
3451 r
= drop_from_identity("aliases");
3457 for (const char *p
= optarg
;;) {
3458 _cleanup_free_
char *word
= NULL
;
3460 r
= extract_first_word(&p
, &word
, ",", 0);
3462 return log_error_errno(r
, "Failed to parse alias list: %m");
3466 if (!valid_user_group_name(word
, 0))
3467 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid alias user name %s.", word
);
3469 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*av
=
3470 sd_json_variant_ref(sd_json_variant_by_key(arg_identity_extra
, "aliases"));
3472 _cleanup_strv_free_
char **list
= NULL
;
3473 r
= sd_json_variant_strv(av
, &list
);
3475 return log_error_errno(r
, "Failed to parse group list: %m");
3477 r
= strv_extend(&list
, word
);
3481 strv_sort_uniq(list
);
3483 av
= sd_json_variant_unref(av
);
3484 r
= sd_json_variant_new_array_strv(&av
, list
);
3486 return log_error_errno(r
, "Failed to create alias list JSON: %m");
3488 r
= sd_json_variant_set_field(&arg_identity_extra
, "aliases", av
);
3490 return log_error_errno(r
, "Failed to update alias list: %m");
3497 _cleanup_free_
char *hd
= NULL
;
3499 if (isempty(optarg
)) {
3500 r
= drop_from_identity("homeDirectory");
3507 r
= parse_path_argument(optarg
, false, &hd
);
3511 if (!valid_home(hd
))
3512 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Home directory '%s' not valid.", hd
);
3514 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "homeDirectory", hd
);
3516 return log_error_errno(r
, "Failed to set homeDirectory field: %m");
3522 if (isempty(optarg
)) {
3523 r
= drop_from_identity("realm");
3530 r
= dns_name_is_valid(optarg
);
3532 return log_error_errno(r
, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg
);
3534 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Realm '%s' is not a valid DNS domain.", optarg
);
3536 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "realm", optarg
);
3538 return log_error_errno(r
, "Failed to set realm field: %m");
3541 case ARG_EMAIL_ADDRESS
:
3544 case ARG_CIFS_USER_NAME
:
3545 case ARG_CIFS_DOMAIN
:
3546 case ARG_CIFS_EXTRA_MOUNT_OPTIONS
:
3547 case ARG_LUKS_EXTRA_MOUNT_OPTIONS
:
3548 case ARG_SESSION_LAUNCHER
:
3549 case ARG_SESSION_TYPE
: {
3552 c
== ARG_EMAIL_ADDRESS
? "emailAddress" :
3553 c
== ARG_LOCATION
? "location" :
3554 c
== ARG_ICON_NAME
? "iconName" :
3555 c
== ARG_CIFS_USER_NAME
? "cifsUserName" :
3556 c
== ARG_CIFS_DOMAIN
? "cifsDomain" :
3557 c
== ARG_CIFS_EXTRA_MOUNT_OPTIONS
? "cifsExtraMountOptions" :
3558 c
== ARG_LUKS_EXTRA_MOUNT_OPTIONS
? "luksExtraMountOptions" :
3559 c
== ARG_SESSION_LAUNCHER
? "preferredSessionLauncher" :
3560 c
== ARG_SESSION_TYPE
? "preferredSessionType" :
3565 if (isempty(optarg
)) {
3566 r
= drop_from_identity(field
);
3573 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, field
, optarg
);
3575 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3580 case ARG_CIFS_SERVICE
:
3581 if (isempty(optarg
)) {
3582 r
= drop_from_identity("cifsService");
3589 r
= parse_cifs_service(optarg
, NULL
, NULL
, NULL
);
3591 return log_error_errno(r
, "Failed to validate CIFS service name: %s", optarg
);
3593 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "cifsService", optarg
);
3595 return log_error_errno(r
, "Failed to set cifsService field: %m");
3599 case ARG_PASSWORD_HINT
:
3600 if (isempty(optarg
)) {
3601 r
= drop_from_identity("passwordHint");
3608 r
= sd_json_variant_set_field_string(&arg_identity_extra_privileged
, "passwordHint", optarg
);
3610 return log_error_errno(r
, "Failed to set passwordHint field: %m");
3612 string_erase(optarg
);
3618 if (isempty(optarg
)) {
3619 r
= drop_from_identity("niceLevel");
3625 r
= parse_nice(optarg
, &nc
);
3627 return log_error_errno(r
, "Failed to parse nice level: %s", optarg
);
3629 r
= sd_json_variant_set_field_integer(match_identity
?: &arg_identity_extra
, "niceLevel", nc
);
3631 return log_error_errno(r
, "Failed to set niceLevel field: %m");
3637 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*jcur
= NULL
, *jmax
= NULL
;
3638 _cleanup_free_
char *field
= NULL
, *t
= NULL
;
3643 if (isempty(optarg
)) {
3644 /* Remove all resource limits */
3646 r
= drop_from_identity("resourceLimits");
3650 arg_identity_filter_rlimits
= strv_free(arg_identity_filter_rlimits
);
3651 arg_identity_extra_rlimits
= sd_json_variant_unref(arg_identity_extra_rlimits
);
3655 eq
= strchr(optarg
, '=');
3657 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Can't parse resource limit assignment: %s", optarg
);
3659 field
= strndup(optarg
, eq
- optarg
);
3663 l
= rlimit_from_string_harder(field
);
3665 return log_error_errno(l
, "Unknown resource limit type: %s", field
);
3667 if (isempty(eq
+ 1)) {
3668 /* Remove only the specific rlimit */
3670 r
= strv_extend(&arg_identity_filter_rlimits
, rlimit_to_string(l
));
3674 r
= sd_json_variant_filter(&arg_identity_extra_rlimits
, STRV_MAKE(field
));
3676 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
3681 r
= rlimit_parse(l
, eq
+ 1, &rl
);
3683 return log_error_errno(r
, "Failed to parse resource limit value: %s", eq
+ 1);
3685 r
= rl
.rlim_cur
== RLIM_INFINITY
? sd_json_variant_new_null(&jcur
) : sd_json_variant_new_unsigned(&jcur
, rl
.rlim_cur
);
3687 return log_error_errno(r
, "Failed to allocate current integer: %m");
3689 r
= rl
.rlim_max
== RLIM_INFINITY
? sd_json_variant_new_null(&jmax
) : sd_json_variant_new_unsigned(&jmax
, rl
.rlim_max
);
3691 return log_error_errno(r
, "Failed to allocate maximum integer: %m");
3693 t
= strjoin("RLIMIT_", rlimit_to_string(l
));
3697 r
= sd_json_variant_set_fieldbo(
3698 &arg_identity_extra_rlimits
, t
,
3699 SD_JSON_BUILD_PAIR("cur", SD_JSON_BUILD_VARIANT(jcur
)),
3700 SD_JSON_BUILD_PAIR("max", SD_JSON_BUILD_VARIANT(jmax
)));
3702 return log_error_errno(r
, "Failed to set %s field: %m", rlimit_to_string(l
));
3710 if (isempty(optarg
)) {
3711 r
= drop_from_identity("uid");
3718 r
= parse_uid(optarg
, &uid
);
3720 return log_error_errno(r
, "Failed to parse UID '%s'.", optarg
);
3722 if (uid_is_system(uid
))
3723 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in system range, refusing.", uid
);
3724 if (uid_is_greeter(uid
))
3725 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in greeter range, refusing.", uid
);
3726 if (uid_is_dynamic(uid
))
3727 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in dynamic range, refusing.", uid
);
3728 if (uid
== UID_NOBODY
)
3729 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is nobody UID, refusing.", uid
);
3731 r
= sd_json_variant_set_field_unsigned(&arg_identity_extra
, "uid", uid
);
3733 return log_error_errno(r
, "Failed to set realm field: %m");
3739 case ARG_IMAGE_PATH
: {
3740 const char *field
= c
== 'k' ? "skeletonDirectory" : "imagePath";
3741 _cleanup_free_
char *v
= NULL
;
3743 if (isempty(optarg
)) {
3744 r
= drop_from_identity(field
);
3751 r
= parse_path_argument(optarg
, false, &v
);
3755 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra_this_machine
, field
, v
);
3757 return log_error_errno(r
, "Failed to set %s field: %m", v
);
3763 if (isempty(optarg
)) {
3764 r
= drop_from_identity("shell");
3771 if (!valid_shell(optarg
))
3772 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Shell '%s' not valid.", optarg
);
3774 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "shell", optarg
);
3776 return log_error_errno(r
, "Failed to set shell field: %m");
3781 _cleanup_free_
char **l
= NULL
;
3782 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*ne
= NULL
;
3785 if (isempty(optarg
)) {
3786 r
= drop_from_identity("environment");
3793 e
= sd_json_variant_by_key(match_identity
? *match_identity
: arg_identity_extra
, "environment");
3795 r
= sd_json_variant_strv(e
, &l
);
3797 return log_error_errno(r
, "Failed to parse JSON environment field: %m");
3800 r
= strv_env_replace_strdup_passthrough(&l
, optarg
);
3802 return log_error_errno(r
, "Cannot assign environment variable %s: %m", optarg
);
3806 r
= sd_json_variant_new_array_strv(&ne
, l
);
3808 return log_error_errno(r
, "Failed to allocate environment list JSON: %m");
3810 r
= sd_json_variant_set_field(match_identity
?: &arg_identity_extra
, "environment", ne
);
3812 return log_error_errno(r
, "Failed to set environment list: %m");
3818 if (isempty(optarg
)) {
3819 r
= drop_from_identity("timeZone");
3826 if (!timezone_is_valid(optarg
, LOG_DEBUG
))
3827 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Timezone '%s' is not valid.", optarg
);
3829 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "timeZone", optarg
);
3831 return log_error_errno(r
, "Failed to set timezone field: %m");
3835 case ARG_LANGUAGE
: {
3836 const char *p
= optarg
;
3839 r
= drop_from_identity("preferredLanguage");
3843 r
= drop_from_identity("additionalLanguages");
3847 arg_languages
= strv_free(arg_languages
);
3852 _cleanup_free_
char *word
= NULL
;
3854 r
= extract_first_word(&p
, &word
, ",:", 0);
3856 return log_error_errno(r
, "Failed to parse locale list: %m");
3860 if (!locale_is_valid(word
))
3861 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Locale '%s' is not valid.", word
);
3863 if (locale_is_installed(word
) <= 0)
3864 log_warning("Locale '%s' is not installed, accepting anyway.", word
);
3866 r
= strv_consume(&arg_languages
, TAKE_PTR(word
));
3870 strv_uniq(arg_languages
);
3880 case ARG_KILL_PROCESSES
:
3881 case ARG_ENFORCE_PASSWORD_POLICY
:
3882 case ARG_AUTO_LOGIN
:
3883 case ARG_PASSWORD_CHANGE_NOW
: {
3885 c
== ARG_LOCKED
? "locked" :
3886 c
== ARG_NOSUID
? "mountNoSuid" :
3887 c
== ARG_NODEV
? "mountNoDevices" :
3888 c
== ARG_NOEXEC
? "mountNoExecute" :
3889 c
== ARG_KILL_PROCESSES
? "killProcesses" :
3890 c
== ARG_ENFORCE_PASSWORD_POLICY
? "enforcePasswordPolicy" :
3891 c
== ARG_AUTO_LOGIN
? "autoLogin" :
3892 c
== ARG_PASSWORD_CHANGE_NOW
? "passwordChangeNow" :
3897 if (isempty(optarg
)) {
3898 r
= drop_from_identity(field
);
3905 r
= parse_boolean(optarg
);
3907 return log_error_errno(r
, "Failed to parse %s boolean: %m", field
);
3909 r
= sd_json_variant_set_field_boolean(match_identity
?: &arg_identity_extra
, field
, r
> 0);
3911 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3917 r
= sd_json_variant_set_field_boolean(&arg_identity_extra
, "enforcePasswordPolicy", false);
3919 return log_error_errno(r
, "Failed to set enforcePasswordPolicy field: %m");
3924 if (isempty(optarg
)) {
3925 FOREACH_STRING(prop
, "diskSize", "diskSizeRelative", "rebalanceWeight") {
3926 r
= drop_from_identity(prop
);
3931 arg_disk_size
= arg_disk_size_relative
= UINT64_MAX
;
3935 r
= parse_permyriad(optarg
);
3937 r
= parse_disk_size(optarg
, &arg_disk_size
);
3941 r
= drop_from_identity("diskSizeRelative");
3945 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra_this_machine
, "diskSize", arg_disk_size
);
3947 return log_error_errno(r
, "Failed to set diskSize field: %m");
3949 arg_disk_size_relative
= UINT64_MAX
;
3951 /* Normalize to UINT32_MAX == 100% */
3952 arg_disk_size_relative
= UINT32_SCALE_FROM_PERMYRIAD(r
);
3954 r
= drop_from_identity("diskSize");
3958 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra_this_machine
, "diskSizeRelative", arg_disk_size_relative
);
3960 return log_error_errno(r
, "Failed to set diskSizeRelative field: %m");
3962 arg_disk_size
= UINT64_MAX
;
3965 /* Automatically turn off the rebalance logic if user configured a size explicitly */
3966 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra_this_machine
, "rebalanceWeight", REBALANCE_WEIGHT_OFF
);
3968 return log_error_errno(r
, "Failed to set rebalanceWeight field: %m");
3972 case ARG_ACCESS_MODE
: {
3975 if (isempty(optarg
)) {
3976 r
= drop_from_identity("accessMode");
3983 r
= parse_mode(optarg
, &mode
);
3985 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Access mode '%s' not valid.", optarg
);
3987 r
= sd_json_variant_set_field_unsigned(&arg_identity_extra
, "accessMode", mode
);
3989 return log_error_errno(r
, "Failed to set access mode field: %m");
3994 case ARG_LUKS_DISCARD
:
3995 if (isempty(optarg
)) {
3996 r
= drop_from_identity("luksDiscard");
4003 r
= parse_boolean(optarg
);
4005 return log_error_errno(r
, "Failed to parse --luks-discard= parameter: %s", optarg
);
4007 r
= sd_json_variant_set_field_boolean(match_identity
?: &arg_identity_extra
, "luksDiscard", r
);
4009 return log_error_errno(r
, "Failed to set discard field: %m");
4013 case ARG_LUKS_OFFLINE_DISCARD
:
4014 if (isempty(optarg
)) {
4015 r
= drop_from_identity("luksOfflineDiscard");
4022 r
= parse_boolean(optarg
);
4024 return log_error_errno(r
, "Failed to parse --luks-offline-discard= parameter: %s", optarg
);
4026 r
= sd_json_variant_set_field_boolean(match_identity
?: &arg_identity_extra
, "luksOfflineDiscard", r
);
4028 return log_error_errno(r
, "Failed to set offline discard field: %m");
4032 case ARG_LUKS_VOLUME_KEY_SIZE
:
4033 case ARG_LUKS_PBKDF_FORCE_ITERATIONS
:
4034 case ARG_LUKS_PBKDF_PARALLEL_THREADS
:
4035 case ARG_RATE_LIMIT_BURST
: {
4037 c
== ARG_LUKS_VOLUME_KEY_SIZE
? "luksVolumeKeySize" :
4038 c
== ARG_LUKS_PBKDF_FORCE_ITERATIONS
? "luksPbkdfForceIterations" :
4039 c
== ARG_LUKS_PBKDF_PARALLEL_THREADS
? "luksPbkdfParallelThreads" :
4040 c
== ARG_RATE_LIMIT_BURST
? "rateLimitBurst" : NULL
;
4045 if (isempty(optarg
)) {
4046 r
= drop_from_identity(field
);
4051 r
= safe_atou(optarg
, &n
);
4053 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
4055 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, n
);
4057 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4062 case ARG_LUKS_SECTOR_SIZE
: {
4065 if (isempty(optarg
)) {
4066 r
= drop_from_identity("luksSectorSize");
4073 r
= parse_sector_size(optarg
, &ss
);
4077 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, "luksSectorSize", ss
);
4079 return log_error_errno(r
, "Failed to set sector size field: %m");
4087 if (isempty(optarg
)) {
4088 r
= drop_from_identity("umask");
4095 r
= parse_mode(optarg
, &m
);
4097 return log_error_errno(r
, "Failed to parse umask: %m");
4099 r
= sd_json_variant_set_field_integer(match_identity
?: &arg_identity_extra
, "umask", m
);
4101 return log_error_errno(r
, "Failed to set umask field: %m");
4106 case ARG_SSH_AUTHORIZED_KEYS
: {
4107 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
4108 _cleanup_strv_free_
char **l
= NULL
, **add
= NULL
;
4110 if (isempty(optarg
)) {
4111 r
= drop_from_identity("sshAuthorizedKeys");
4118 if (optarg
[0] == '@') {
4119 _cleanup_fclose_
FILE *f
= NULL
;
4121 /* If prefixed with '@' read from a file */
4123 f
= fopen(optarg
+1, "re");
4125 return log_error_errno(errno
, "Failed to open '%s': %m", optarg
+1);
4128 _cleanup_free_
char *line
= NULL
;
4130 r
= read_line(f
, LONG_LINE_MAX
, &line
);
4132 return log_error_errno(r
, "Failed to read from '%s': %m", optarg
+1);
4142 r
= strv_consume(&add
, TAKE_PTR(line
));
4147 /* Otherwise, assume it's a literal key. Let's do some superficial checks
4148 * before accept it though. */
4150 if (string_has_cc(optarg
, NULL
))
4151 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Authorized key contains control characters, refusing.");
4152 if (optarg
[0] == '#')
4153 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specified key is a comment?");
4155 add
= strv_new(optarg
);
4160 v
= sd_json_variant_ref(sd_json_variant_by_key(arg_identity_extra_privileged
, "sshAuthorizedKeys"));
4162 r
= sd_json_variant_strv(v
, &l
);
4164 return log_error_errno(r
, "Failed to parse SSH authorized keys list: %m");
4167 r
= strv_extend_strv_consume(&l
, TAKE_PTR(add
), /* filter_duplicates = */ true);
4171 v
= sd_json_variant_unref(v
);
4173 r
= sd_json_variant_new_array_strv(&v
, l
);
4177 r
= sd_json_variant_set_field(&arg_identity_extra_privileged
, "sshAuthorizedKeys", v
);
4179 return log_error_errno(r
, "Failed to set authorized keys: %m");
4184 case ARG_NOT_BEFORE
:
4190 field
= c
== ARG_NOT_BEFORE
? "notBeforeUSec" :
4191 IN_SET(c
, ARG_NOT_AFTER
, 'e') ? "notAfterUSec" : NULL
;
4195 if (isempty(optarg
)) {
4196 r
= drop_from_identity(field
);
4203 /* Note the minor discrepancy regarding -e parsing here: we support that for compat
4204 * reasons, and in the original useradd(8) implementation it accepts dates in the
4205 * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
4206 * with greater precision. */
4207 r
= parse_timestamp(optarg
, &n
);
4209 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
4211 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, n
);
4213 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4217 case ARG_PASSWORD_CHANGE_MIN
:
4218 case ARG_PASSWORD_CHANGE_MAX
:
4219 case ARG_PASSWORD_CHANGE_WARN
:
4220 case ARG_PASSWORD_CHANGE_INACTIVE
: {
4224 field
= c
== ARG_PASSWORD_CHANGE_MIN
? "passwordChangeMinUSec" :
4225 c
== ARG_PASSWORD_CHANGE_MAX
? "passwordChangeMaxUSec" :
4226 c
== ARG_PASSWORD_CHANGE_WARN
? "passwordChangeWarnUSec" :
4227 c
== ARG_PASSWORD_CHANGE_INACTIVE
? "passwordChangeInactiveUSec" :
4232 if (isempty(optarg
)) {
4233 r
= drop_from_identity(field
);
4240 r
= parse_sec(optarg
, &n
);
4242 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
4244 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, n
);
4246 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4252 case ARG_LUKS_CIPHER
:
4253 case ARG_LUKS_CIPHER_MODE
:
4254 case ARG_LUKS_PBKDF_TYPE
:
4255 case ARG_LUKS_PBKDF_HASH_ALGORITHM
: {
4258 c
== ARG_STORAGE
? "storage" :
4259 c
== ARG_FS_TYPE
? "fileSystemType" :
4260 c
== ARG_LUKS_CIPHER
? "luksCipher" :
4261 c
== ARG_LUKS_CIPHER_MODE
? "luksCipherMode" :
4262 c
== ARG_LUKS_PBKDF_TYPE
? "luksPbkdfType" :
4263 c
== ARG_LUKS_PBKDF_HASH_ALGORITHM
? "luksPbkdfHashAlgorithm" : NULL
;
4267 if (isempty(optarg
)) {
4268 r
= drop_from_identity(field
);
4275 if (!string_is_safe(optarg
))
4276 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Parameter for %s field not valid: %s", field
, optarg
);
4278 r
= sd_json_variant_set_field_string(
4279 match_identity
?: (IN_SET(c
, ARG_STORAGE
, ARG_FS_TYPE
) ?
4280 &arg_identity_extra_this_machine
:
4281 &arg_identity_extra
), field
, optarg
);
4283 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4288 case ARG_LUKS_PBKDF_TIME_COST
:
4289 case ARG_RATE_LIMIT_INTERVAL
:
4290 case ARG_STOP_DELAY
: {
4292 c
== ARG_LUKS_PBKDF_TIME_COST
? "luksPbkdfTimeCostUSec" :
4293 c
== ARG_RATE_LIMIT_INTERVAL
? "rateLimitIntervalUSec" :
4294 c
== ARG_STOP_DELAY
? "stopDelayUSec" :
4300 if (isempty(optarg
)) {
4301 r
= drop_from_identity(field
);
4308 r
= parse_sec(optarg
, &t
);
4310 return log_error_errno(r
, "Failed to parse %s field: %s", field
, optarg
);
4312 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, t
);
4314 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4320 const char *p
= optarg
;
4323 r
= drop_from_identity("memberOf");
4331 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*mo
= NULL
;
4332 _cleanup_strv_free_
char **list
= NULL
;
4333 _cleanup_free_
char *word
= NULL
;
4335 r
= extract_first_word(&p
, &word
, ",", 0);
4337 return log_error_errno(r
, "Failed to parse group list: %m");
4341 if (!valid_user_group_name(word
, 0))
4342 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid group name %s.", word
);
4344 mo
= sd_json_variant_ref(sd_json_variant_by_key(arg_identity_extra
, "memberOf"));
4346 r
= sd_json_variant_strv(mo
, &list
);
4348 return log_error_errno(r
, "Failed to parse group list: %m");
4350 r
= strv_extend(&list
, word
);
4354 strv_sort_uniq(list
);
4356 mo
= sd_json_variant_unref(mo
);
4357 r
= sd_json_variant_new_array_strv(&mo
, list
);
4359 return log_error_errno(r
, "Failed to create group list JSON: %m");
4361 r
= sd_json_variant_set_field(match_identity
?: &arg_identity_extra
, "memberOf", mo
);
4363 return log_error_errno(r
, "Failed to update group list: %m");
4369 case ARG_TASKS_MAX
: {
4372 if (isempty(optarg
)) {
4373 r
= drop_from_identity("tasksMax");
4379 r
= safe_atou64(optarg
, &u
);
4381 return log_error_errno(r
, "Failed to parse --tasks-max= parameter: %s", optarg
);
4383 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, "tasksMax", u
);
4385 return log_error_errno(r
, "Failed to set tasksMax field: %m");
4390 case ARG_MEMORY_MAX
:
4391 case ARG_MEMORY_HIGH
:
4392 case ARG_LUKS_PBKDF_MEMORY_COST
: {
4394 c
== ARG_MEMORY_MAX
? "memoryMax" :
4395 c
== ARG_MEMORY_HIGH
? "memoryHigh" :
4396 c
== ARG_LUKS_PBKDF_MEMORY_COST
? "luksPbkdfMemoryCost" : NULL
;
4402 if (isempty(optarg
)) {
4403 r
= drop_from_identity(field
);
4409 r
= parse_size(optarg
, 1024, &u
);
4411 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
4413 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra_this_machine
, field
, u
);
4415 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4420 case ARG_CPU_WEIGHT
:
4421 case ARG_IO_WEIGHT
: {
4422 const char *field
= c
== ARG_CPU_WEIGHT
? "cpuWeight" :
4423 c
== ARG_IO_WEIGHT
? "ioWeight" : NULL
;
4428 if (isempty(optarg
)) {
4429 r
= drop_from_identity(field
);
4435 r
= safe_atou64(optarg
, &u
);
4437 return log_error_errno(r
, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg
);
4439 if (!CGROUP_WEIGHT_IS_OK(u
))
4440 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Weight %" PRIu64
" is out of valid weight range.", u
);
4442 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, u
);
4444 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4449 case ARG_PKCS11_TOKEN_URI
:
4450 if (streq(optarg
, "list"))
4451 return pkcs11_list_tokens();
4453 /* If --pkcs11-token-uri= is specified we always drop everything old */
4454 FOREACH_STRING(p
, "pkcs11TokenUri", "pkcs11EncryptedKey") {
4455 r
= drop_from_identity(p
);
4460 if (isempty(optarg
)) {
4461 arg_pkcs11_token_uri
= strv_free(arg_pkcs11_token_uri
);
4465 if (streq(optarg
, "auto")) {
4466 _cleanup_free_
char *found
= NULL
;
4468 r
= pkcs11_find_token_auto(&found
);
4471 r
= strv_consume(&arg_pkcs11_token_uri
, TAKE_PTR(found
));
4473 if (!pkcs11_uri_valid(optarg
))
4474 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Not a valid PKCS#11 URI: %s", optarg
);
4476 r
= strv_extend(&arg_pkcs11_token_uri
, optarg
);
4481 strv_uniq(arg_pkcs11_token_uri
);
4484 case ARG_FIDO2_CRED_ALG
:
4485 r
= parse_fido2_algorithm(optarg
, &arg_fido2_cred_alg
);
4487 return log_error_errno(r
, "Failed to parse COSE algorithm: %s", optarg
);
4490 case ARG_FIDO2_DEVICE
:
4491 if (streq(optarg
, "list"))
4492 return fido2_list_devices();
4494 FOREACH_STRING(p
, "fido2HmacCredential", "fido2HmacSalt") {
4495 r
= drop_from_identity(p
);
4500 if (isempty(optarg
)) {
4501 arg_fido2_device
= strv_free(arg_fido2_device
);
4505 if (streq(optarg
, "auto")) {
4506 _cleanup_free_
char *found
= NULL
;
4508 r
= fido2_find_device_auto(&found
);
4512 r
= strv_consume(&arg_fido2_device
, TAKE_PTR(found
));
4514 r
= strv_extend(&arg_fido2_device
, optarg
);
4518 strv_uniq(arg_fido2_device
);
4521 case ARG_FIDO2_WITH_PIN
:
4522 r
= parse_boolean_argument("--fido2-with-client-pin=", optarg
, NULL
);
4526 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_PIN
, r
);
4529 case ARG_FIDO2_WITH_UP
:
4530 r
= parse_boolean_argument("--fido2-with-user-presence=", optarg
, NULL
);
4534 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UP
, r
);
4537 case ARG_FIDO2_WITH_UV
:
4538 r
= parse_boolean_argument("--fido2-with-user-verification=", optarg
, NULL
);
4542 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UV
, r
);
4545 case ARG_RECOVERY_KEY
:
4546 r
= parse_boolean(optarg
);
4548 return log_error_errno(r
, "Failed to parse --recovery-key= argument: %s", optarg
);
4550 arg_recovery_key
= r
;
4552 FOREACH_STRING(p
, "recoveryKey", "recoveryKeyType") {
4553 r
= drop_from_identity(p
);
4560 case ARG_AUTO_RESIZE_MODE
:
4561 if (isempty(optarg
)) {
4562 r
= drop_from_identity("autoResizeMode");
4569 r
= auto_resize_mode_from_string(optarg
);
4571 return log_error_errno(r
, "Failed to parse --auto-resize-mode= argument: %s", optarg
);
4573 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "autoResizeMode", auto_resize_mode_to_string(r
));
4575 return log_error_errno(r
, "Failed to set autoResizeMode field: %m");
4579 case ARG_REBALANCE_WEIGHT
: {
4582 if (isempty(optarg
)) {
4583 r
= drop_from_identity("rebalanceWeight");
4589 if (streq(optarg
, "off"))
4590 u
= REBALANCE_WEIGHT_OFF
;
4592 r
= safe_atou64(optarg
, &u
);
4594 return log_error_errno(r
, "Failed to parse --rebalance-weight= argument: %s", optarg
);
4596 if (u
< REBALANCE_WEIGHT_MIN
|| u
> REBALANCE_WEIGHT_MAX
)
4597 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
), "Rebalancing weight out of valid range %" PRIu64
"%s%" PRIu64
": %s",
4598 REBALANCE_WEIGHT_MIN
, glyph(GLYPH_ELLIPSIS
), REBALANCE_WEIGHT_MAX
, optarg
);
4601 /* Drop from per machine stuff and everywhere */
4602 r
= drop_from_identity("rebalanceWeight");
4606 /* Add to main identity */
4607 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, "rebalanceWeight", u
);
4609 return log_error_errno(r
, "Failed to set rebalanceWeight field: %m");
4615 arg_json_format_flags
= SD_JSON_FORMAT_PRETTY_AUTO
|SD_JSON_FORMAT_COLOR_AUTO
;
4619 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
4626 if (arg_export_format
== EXPORT_FORMAT_FULL
)
4627 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
4628 else if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
4629 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
4631 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specifying -E more than twice is not supported.");
4633 arg_json_format_flags
&= ~SD_JSON_FORMAT_OFF
;
4634 if (arg_json_format_flags
== 0)
4635 arg_json_format_flags
= SD_JSON_FORMAT_PRETTY_AUTO
|SD_JSON_FORMAT_COLOR_AUTO
;
4638 case ARG_EXPORT_FORMAT
:
4639 if (streq(optarg
, "full"))
4640 arg_export_format
= EXPORT_FORMAT_FULL
;
4641 else if (streq(optarg
, "stripped"))
4642 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
4643 else if (streq(optarg
, "minimal"))
4644 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
4645 else if (streq(optarg
, "help")) {
4654 case ARG_AND_RESIZE
:
4655 arg_and_resize
= true;
4658 case ARG_AND_CHANGE_PASSWORD
:
4659 arg_and_change_password
= true;
4662 case ARG_DROP_CACHES
: {
4663 if (isempty(optarg
)) {
4664 r
= drop_from_identity("dropCaches");
4670 r
= parse_boolean_argument("--drop-caches=", optarg
, NULL
);
4674 r
= sd_json_variant_set_field_boolean(match_identity
?: &arg_identity_extra
, "dropCaches", r
);
4676 return log_error_errno(r
, "Failed to set drop caches field: %m");
4681 case ARG_CAPABILITY_AMBIENT_SET
:
4682 case ARG_CAPABILITY_BOUNDING_SET
: {
4683 _cleanup_strv_free_
char **l
= NULL
;
4684 bool subtract
= false;
4685 uint64_t parsed
, *which
, updated
;
4686 const char *p
, *field
;
4688 if (c
== ARG_CAPABILITY_AMBIENT_SET
) {
4689 which
= &arg_capability_ambient_set
;
4690 field
= "capabilityAmbientSet";
4692 assert(c
== ARG_CAPABILITY_BOUNDING_SET
);
4693 which
= &arg_capability_bounding_set
;
4694 field
= "capabilityBoundingSet";
4697 if (isempty(optarg
)) {
4698 r
= drop_from_identity(field
);
4702 *which
= UINT64_MAX
;
4712 r
= capability_set_from_string(p
, &parsed
);
4714 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid capabilities in capability string '%s'.", p
);
4716 return log_error_errno(r
, "Failed to parse capability string '%s': %m", p
);
4718 if (*which
== UINT64_MAX
)
4719 updated
= subtract
? all_capabilities() & ~parsed
: parsed
;
4721 updated
= *which
& ~parsed
;
4723 updated
= *which
| parsed
;
4725 if (capability_set_to_strv(updated
, &l
) < 0)
4728 r
= sd_json_variant_set_field_strv(match_identity
?: &arg_identity_extra
, field
, l
);
4730 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4736 case ARG_PROMPT_NEW_USER
:
4737 arg_prompt_new_user
= true;
4742 case ARG_LOGIN_BACKGROUND
: {
4743 _cleanup_close_
int fd
= -EBADF
;
4744 _cleanup_free_
char *path
= NULL
, *filename
= NULL
;
4749 if (isempty(optarg
)) { /* --blob= deletes everything, including existing blob dirs */
4750 hashmap_clear(arg_blob_files
);
4751 arg_blob_dir
= mfree(arg_blob_dir
);
4752 arg_blob_clear
= true;
4756 eq
= strrchr(optarg
, '=');
4757 if (!eq
) { /* --blob=/some/path replaces the blob dir */
4758 r
= parse_path_argument(optarg
, false, &arg_blob_dir
);
4760 return log_error_errno(r
, "Failed to parse path %s: %m", optarg
);
4764 /* --blob=filename=/some/path replaces the file "filename" with /some/path */
4765 filename
= strndup(optarg
, eq
- optarg
);
4769 if (isempty(filename
))
4770 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Can't parse blob file assignment: %s", optarg
);
4771 if (!suitable_blob_filename(filename
))
4772 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid blob filename: %s", filename
);
4774 r
= parse_path_argument(eq
+ 1, false, &path
);
4776 return log_error_errno(r
, "Failed to parse path %s: %m", eq
+ 1);
4778 const char *well_known_filename
=
4779 c
== ARG_AVATAR
? "avatar" :
4780 c
== ARG_LOGIN_BACKGROUND
? "login-background" :
4782 assert(well_known_filename
);
4784 filename
= strdup(well_known_filename
);
4788 r
= parse_path_argument(optarg
, false, &path
);
4790 return log_error_errno(r
, "Failed to parse path %s: %m", optarg
);
4794 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
4796 return log_error_errno(errno
, "Failed to open %s: %m", path
);
4798 if (fd_verify_regular(fd
) < 0)
4799 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Provided blob is not a regular file: %s", path
);
4801 fd
= -EBADF
; /* Delete the file */
4803 r
= hashmap_ensure_put(&arg_blob_files
, &blob_fd_hash_ops
, filename
, FD_TO_PTR(fd
));
4805 return log_error_errno(r
, "Failed to map %s to %s in blob directory: %m", path
, filename
);
4806 TAKE_PTR(filename
); /* hashmap takes ownership */
4813 case ARG_DEV_SHM_LIMIT
: {
4815 c
== ARG_TMP_LIMIT
? "tmpLimit" :
4816 c
== ARG_DEV_SHM_LIMIT
? "devShmLimit" : NULL
;
4817 const char *field_scale
=
4818 c
== ARG_TMP_LIMIT
? "tmpLimitScale" :
4819 c
== ARG_DEV_SHM_LIMIT
? "devShmLimitScale" : NULL
;
4822 assert(field_scale
);
4824 if (isempty(optarg
)) {
4825 r
= drop_from_identity(field
);
4828 r
= drop_from_identity(field_scale
);
4834 r
= parse_permyriad(optarg
);
4838 r
= parse_size(optarg
, 1024, &u
);
4840 return log_error_errno(r
, "Failed to parse %s/%s parameter: %s", field
, field_scale
, optarg
);
4842 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field
, u
);
4844 return log_error_errno(r
, "Failed to set %s field: %m", field
);
4846 r
= drop_from_identity(field_scale
);
4850 r
= sd_json_variant_set_field_unsigned(match_identity
?: &arg_identity_extra
, field_scale
, UINT32_SCALE_FROM_PERMYRIAD(r
));
4852 return log_error_errno(r
, "Failed to set %s field: %m", field_scale
);
4854 r
= drop_from_identity(field
);
4862 case ARG_DEFAULT_AREA
:
4863 if (isempty(optarg
)) {
4864 r
= drop_from_identity("defaultArea");
4871 if (!filename_is_valid(optarg
))
4872 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Parameter for default area field not valid: %s", optarg
);
4874 r
= sd_json_variant_set_field_string(match_identity
?: &arg_identity_extra
, "defaultArea", optarg
);
4876 return log_error_errno(r
, "Failed to set default area field: %m");
4881 if (isempty(optarg
)) {
4882 arg_key_name
= mfree(arg_key_name
);
4886 if (!filename_is_valid(optarg
))
4887 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specified key name not valid: %s", optarg
);
4889 r
= free_and_strdup_warn(&arg_key_name
, optarg
);
4896 r
= parse_boolean_argument("--seize=", optarg
, &arg_seize
);
4902 if (streq(optarg
, "any"))
4903 match_identity
= &arg_identity_extra
;
4904 else if (streq(optarg
, "this"))
4905 match_identity
= &arg_identity_extra_this_machine
;
4906 else if (streq(optarg
, "other"))
4907 match_identity
= &arg_identity_extra_other_machines
;
4908 else if (streq(optarg
, "auto"))
4909 match_identity
= NULL
;
4911 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "--machine= argument not understood. Refusing.");
4915 match_identity
= &arg_identity_extra
;
4918 match_identity
= &arg_identity_extra_this_machine
;
4921 match_identity
= &arg_identity_extra_other_machines
;
4928 assert_not_reached();
4932 if (!strv_isempty(arg_pkcs11_token_uri
) || !strv_isempty(arg_fido2_device
))
4933 arg_and_change_password
= true;
4935 if (arg_disk_size
!= UINT64_MAX
|| arg_disk_size_relative
!= UINT64_MAX
)
4936 arg_and_resize
= true;
4938 if (!strv_isempty(arg_languages
)) {
4941 r
= sd_json_variant_set_field_string(&arg_identity_extra
, "preferredLanguage", arg_languages
[0]);
4943 return log_error_errno(r
, "Failed to update preferred language: %m");
4945 additional
= strv_skip(arg_languages
, 1);
4946 if (!strv_isempty(additional
)) {
4947 r
= sd_json_variant_set_field_strv(&arg_identity_extra
, "additionalLanguages", additional
);
4949 return log_error_errno(r
, "Failed to update additional language list: %m");
4951 r
= drop_from_identity("additionalLanguages");
4960 static int redirect_bus_mgr(void) {
4963 /* Talk to a different service if that's requested. (The same env var is also understood by homed, so
4964 * that it is relatively easily possible to invoke a second instance of homed for debug purposes and
4965 * have homectl talk to it, without colliding with the host version. This is handy when operating
4966 * from a homed-managed account.) */
4968 suffix
= getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
4970 static BusLocator locator
= {
4971 .path
= "/org/freedesktop/home1",
4972 .interface
= "org.freedesktop.home1.Manager",
4975 /* Yes, we leak this memory, but there's little point to collect this, given that we only do
4976 * this in a debug environment, do it only once, and the string shall live for out entire
4977 * process runtime. */
4979 locator
.destination
= strjoin("org.freedesktop.home1.", suffix
);
4980 if (!locator
.destination
)
4985 bus_mgr
= bus_home_mgr
;
4990 static bool is_fallback_shell(const char *p
) {
4997 /* Skip over login shell dash */
5000 if (streq(p
, "ystemd-home-fallback-shell")) /* maybe the dash was used to override the binary name? */
5004 q
= strrchr(p
, '/'); /* Skip over path */
5008 return streq(p
, "systemd-home-fallback-shell");
5011 static int fallback_shell(int argc
, char *argv
[]) {
5012 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
, *hr
= NULL
;
5013 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5014 _cleanup_strv_free_
char **l
= NULL
;
5015 _cleanup_free_
char *argv0
= NULL
;
5016 const char *json
, *hd
, *shell
;
5019 /* So here's the deal: if users log into a system via ssh, and their homed-managed home directory
5020 * wasn't activated yet, SSH will permit the access but the home directory isn't actually available
5021 * yet. SSH doesn't allow us to ask authentication questions from the PAM session stack, and doesn't
5022 * run the PAM authentication stack (because it authenticates via its own key management, after
5023 * all). So here's our way to support this: homectl can be invoked as a multi-call binary under the
5024 * name "systemd-home-fallback-shell". If so, it will chainload a login shell, but first try to
5025 * unlock the home directory of the user it is invoked as. systemd-homed will then override the shell
5026 * listed in user records whose home directory is not activated yet with this pseudo-shell. Net
5027 * effect: one SSH auth succeeds this pseudo shell gets invoked, which will unlock the homedir
5028 * (possibly asking for a passphrase) and then chainload the regular shell. Once the login is
5029 * complete the user record will look like any other. */
5031 r
= acquire_bus(&bus
);
5035 for (unsigned n_tries
= 0;; n_tries
++) {
5036 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5037 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
5038 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
5041 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
),
5042 "Failed to activate home dir, even after %u tries.", n_tries
);
5044 /* Let's start by checking if this all is even necessary, i.e. if the useFallback boolean field is actually set. */
5045 r
= bus_call_method(bus
, bus_mgr
, "GetUserRecordByName", &error
, &reply
, "s", NULL
); /* empty user string means: our calling user */
5047 return log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
5049 r
= sd_bus_message_read(reply
, "sbo", &json
, NULL
, NULL
);
5051 return bus_log_parse_error(r
);
5053 r
= sd_json_parse(json
, SD_JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
5055 return log_error_errno(r
, "Failed to parse JSON identity: %m");
5057 hr
= user_record_new();
5061 r
= user_record_load(hr
, v
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_LOG
|USER_RECORD_PERMISSIVE
);
5065 if (!hr
->use_fallback
) /* Nice! We are done, fallback logic not necessary */
5069 r
= acquire_passed_secrets(hr
->user_name
, &secret
);
5075 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
5077 r
= bus_message_new_method_call(bus
, &m
, bus_mgr
, "ActivateHomeIfReferenced");
5079 return bus_log_create_error(r
);
5081 r
= sd_bus_message_append(m
, "s", NULL
); /* empty user string means: our calling user */
5083 return bus_log_create_error(r
);
5085 r
= bus_message_append_secret(m
, secret
);
5087 return bus_log_create_error(r
);
5089 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
5091 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_NOT_REFERENCED
))
5092 return log_error_errno(r
, "Called without reference on home taken, can't operate.");
5094 r
= handle_generic_user_record_error(hr
->user_name
, secret
, &error
, r
, false);
5098 sd_bus_error_free(&error
);
5104 hr
= user_record_unref(hr
);
5107 incomplete
= getenv_bool("XDG_SESSION_INCOMPLETE"); /* pam_systemd_home reports this state via an environment variable to us. */
5108 if (incomplete
< 0 && incomplete
!= -ENXIO
)
5109 return log_error_errno(incomplete
, "Failed to parse $XDG_SESSION_INCOMPLETE environment variable: %m");
5110 if (incomplete
> 0) {
5111 /* We are still in an "incomplete" session here. Now upgrade it to a full one. This will make logind
5112 * start the user@.service instance for us. */
5113 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5114 r
= sd_bus_call_method(
5116 "org.freedesktop.login1",
5117 "/org/freedesktop/login1/session/self",
5118 "org.freedesktop.login1.Session",
5121 /* ret_reply= */ NULL
,
5125 return log_error_errno(r
, "Failed to upgrade session: %s", bus_error_message(&error
, r
));
5127 if (setenv("XDG_SESSION_CLASS", "user", /* overwrite= */ true) < 0) /* Update the XDG_SESSION_CLASS environment variable to match the above */
5128 return log_error_errno(errno
, "Failed to set $XDG_SESSION_CLASS: %m");
5130 if (unsetenv("XDG_SESSION_INCOMPLETE") < 0) /* Unset the 'incomplete' env var */
5131 return log_error_errno(errno
, "Failed to unset $XDG_SESSION_INCOMPLETE: %m");
5134 /* We are going to invoke execv() soon. Let's be extra accurate and flush/close our bus connection
5135 * first, just to make sure anything queued is flushed out (though there shouldn't be anything) */
5136 bus
= sd_bus_flush_close_unref(bus
);
5138 assert(!hr
->use_fallback
);
5139 assert_se(shell
= user_record_shell(hr
));
5140 assert_se(hd
= user_record_home_directory(hr
));
5142 /* Extra protection: avoid loops */
5143 if (is_fallback_shell(shell
))
5144 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Primary shell of '%s' is fallback shell, refusing loop.", hr
->user_name
);
5147 return log_error_errno(errno
, "Failed to change directory to home directory '%s': %m", hd
);
5149 if (setenv("SHELL", shell
, /* overwrite= */ true) < 0)
5150 return log_error_errno(errno
, "Failed to set $SHELL: %m");
5152 if (setenv("HOME", hd
, /* overwrite= */ true) < 0)
5153 return log_error_errno(errno
, "Failed to set $HOME: %m");
5155 /* Paranoia: in case the client passed some passwords to us to help us unlock, unlock things now */
5156 FOREACH_STRING(ue
, "PASSWORD", "NEWPASSWORD", "PIN")
5157 if (unsetenv(ue
) < 0)
5158 return log_error_errno(errno
, "Failed to unset $%s: %m", ue
);
5160 r
= path_extract_filename(shell
, &argv0
);
5162 return log_error_errno(r
, "Unable to extract file name from '%s': %m", shell
);
5163 if (r
== O_DIRECTORY
)
5164 return log_error_errno(SYNTHETIC_ERRNO(EISDIR
), "Shell '%s' is a path to a directory, refusing.", shell
);
5166 /* Invoke this as login shell, by setting argv[0][0] to '-' (unless we ourselves weren't called as login shell) */
5167 if (!argv
|| isempty(argv
[0]) || argv
[0][0] == '-') {
5168 _cleanup_free_
char *prefixed
= strjoin("-", argv0
);
5172 free_and_replace(argv0
, prefixed
);
5175 l
= strv_new(argv0
);
5179 if (strv_extend_strv(&l
, strv_skip(argv
, 1), /* filter_duplicates= */ false) < 0)
5183 return log_error_errno(errno
, "Failed to execute shell '%s': %m", shell
);
5186 static int verb_list_signing_keys(int argc
, char *argv
[], void *userdata
) {
5189 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5190 r
= acquire_bus(&bus
);
5194 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5195 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
5196 r
= bus_call_method(bus
, bus_mgr
, "ListSigningKeys", &error
, &reply
, NULL
);
5198 return log_error_errno(r
, "Failed to list signing keys: %s", bus_error_message(&error
, r
));
5200 _cleanup_(table_unrefp
) Table
*table
= table_new("name", "key");
5204 r
= sd_bus_message_enter_container(reply
, 'a', "(sst)");
5206 return bus_log_parse_error(r
);
5209 const char *name
, *pem
;
5211 r
= sd_bus_message_read(reply
, "(sst)", &name
, &pem
, NULL
);
5213 return bus_log_parse_error(r
);
5217 _cleanup_free_
char *h
= NULL
;
5218 if (!sd_json_format_enabled(arg_json_format_flags
)) {
5219 /* Let's decode the PEM key to DER (so that we lose prefix/suffix), then truncate it
5220 * for display reasons. */
5222 _cleanup_(EVP_PKEY_freep
) EVP_PKEY
*key
= NULL
;
5223 r
= openssl_pubkey_from_pem(pem
, SIZE_MAX
, &key
);
5225 return log_error_errno(r
, "Failed to parse PEM: %m");
5227 _cleanup_free_
void *der
= NULL
;
5228 int n
= i2d_PUBKEY(key
, (unsigned char**) &der
);
5230 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to encode key as DER: %m");
5232 ssize_t m
= base64mem(der
, MIN(n
, 64), &h
);
5235 if (n
> 64) /* check if we truncated the original version */
5236 if (!strextend(&h
, glyph(GLYPH_ELLIPSIS
)))
5243 TABLE_STRING
, h
?: pem
);
5245 return table_log_add_error(r
);
5248 r
= sd_bus_message_exit_container(reply
);
5250 return bus_log_parse_error(r
);
5252 if (!table_isempty(table
) || sd_json_format_enabled(arg_json_format_flags
)) {
5253 r
= table_set_sort(table
, (size_t) 0);
5255 return table_log_sort_error(r
);
5257 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, arg_legend
);
5262 if (arg_legend
&& !sd_json_format_enabled(arg_json_format_flags
)) {
5263 if (table_isempty(table
))
5264 printf("No signing keys.\n");
5266 printf("\n%zu signing keys listed.\n", table_get_rows(table
) - 1);
5272 static int verb_get_signing_key(int argc
, char *argv
[], void *userdata
) {
5275 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5276 r
= acquire_bus(&bus
);
5280 char **keys
= argc
>= 2 ? strv_skip(argv
, 1) : STRV_MAKE("local.public");
5282 STRV_FOREACH(k
, keys
) {
5283 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5284 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
5285 r
= bus_call_method(bus
, bus_mgr
, "GetSigningKey", &error
, &reply
, "s", *k
);
5287 RET_GATHER(ret
, log_error_errno(r
, "Failed to get signing key '%s': %s", *k
, bus_error_message(&error
, r
)));
5292 r
= sd_bus_message_read(reply
, "st", &pem
, NULL
);
5294 RET_GATHER(ret
, bus_log_parse_error(r
));
5299 if (!endswith(pem
, "\n"))
5300 fputc('\n', stdout
);
5308 static int add_signing_key_one(sd_bus
*bus
, const char *fn
, FILE *key
) {
5315 _cleanup_free_
char *pem
= NULL
;
5316 r
= read_full_stream(key
, &pem
, /* ret_size= */ NULL
);
5318 return log_error_errno(r
, "Failed to read key '%s': %m", fn
);
5320 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5321 r
= bus_call_method(bus
, bus_mgr
, "AddSigningKey", &error
, /* ret_reply= */ NULL
, "sst", fn
, pem
, UINT64_C(0));
5323 return log_error_errno(r
, "Failed to add signing key '%s': %s", fn
, bus_error_message(&error
, r
));
5328 static int verb_add_signing_key(int argc
, char *argv
[], void *userdata
) {
5331 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5332 r
= acquire_bus(&bus
);
5336 int ret
= EXIT_SUCCESS
;
5337 if (argc
< 2 || streq(argv
[1], "-")) {
5339 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Key name must be specified via --key-name= when reading key from standard input, refusing.");
5341 RET_GATHER(ret
, add_signing_key_one(bus
, arg_key_name
, stdin
));
5343 /* Refuse if more han one key is specified in combination with --key-name= */
5344 if (argc
>= 3 && arg_key_name
)
5345 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "--key-name= is not supported if multiple signing keys are specified, refusing.");
5347 STRV_FOREACH(k
, strv_skip(argv
, 1)) {
5350 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Refusing to read from standard input if multiple keys are specified.");
5352 _cleanup_free_
char *fn
= NULL
;
5353 if (!arg_key_name
) {
5354 r
= path_extract_filename(*k
, &fn
);
5356 RET_GATHER(ret
, log_error_errno(r
, "Failed to extract filename from path '%s': %m", *k
));
5361 _cleanup_fclose_
FILE *f
= fopen(*k
, "re");
5363 RET_GATHER(ret
, log_error_errno(errno
, "Failed to open '%s': %m", *k
));
5367 RET_GATHER(ret
, add_signing_key_one(bus
, fn
?: arg_key_name
, f
));
5374 static int add_signing_keys_from_credentials(void) {
5377 _cleanup_close_
int fd
= open_credentials_dir();
5378 if (IN_SET(fd
, -ENXIO
, -ENOENT
)) /* Credential env var not set, or dir doesn't exist. */
5381 return log_error_errno(fd
, "Failed to open credentials directory: %m");
5383 _cleanup_free_ DirectoryEntries
*des
= NULL
;
5384 r
= readdir_all(fd
, RECURSE_DIR_SORT
|RECURSE_DIR_IGNORE_DOT
|RECURSE_DIR_ENSURE_TYPE
, &des
);
5386 return log_error_errno(r
, "Failed to enumerate credentials: %m");
5389 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5390 FOREACH_ARRAY(i
, des
->entries
, des
->n_entries
) {
5391 struct dirent
*de
= *i
;
5392 if (de
->d_type
!= DT_REG
)
5395 const char *e
= startswith(de
->d_name
, "home.add-signing-key.");
5399 if (!filename_is_valid(e
))
5403 r
= acquire_bus(&bus
);
5408 _cleanup_fclose_
FILE *f
= NULL
;
5409 r
= xfopenat(fd
, de
->d_name
, "re", O_NOFOLLOW
, &f
);
5411 RET_GATHER(ret
, log_error_errno(r
, "Failed to open credential '%s': %m", de
->d_name
));
5415 RET_GATHER(ret
, add_signing_key_one(bus
, e
, f
));
5421 static int remove_signing_key_one(sd_bus
*bus
, const char *fn
) {
5427 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
5428 r
= bus_call_method(bus
, bus_mgr
, "RemoveSigningKey", &error
, /* ret_reply= */ NULL
, "st", fn
, UINT64_C(0));
5430 return log_error_errno(r
, "Failed to remove signing key '%s': %s", fn
, bus_error_message(&error
, r
));
5435 static int verb_remove_signing_key(int argc
, char *argv
[], void *userdata
) {
5438 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
5439 r
= acquire_bus(&bus
);
5444 STRV_FOREACH(k
, strv_skip(argv
, 1))
5445 RET_GATHER(r
, remove_signing_key_one(bus
, *k
));
5450 static int run(int argc
, char *argv
[]) {
5451 static const Verb verbs
[] = {
5452 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
5453 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_homes
},
5454 { "activate", 2, VERB_ANY
, 0, activate_home
},
5455 { "deactivate", 2, VERB_ANY
, 0, deactivate_home
},
5456 { "inspect", VERB_ANY
, VERB_ANY
, 0, inspect_homes
},
5457 { "authenticate", VERB_ANY
, VERB_ANY
, 0, authenticate_homes
},
5458 { "create", VERB_ANY
, 2, 0, create_home
},
5459 { "adopt", VERB_ANY
, VERB_ANY
, 0, verb_adopt_home
},
5460 { "register", VERB_ANY
, VERB_ANY
, 0, verb_register_home
},
5461 { "unregister", 2, VERB_ANY
, 0, verb_unregister_home
},
5462 { "remove", 2, VERB_ANY
, 0, remove_home
},
5463 { "update", VERB_ANY
, 2, 0, update_home
},
5464 { "passwd", VERB_ANY
, 2, 0, passwd_home
},
5465 { "resize", 2, 3, 0, resize_home
},
5466 { "lock", 2, VERB_ANY
, 0, lock_home
},
5467 { "unlock", 2, VERB_ANY
, 0, unlock_home
},
5468 { "with", 2, VERB_ANY
, 0, with_home
},
5469 { "lock-all", VERB_ANY
, 1, 0, lock_all_homes
},
5470 { "deactivate-all", VERB_ANY
, 1, 0, deactivate_all_homes
},
5471 { "rebalance", VERB_ANY
, 1, 0, rebalance
},
5472 { "firstboot", VERB_ANY
, 1, 0, verb_firstboot
},
5473 { "list-signing-keys", VERB_ANY
, 1, 0, verb_list_signing_keys
},
5474 { "get-signing-key", VERB_ANY
, VERB_ANY
, 0, verb_get_signing_key
},
5475 { "add-signing-key", VERB_ANY
, VERB_ANY
, 0, verb_add_signing_key
},
5476 { "remove-signing-key", 2, VERB_ANY
, 0, verb_remove_signing_key
},
5484 r
= redirect_bus_mgr();
5488 if (is_fallback_shell(argv
[0]))
5489 return fallback_shell(argc
, argv
);
5491 r
= parse_argv(argc
, argv
);
5495 return dispatch_verb(argc
, argv
, verbs
, NULL
);
5498 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);