1 /* SPDX-License-Identifier: LGPL-2.1+ */
7 #include "ask-password-api.h"
8 #include "bus-common-errors.h"
10 #include "bus-locator.h"
11 #include "cgroup-util.h"
12 #include "dns-domain.h"
16 #include "format-table.h"
17 #include "home-util.h"
18 #include "homectl-fido2.h"
19 #include "homectl-pkcs11.h"
20 #include "locale-util.h"
21 #include "main-func.h"
22 #include "memory-util.h"
24 #include "parse-util.h"
25 #include "path-util.h"
26 #include "pkcs11-util.h"
27 #include "pretty-print.h"
28 #include "process-util.h"
29 #include "pwquality-util.h"
30 #include "rlimit-util.h"
31 #include "spawn-polkit-agent.h"
32 #include "terminal-util.h"
33 #include "user-record-show.h"
34 #include "user-record-util.h"
35 #include "user-record.h"
36 #include "user-util.h"
39 static PagerFlags arg_pager_flags
= 0;
40 static bool arg_legend
= true;
41 static bool arg_ask_password
= true;
42 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
43 static const char *arg_host
= NULL
;
44 static const char *arg_identity
= NULL
;
45 static JsonVariant
*arg_identity_extra
= NULL
;
46 static JsonVariant
*arg_identity_extra_privileged
= NULL
;
47 static JsonVariant
*arg_identity_extra_this_machine
= NULL
;
48 static JsonVariant
*arg_identity_extra_rlimits
= NULL
;
49 static char **arg_identity_filter
= NULL
; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
50 static char **arg_identity_filter_rlimits
= NULL
;
51 static uint64_t arg_disk_size
= UINT64_MAX
;
52 static uint64_t arg_disk_size_relative
= UINT64_MAX
;
53 static char **arg_pkcs11_token_uri
= NULL
;
54 static char **arg_fido2_device
= NULL
;
55 static bool arg_json
= false;
56 static JsonFormatFlags arg_json_format_flags
= 0;
57 static bool arg_and_resize
= false;
58 static bool arg_and_change_password
= false;
60 EXPORT_FORMAT_FULL
, /* export the full record */
61 EXPORT_FORMAT_STRIPPED
, /* strip "state" + "binding", but leave signature in place */
62 EXPORT_FORMAT_MINIMAL
, /* also strip signature */
63 } arg_export_format
= EXPORT_FORMAT_FULL
;
65 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra
, json_variant_unrefp
);
66 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine
, json_variant_unrefp
);
67 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged
, json_variant_unrefp
);
68 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits
, json_variant_unrefp
);
69 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter
, strv_freep
);
70 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits
, strv_freep
);
71 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri
, strv_freep
);
72 STATIC_DESTRUCTOR_REGISTER(arg_fido2_device
, strv_freep
);
74 static bool identity_properties_specified(void) {
77 !json_variant_is_blank_object(arg_identity_extra
) ||
78 !json_variant_is_blank_object(arg_identity_extra_privileged
) ||
79 !json_variant_is_blank_object(arg_identity_extra_this_machine
) ||
80 !json_variant_is_blank_object(arg_identity_extra_rlimits
) ||
81 !strv_isempty(arg_identity_filter
) ||
82 !strv_isempty(arg_identity_filter_rlimits
) ||
83 !strv_isempty(arg_pkcs11_token_uri
) ||
84 !strv_isempty(arg_fido2_device
);
87 static int acquire_bus(sd_bus
**bus
) {
95 r
= bus_connect_transport(arg_transport
, arg_host
, false, bus
);
97 return log_error_errno(r
, "Failed to connect to bus: %m");
99 (void) sd_bus_set_allow_interactive_authorization(*bus
, arg_ask_password
);
104 static int list_homes(int argc
, char *argv
[], void *userdata
) {
105 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
106 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
107 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
108 _cleanup_(table_unrefp
) Table
*table
= NULL
;
111 (void) pager_open(arg_pager_flags
);
113 r
= acquire_bus(&bus
);
117 r
= bus_call_method(bus
, bus_home_mgr
, "ListHomes", &error
, &reply
, NULL
);
119 return log_error_errno(r
, "Failed to list homes: %s", bus_error_message(&error
, r
));
121 table
= table_new("name", "uid", "gid", "state", "realname", "home", "shell");
125 r
= sd_bus_message_enter_container(reply
, 'a', "(susussso)");
127 return bus_log_parse_error(r
);
130 const char *name
, *state
, *realname
, *home
, *shell
, *color
;
134 r
= sd_bus_message_read(reply
, "(susussso)", &name
, &uid
, &state
, &gid
, &realname
, &home
, &shell
, NULL
);
136 return bus_log_parse_error(r
);
140 r
= table_add_many(table
,
145 return table_log_add_error(r
);
148 r
= table_add_cell(table
, &cell
, TABLE_STRING
, state
);
150 return table_log_add_error(r
);
152 color
= user_record_state_color(state
);
154 (void) table_set_color(table
, cell
, color
);
156 r
= table_add_many(table
,
157 TABLE_STRING
, strna(empty_to_null(realname
)),
159 TABLE_STRING
, strna(empty_to_null(shell
)));
161 return table_log_add_error(r
);
164 r
= sd_bus_message_exit_container(reply
);
166 return bus_log_parse_error(r
);
168 if (table_get_rows(table
) > 1 || arg_json
) {
169 r
= table_set_sort(table
, (size_t) 0, (size_t) -1);
171 return log_error_errno(r
, "Failed to sort table: %m");
173 table_set_header(table
, arg_legend
);
176 r
= table_print_json(table
, stdout
, arg_json_format_flags
);
178 r
= table_print(table
, NULL
);
180 return log_error_errno(r
, "Failed to show table: %m");
183 if (arg_legend
&& !arg_json
) {
184 if (table_get_rows(table
) > 1)
185 printf("\n%zu home areas listed.\n", table_get_rows(table
) - 1);
187 printf("No home areas.\n");
193 static int acquire_existing_password(const char *user_name
, UserRecord
*hr
, bool emphasize_current
) {
194 _cleanup_(strv_free_erasep
) char **password
= NULL
;
195 _cleanup_free_
char *question
= NULL
;
202 e
= getenv("PASSWORD");
204 /* People really shouldn't use environment variables for passing passwords. We support this
205 * only for testing purposes, and do not document the behaviour, so that people won't
206 * actually use this outside of testing. */
208 r
= user_record_set_password(hr
, STRV_MAKE(e
), true);
210 return log_error_errno(r
, "Failed to store password: %m");
214 if (unsetenv("PASSWORD") < 0)
215 return log_error_errno(errno
, "Failed to unset $PASSWORD: %m");
220 if (asprintf(&question
, emphasize_current
?
221 "Please enter current password for user %s:" :
222 "Please enter password for user %s:",
226 r
= ask_password_auto(question
, "user-home", NULL
, "home-password", USEC_INFINITY
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, &password
);
228 return log_error_errno(r
, "Failed to acquire password: %m");
230 r
= user_record_set_password(hr
, password
, true);
232 return log_error_errno(r
, "Failed to store password: %m");
237 static int acquire_token_pin(const char *user_name
, UserRecord
*hr
) {
238 _cleanup_(strv_free_erasep
) char **pin
= NULL
;
239 _cleanup_free_
char *question
= NULL
;
248 r
= user_record_set_token_pin(hr
, STRV_MAKE(e
), false);
250 return log_error_errno(r
, "Failed to store token PIN: %m");
254 if (unsetenv("PIN") < 0)
255 return log_error_errno(errno
, "Failed to unset $PIN: %m");
260 if (asprintf(&question
, "Please enter security token PIN for user %s:", user_name
) < 0)
263 /* We never cache or use cached PINs, since usually there are only very few attempts allowed before the PIN is blocked */
264 r
= ask_password_auto(question
, "user-home", NULL
, "token-pin", USEC_INFINITY
, 0, &pin
);
266 return log_error_errno(r
, "Failed to acquire security token PIN: %m");
268 r
= user_record_set_token_pin(hr
, pin
, false);
270 return log_error_errno(r
, "Failed to store security token PIN: %m");
275 static int handle_generic_user_record_error(
276 const char *user_name
,
278 const sd_bus_error
*error
,
280 bool emphasize_current_password
) {
286 if (sd_bus_error_has_name(error
, BUS_ERROR_HOME_ABSENT
))
287 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE
),
288 "Home of user %s is currently absent, please plug in the necessary storage device or backing file system.", user_name
);
290 else if (sd_bus_error_has_name(error
, BUS_ERROR_AUTHENTICATION_LIMIT_HIT
))
291 return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS
),
292 "Too frequent unsuccessful login attempts for user %s, try again later.", user_name
);
294 else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD
)) {
296 if (!strv_isempty(hr
->password
))
297 log_notice("Password incorrect or not sufficient, please try again.");
299 r
= acquire_existing_password(user_name
, hr
, emphasize_current_password
);
303 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
)) {
305 if (strv_isempty(hr
->password
))
306 log_notice("Security token not inserted, please enter password.");
308 log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
310 r
= acquire_existing_password(user_name
, hr
, emphasize_current_password
);
314 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_NEEDED
)) {
316 r
= acquire_token_pin(user_name
, hr
);
320 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED
)) {
322 log_notice("%s%sPlease authenticate physically on security token.",
323 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
324 emoji_enabled() ? " " : "");
326 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, true);
328 return log_error_errno(r
, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
330 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED
)) {
332 log_notice("%s%sAuthentication requires presence verification on security token.",
333 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH
) : "",
334 emoji_enabled() ? " " : "");
336 r
= user_record_set_fido2_user_presence_permitted(hr
, true);
338 return log_error_errno(r
, "Failed to set FIDO2 user presence permitted flag: %m");
340 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_LOCKED
))
341 return log_error_errno(SYNTHETIC_ERRNO(EPERM
), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
343 else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN
)) {
345 log_notice("Security token PIN incorrect, please try again.");
347 r
= acquire_token_pin(user_name
, hr
);
351 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT
)) {
353 log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
355 r
= acquire_token_pin(user_name
, hr
);
359 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT
)) {
361 log_notice("Security token PIN incorrect, please try again (only one try left!).");
363 r
= acquire_token_pin(user_name
, hr
);
367 return log_error_errno(ret
, "Operation on home %s failed: %s", user_name
, bus_error_message(error
, ret
));
372 static int activate_home(int argc
, char *argv
[], void *userdata
) {
373 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
377 r
= acquire_bus(&bus
);
381 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
382 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
384 secret
= user_record_new();
389 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
390 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
392 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ActivateHome");
394 return bus_log_create_error(r
);
396 r
= sd_bus_message_append(m
, "s", *i
);
398 return bus_log_create_error(r
);
400 r
= bus_message_append_secret(m
, secret
);
402 return bus_log_create_error(r
);
404 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
406 r
= handle_generic_user_record_error(*i
, secret
, &error
, r
, false);
421 static int deactivate_home(int argc
, char *argv
[], void *userdata
) {
422 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
426 r
= acquire_bus(&bus
);
430 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
431 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
432 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
434 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "DeactivateHome");
436 return bus_log_create_error(r
);
438 r
= sd_bus_message_append(m
, "s", *i
);
440 return bus_log_create_error(r
);
442 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
444 log_error_errno(r
, "Failed to deactivate user home: %s", bus_error_message(&error
, r
));
453 static void dump_home_record(UserRecord
*hr
) {
458 if (hr
->incomplete
) {
460 log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr
->user_name
);
464 _cleanup_(user_record_unrefp
) UserRecord
*stripped
= NULL
;
466 if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
467 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_EMBEDDED
, &stripped
);
468 else if (arg_export_format
== EXPORT_FORMAT_MINIMAL
)
469 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_SIGNABLE
, &stripped
);
473 log_warning_errno(r
, "Failed to strip user record, ignoring: %m");
477 json_variant_dump(hr
->json
, arg_json_format_flags
, stdout
, NULL
);
479 user_record_show(hr
, true);
482 static char **mangle_user_list(char **list
, char ***ret_allocated
) {
483 _cleanup_free_
char *myself
= NULL
;
486 if (!strv_isempty(list
)) {
487 *ret_allocated
= NULL
;
491 myself
= getusername_malloc();
499 l
[0] = TAKE_PTR(myself
);
506 static int inspect_home(int argc
, char *argv
[], void *userdata
) {
507 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
508 _cleanup_(strv_freep
) char **mangled_list
= NULL
;
512 (void) pager_open(arg_pager_flags
);
514 r
= acquire_bus(&bus
);
518 items
= mangle_user_list(strv_skip(argv
, 1), &mangled_list
);
522 STRV_FOREACH(i
, items
) {
523 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
524 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
525 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
526 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
531 r
= parse_uid(*i
, &uid
);
533 if (!valid_user_group_name(*i
, 0)) {
534 log_error("Invalid user name '%s'.", *i
);
541 r
= bus_call_method(bus
, bus_home_mgr
, "GetUserRecordByName", &error
, &reply
, "s", *i
);
543 r
= bus_call_method(bus
, bus_home_mgr
, "GetUserRecordByUID", &error
, &reply
, "u", (uint32_t) uid
);
546 log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
553 r
= sd_bus_message_read(reply
, "sbo", &json
, &incomplete
, NULL
);
555 bus_log_parse_error(r
);
562 r
= json_parse(json
, JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
564 log_error_errno(r
, "Failed to parse JSON identity: %m");
571 hr
= user_record_new();
575 r
= user_record_load(hr
, v
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_LOG
);
583 hr
->incomplete
= incomplete
;
584 dump_home_record(hr
);
590 static int authenticate_home(int argc
, char *argv
[], void *userdata
) {
591 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
592 _cleanup_(strv_freep
) char **mangled_list
= NULL
;
596 items
= mangle_user_list(strv_skip(argv
, 1), &mangled_list
);
600 r
= acquire_bus(&bus
);
604 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
606 STRV_FOREACH(i
, items
) {
607 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
609 secret
= user_record_new();
614 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
615 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
617 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "AuthenticateHome");
619 return bus_log_create_error(r
);
621 r
= sd_bus_message_append(m
, "s", *i
);
623 return bus_log_create_error(r
);
625 r
= bus_message_append_secret(m
, secret
);
627 return bus_log_create_error(r
);
629 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
631 r
= handle_generic_user_record_error(*i
, secret
, &error
, r
, false);
646 static int update_last_change(JsonVariant
**v
, bool with_password
, bool override
) {
653 n
= now(CLOCK_REALTIME
);
655 c
= json_variant_by_key(*v
, "lastChangeUSec");
660 goto update_password
;
662 if (!json_variant_is_unsigned(c
))
663 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec field is not an unsigned integer, refusing.");
665 u
= json_variant_unsigned(c
);
667 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec is from the future, can't update.");
670 r
= json_variant_set_field_unsigned(v
, "lastChangeUSec", n
);
672 return log_error_errno(r
, "Failed to update lastChangeUSec: %m");
678 c
= json_variant_by_key(*v
, "lastPasswordChangeUSec");
685 if (!json_variant_is_unsigned(c
))
686 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec field is not an unsigned integer, refusing.");
688 u
= json_variant_unsigned(c
);
690 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec is from the future, can't update.");
693 r
= json_variant_set_field_unsigned(v
, "lastPasswordChangeUSec", n
);
695 return log_error_errno(r
, "Failed to update lastPasswordChangeUSec: %m");
700 static int apply_identity_changes(JsonVariant
**_v
) {
701 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
706 v
= json_variant_ref(*_v
);
708 r
= json_variant_filter(&v
, arg_identity_filter
);
710 return log_error_errno(r
, "Failed to filter identity: %m");
712 r
= json_variant_merge(&v
, arg_identity_extra
);
714 return log_error_errno(r
, "Failed to merge identities: %m");
716 if (arg_identity_extra_this_machine
|| !strv_isempty(arg_identity_filter
)) {
717 _cleanup_(json_variant_unrefp
) JsonVariant
*per_machine
= NULL
, *mmid
= NULL
;
718 char mids
[SD_ID128_STRING_MAX
];
721 r
= sd_id128_get_machine(&mid
);
723 return log_error_errno(r
, "Failed to acquire machine ID: %m");
725 r
= json_variant_new_string(&mmid
, sd_id128_to_string(mid
, mids
));
727 return log_error_errno(r
, "Failed to allocate matchMachineId object: %m");
729 per_machine
= json_variant_ref(json_variant_by_key(v
, "perMachine"));
731 _cleanup_(json_variant_unrefp
) JsonVariant
*npm
= NULL
, *add
= NULL
;
732 _cleanup_free_ JsonVariant
**array
= NULL
;
736 if (!json_variant_is_array(per_machine
))
737 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine field is not an array, refusing.");
739 array
= new(JsonVariant
*, json_variant_elements(per_machine
) + 1);
743 JSON_VARIANT_ARRAY_FOREACH(z
, per_machine
) {
746 if (!json_variant_is_object(z
))
747 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine entry is not an object, refusing.");
751 u
= json_variant_by_key(z
, "matchMachineId");
755 if (!json_variant_equal(u
, mmid
))
758 r
= json_variant_merge(&add
, z
);
760 return log_error_errno(r
, "Failed to merge perMachine entry: %m");
765 r
= json_variant_filter(&add
, arg_identity_filter
);
767 return log_error_errno(r
, "Failed to filter perMachine: %m");
769 r
= json_variant_merge(&add
, arg_identity_extra_this_machine
);
771 return log_error_errno(r
, "Failed to merge in perMachine fields: %m");
773 if (arg_identity_filter_rlimits
|| arg_identity_extra_rlimits
) {
774 _cleanup_(json_variant_unrefp
) JsonVariant
*rlv
= NULL
;
776 rlv
= json_variant_ref(json_variant_by_key(add
, "resourceLimits"));
778 r
= json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
780 return log_error_errno(r
, "Failed to filter resource limits: %m");
782 r
= json_variant_merge(&rlv
, arg_identity_extra_rlimits
);
784 return log_error_errno(r
, "Failed to set resource limits: %m");
786 if (json_variant_is_blank_object(rlv
)) {
787 r
= json_variant_filter(&add
, STRV_MAKE("resourceLimits"));
789 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
791 r
= json_variant_set_field(&add
, "resourceLimits", rlv
);
793 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
797 if (!json_variant_is_blank_object(add
)) {
798 r
= json_variant_set_field(&add
, "matchMachineId", mmid
);
800 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
805 r
= json_variant_new_array(&npm
, array
, i
);
807 return log_error_errno(r
, "Failed to allocate new perMachine array: %m");
809 json_variant_unref(per_machine
);
810 per_machine
= TAKE_PTR(npm
);
812 _cleanup_(json_variant_unrefp
) JsonVariant
*item
= json_variant_ref(arg_identity_extra_this_machine
);
814 if (arg_identity_extra_rlimits
) {
815 r
= json_variant_set_field(&item
, "resourceLimits", arg_identity_extra_rlimits
);
817 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
820 r
= json_variant_set_field(&item
, "matchMachineId", mmid
);
822 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
824 r
= json_variant_append_array(&per_machine
, item
);
826 return log_error_errno(r
, "Failed to append to perMachine array: %m");
829 r
= json_variant_set_field(&v
, "perMachine", per_machine
);
831 return log_error_errno(r
, "Failed to update per machine record: %m");
834 if (arg_identity_extra_privileged
|| arg_identity_filter
) {
835 _cleanup_(json_variant_unrefp
) JsonVariant
*privileged
= NULL
;
837 privileged
= json_variant_ref(json_variant_by_key(v
, "privileged"));
839 r
= json_variant_filter(&privileged
, arg_identity_filter
);
841 return log_error_errno(r
, "Failed to filter identity (privileged part): %m");
843 r
= json_variant_merge(&privileged
, arg_identity_extra_privileged
);
845 return log_error_errno(r
, "Failed to merge identities (privileged part): %m");
847 if (json_variant_is_blank_object(privileged
)) {
848 r
= json_variant_filter(&v
, STRV_MAKE("privileged"));
850 return log_error_errno(r
, "Failed to drop privileged part from identity: %m");
852 r
= json_variant_set_field(&v
, "privileged", privileged
);
854 return log_error_errno(r
, "Failed to update privileged part of identity: %m");
858 if (arg_identity_filter_rlimits
) {
859 _cleanup_(json_variant_unrefp
) JsonVariant
*rlv
= NULL
;
861 rlv
= json_variant_ref(json_variant_by_key(v
, "resourceLimits"));
863 r
= json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
865 return log_error_errno(r
, "Failed to filter resource limits: %m");
867 /* Note that we only filter resource limits here, but don't apply them. We do that in the perMachine section */
869 if (json_variant_is_blank_object(rlv
)) {
870 r
= json_variant_filter(&v
, STRV_MAKE("resourceLimits"));
872 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
874 r
= json_variant_set_field(&v
, "resourceLimits", rlv
);
876 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
880 json_variant_unref(*_v
);
886 static int add_disposition(JsonVariant
**v
) {
891 if (json_variant_by_key(*v
, "disposition"))
894 /* Set the disposition to regular, if not configured explicitly */
895 r
= json_variant_set_field_string(v
, "disposition", "regular");
897 return log_error_errno(r
, "Failed to set disposition field: %m");
902 static int acquire_new_home_record(UserRecord
**ret
) {
903 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
904 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
911 unsigned line
, column
;
914 streq(arg_identity
, "-") ? stdin
: NULL
,
915 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, JSON_PARSE_SENSITIVE
, &v
, &line
, &column
);
917 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
920 r
= apply_identity_changes(&v
);
924 r
= add_disposition(&v
);
928 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
929 r
= identity_add_pkcs11_key_data(&v
, *i
);
934 STRV_FOREACH(i
, arg_fido2_device
) {
935 r
= identity_add_fido2_parameters(&v
, *i
);
940 r
= update_last_change(&v
, true, false);
945 json_variant_dump(v
, JSON_FORMAT_PRETTY
, NULL
, NULL
);
947 hr
= user_record_new();
951 r
= user_record_load(hr
, v
, USER_RECORD_REQUIRE_REGULAR
|USER_RECORD_ALLOW_SECRET
|USER_RECORD_ALLOW_PRIVILEGED
|USER_RECORD_ALLOW_PER_MACHINE
|USER_RECORD_ALLOW_SIGNATURE
|USER_RECORD_LOG
);
959 static int acquire_new_password(
960 const char *user_name
,
971 e
= getenv("NEWPASSWORD");
973 /* As above, this is not for use, just for testing */
975 r
= user_record_set_password(hr
, STRV_MAKE(e
), /* prepend = */ false);
977 return log_error_errno(r
, "Failed to store password: %m");
981 if (unsetenv("NEWPASSWORD") < 0)
982 return log_error_errno(errno
, "Failed to unset $NEWPASSWORD: %m");
988 (void) suggest_passwords();
991 _cleanup_(strv_free_erasep
) char **first
= NULL
, **second
= NULL
;
992 _cleanup_free_
char *question
= NULL
;
995 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY
), "Too many attempts, giving up:");
997 if (asprintf(&question
, "Please enter new password for user %s:", user_name
) < 0)
1000 r
= ask_password_auto(question
, "user-home", NULL
, "home-password", USEC_INFINITY
, 0, &first
);
1002 return log_error_errno(r
, "Failed to acquire password: %m");
1004 question
= mfree(question
);
1005 if (asprintf(&question
, "Please enter new password for user %s (repeat):", user_name
) < 0)
1008 r
= ask_password_auto(question
, "user-home", NULL
, "home-password", USEC_INFINITY
, 0, &second
);
1010 return log_error_errno(r
, "Failed to acquire password: %m");
1012 if (strv_equal(first
, second
)) {
1013 r
= user_record_set_password(hr
, first
, /* prepend = */ false);
1015 return log_error_errno(r
, "Failed to store password: %m");
1020 log_error("Password didn't match, try again.");
1024 static int create_home(int argc
, char *argv
[], void *userdata
) {
1025 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1026 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1027 _cleanup_strv_free_
char **original_hashed_passwords
= NULL
;
1030 r
= acquire_bus(&bus
);
1034 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1037 /* If a username was specified, use it */
1039 if (valid_user_group_name(argv
[1], 0))
1040 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", argv
[1]);
1042 _cleanup_free_
char *un
= NULL
, *rr
= NULL
;
1044 /* Before we consider the user name invalid, let's check if we can split it? */
1045 r
= split_user_name_realm(argv
[1], &un
, &rr
);
1047 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name '%s' is not valid: %m", argv
[1]);
1050 r
= json_variant_set_field_string(&arg_identity_extra
, "realm", rr
);
1052 return log_error_errno(r
, "Failed to set realm field: %m");
1055 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", un
);
1058 return log_error_errno(r
, "Failed to set userName field: %m");
1060 /* If neither a username nor an identity have been specified we cannot operate. */
1062 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name required.");
1065 r
= acquire_new_home_record(&hr
);
1069 /* Remember the original hashed passwords before we add our own, so that we can return to them later,
1070 * should the entered password turn out not to be acceptable. */
1071 original_hashed_passwords
= strv_copy(hr
->hashed_password
);
1072 if (!original_hashed_passwords
)
1075 /* If the JSON record carries no plain text password, then let's query it manually. */
1076 if (!hr
->password
) {
1078 if (strv_isempty(hr
->hashed_password
)) {
1079 /* No regular (i.e. non-PKCS#11) hashed passwords set in the record, let's fix that. */
1080 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ true);
1084 r
= user_record_make_hashed_password(hr
, hr
->password
, /* extend = */ true);
1086 return log_error_errno(r
, "Failed to hash password: %m");
1088 /* There's a hash password set in the record, acquire the unhashed version of it. */
1089 r
= acquire_existing_password(hr
->user_name
, hr
, false);
1095 if (hr
->enforce_password_policy
== 0) {
1096 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1098 /* If password quality enforcement is disabled, let's at least warn client side */
1100 r
= quality_check_password(hr
, hr
, &error
);
1102 log_warning_errno(r
, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error
, r
));
1106 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1107 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1108 _cleanup_(erase_and_freep
) char *formatted
= NULL
;
1110 r
= json_variant_format(hr
->json
, 0, &formatted
);
1112 return log_error_errno(r
, "Failed to format user record: %m");
1114 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "CreateHome");
1116 return bus_log_create_error(r
);
1118 (void) sd_bus_message_sensitive(m
);
1120 r
= sd_bus_message_append(m
, "s", formatted
);
1122 return bus_log_create_error(r
);
1124 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1126 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
1127 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
1128 log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
1130 r
= user_record_set_hashed_password(hr
, original_hashed_passwords
);
1134 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ false);
1138 r
= user_record_make_hashed_password(hr
, hr
->password
, /* extend = */ true);
1140 return log_error_errno(r
, "Failed to hash passwords: %m");
1142 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1153 static int remove_home(int argc
, char *argv
[], void *userdata
) {
1154 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1158 r
= acquire_bus(&bus
);
1162 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1164 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1165 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1166 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1168 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "RemoveHome");
1170 return bus_log_create_error(r
);
1172 r
= sd_bus_message_append(m
, "s", *i
);
1174 return bus_log_create_error(r
);
1176 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1178 log_error_errno(r
, "Failed to remove home: %s", bus_error_message(&error
, r
));
1187 static int acquire_updated_home_record(
1189 const char *username
,
1192 _cleanup_(json_variant_unrefp
) JsonVariant
*json
= NULL
;
1193 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1200 unsigned line
, column
;
1203 r
= json_parse_file(
1204 streq(arg_identity
, "-") ? stdin
: NULL
,
1205 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, JSON_PARSE_SENSITIVE
, &json
, &line
, &column
);
1207 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
1209 un
= json_variant_by_key(json
, "userName");
1211 if (!json_variant_is_string(un
) || (username
&& !streq(json_variant_string(un
), username
)))
1212 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name specified on command line and in JSON record do not match.");
1215 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No username specified.");
1217 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", username
);
1219 return log_error_errno(r
, "Failed to set userName field: %m");
1223 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1224 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1228 if (!identity_properties_specified())
1229 return log_error_errno(SYNTHETIC_ERRNO(EALREADY
), "No field to change specified.");
1231 r
= bus_call_method(bus
, bus_home_mgr
, "GetUserRecordByName", &error
, &reply
, "s", username
);
1233 return log_error_errno(r
, "Failed to acquire user home record: %s", bus_error_message(&error
, r
));
1235 r
= sd_bus_message_read(reply
, "sbo", &text
, &incomplete
, NULL
);
1237 return bus_log_parse_error(r
);
1240 return log_error_errno(SYNTHETIC_ERRNO(EACCES
), "Lacking rights to acquire user record including privileged metadata, can't update record.");
1242 r
= json_parse(text
, JSON_PARSE_SENSITIVE
, &json
, NULL
, NULL
);
1244 return log_error_errno(r
, "Failed to parse JSON identity: %m");
1246 reply
= sd_bus_message_unref(reply
);
1248 r
= json_variant_filter(&json
, STRV_MAKE("binding", "status", "signature"));
1250 return log_error_errno(r
, "Failed to strip binding and status from record to update: %m");
1253 r
= apply_identity_changes(&json
);
1257 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
1258 r
= identity_add_pkcs11_key_data(&json
, *i
);
1263 STRV_FOREACH(i
, arg_fido2_device
) {
1264 r
= identity_add_fido2_parameters(&json
, *i
);
1269 /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
1271 r
= update_last_change(&json
, arg_pkcs11_token_uri
|| arg_fido2_device
, !arg_identity
);
1276 json_variant_dump(json
, JSON_FORMAT_PRETTY
, NULL
, NULL
);
1278 hr
= user_record_new();
1282 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
);
1286 *ret
= TAKE_PTR(hr
);
1290 static int home_record_reset_human_interaction_permission(UserRecord
*hr
) {
1295 /* When we execute multiple operations one after the other, let's reset the permission to ask the
1296 * user each time, so that if interaction is necessary we will be told so again and thus can print a
1297 * nice message to the user, telling the user so. */
1299 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, -1);
1301 return log_error_errno(r
, "Failed to reset PKCS#11 protected authentication path permission flag: %m");
1303 r
= user_record_set_fido2_user_presence_permitted(hr
, -1);
1305 return log_error_errno(r
, "Failed to reset FIDO2 user presence permission flag: %m");
1310 static int update_home(int argc
, char *argv
[], void *userdata
) {
1311 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1312 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1313 _cleanup_free_
char *buffer
= NULL
;
1314 const char *username
;
1319 else if (!arg_identity
) {
1320 buffer
= getusername_malloc();
1328 r
= acquire_bus(&bus
);
1332 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1334 r
= acquire_updated_home_record(bus
, username
, &hr
);
1338 /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
1339 * authentication might be confusing. */
1341 if (arg_and_resize
|| arg_and_change_password
)
1342 log_info("Updating home directory.");
1345 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1346 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1347 _cleanup_free_
char *formatted
= NULL
;
1349 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "UpdateHome");
1351 return bus_log_create_error(r
);
1353 r
= json_variant_format(hr
->json
, 0, &formatted
);
1355 return log_error_errno(r
, "Failed to format user record: %m");
1357 (void) sd_bus_message_sensitive(m
);
1359 r
= sd_bus_message_append(m
, "s", formatted
);
1361 return bus_log_create_error(r
);
1363 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1365 if (arg_and_change_password
&&
1366 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1367 /* In the generic handler we'd ask for a password in this case, but when
1368 * changing passwords that's not sufficient, as we need to acquire all keys
1370 return log_error_errno(r
, "Security token not inserted, refusing.");
1372 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1380 log_info("Resizing home.");
1382 (void) home_record_reset_human_interaction_permission(hr
);
1384 /* Also sync down disk size to underlying LUKS/fscrypt/quota */
1385 while (arg_and_resize
) {
1386 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1387 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1389 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ResizeHome");
1391 return bus_log_create_error(r
);
1393 /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
1394 r
= sd_bus_message_append(m
, "st", hr
->user_name
, UINT64_MAX
);
1396 return bus_log_create_error(r
);
1398 r
= bus_message_append_secret(m
, hr
);
1400 return bus_log_create_error(r
);
1402 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1404 if (arg_and_change_password
&&
1405 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1406 return log_error_errno(r
, "Security token not inserted, refusing.");
1408 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1415 if (arg_and_change_password
)
1416 log_info("Synchronizing passwords and encryption keys.");
1418 (void) home_record_reset_human_interaction_permission(hr
);
1420 /* Also sync down passwords to underlying LUKS/fscrypt */
1421 while (arg_and_change_password
) {
1422 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1423 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1425 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ChangePasswordHome");
1427 return bus_log_create_error(r
);
1429 /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
1430 r
= sd_bus_message_append(m
, "ss", hr
->user_name
, "{}");
1432 return bus_log_create_error(r
);
1434 r
= bus_message_append_secret(m
, hr
);
1436 return bus_log_create_error(r
);
1438 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1440 if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1441 return log_error_errno(r
, "Security token not inserted, refusing.");
1443 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1453 static int passwd_home(int argc
, char *argv
[], void *userdata
) {
1454 _cleanup_(user_record_unrefp
) UserRecord
*old_secret
= NULL
, *new_secret
= NULL
;
1455 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1456 _cleanup_free_
char *buffer
= NULL
;
1457 const char *username
;
1460 if (arg_pkcs11_token_uri
)
1461 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=…'.");
1462 if (arg_fido2_device
)
1463 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "To change the FIDO2 security token use 'homectl update --fido2-device=…'.");
1464 if (identity_properties_specified())
1465 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "The 'passwd' verb does not permit changing other record properties at the same time.");
1470 buffer
= getusername_malloc();
1477 r
= acquire_bus(&bus
);
1481 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1483 old_secret
= user_record_new();
1487 new_secret
= user_record_new();
1491 r
= acquire_new_password(username
, new_secret
, /* suggest = */ true);
1496 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1497 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1499 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ChangePasswordHome");
1501 return bus_log_create_error(r
);
1503 r
= sd_bus_message_append(m
, "s", username
);
1505 return bus_log_create_error(r
);
1507 r
= bus_message_append_secret(m
, new_secret
);
1509 return bus_log_create_error(r
);
1511 r
= bus_message_append_secret(m
, old_secret
);
1513 return bus_log_create_error(r
);
1515 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1517 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
1519 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
1521 r
= acquire_new_password(username
, new_secret
, /* suggest = */ false);
1523 } else if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1525 /* In the generic handler we'd ask for a password in this case, but when
1526 * changing passwords that's not sufficeint, as we need to acquire all keys
1528 return log_error_errno(r
, "Security token not inserted, refusing.");
1530 r
= handle_generic_user_record_error(username
, old_secret
, &error
, r
, true);
1540 static int resize_home(int argc
, char *argv
[], void *userdata
) {
1541 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1542 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
1543 uint64_t ds
= UINT64_MAX
;
1546 r
= acquire_bus(&bus
);
1550 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1552 if (arg_disk_size_relative
!= UINT64_MAX
||
1553 (argc
> 2 && parse_percent(argv
[2]) >= 0))
1554 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1555 "Relative disk size specification currently not supported when resizing.");
1558 r
= parse_size(argv
[2], 1024, &ds
);
1560 return log_error_errno(r
, "Failed to parse disk size parameter: %s", argv
[2]);
1563 if (arg_disk_size
!= UINT64_MAX
) {
1564 if (ds
!= UINT64_MAX
&& ds
!= arg_disk_size
)
1565 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Disk size specified twice and doesn't match, refusing.");
1570 secret
= user_record_new();
1575 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1576 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1578 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ResizeHome");
1580 return bus_log_create_error(r
);
1582 r
= sd_bus_message_append(m
, "st", argv
[1], ds
);
1584 return bus_log_create_error(r
);
1586 r
= bus_message_append_secret(m
, secret
);
1588 return bus_log_create_error(r
);
1590 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1592 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
1602 static int lock_home(int argc
, char *argv
[], void *userdata
) {
1603 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1607 r
= acquire_bus(&bus
);
1611 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1612 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1613 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1615 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "LockHome");
1617 return bus_log_create_error(r
);
1619 r
= sd_bus_message_append(m
, "s", *i
);
1621 return bus_log_create_error(r
);
1623 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1625 log_error_errno(r
, "Failed to lock home: %s", bus_error_message(&error
, r
));
1634 static int unlock_home(int argc
, char *argv
[], void *userdata
) {
1635 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1639 r
= acquire_bus(&bus
);
1643 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1644 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
1646 secret
= user_record_new();
1651 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1652 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1654 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "UnlockHome");
1656 return bus_log_create_error(r
);
1658 r
= sd_bus_message_append(m
, "s", *i
);
1660 return bus_log_create_error(r
);
1662 r
= bus_message_append_secret(m
, secret
);
1664 return bus_log_create_error(r
);
1666 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1668 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
1683 static int with_home(int argc
, char *argv
[], void *userdata
) {
1684 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1685 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
1686 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1687 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
1688 _cleanup_close_
int acquired_fd
= -1;
1689 _cleanup_strv_free_
char **cmdline
= NULL
;
1694 r
= acquire_bus(&bus
);
1699 _cleanup_free_
char *shell
= NULL
;
1701 /* If no command is specified, spawn a shell */
1702 r
= get_shell(&shell
);
1704 return log_error_errno(r
, "Failed to acquire shell: %m");
1706 cmdline
= strv_new(shell
);
1708 cmdline
= strv_copy(argv
+ 2);
1712 secret
= user_record_new();
1717 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "AcquireHome");
1719 return bus_log_create_error(r
);
1721 r
= sd_bus_message_append(m
, "s", argv
[1]);
1723 return bus_log_create_error(r
);
1725 r
= bus_message_append_secret(m
, secret
);
1727 return bus_log_create_error(r
);
1729 r
= sd_bus_message_append(m
, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
1731 return bus_log_create_error(r
);
1733 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
1734 m
= sd_bus_message_unref(m
);
1736 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
1740 sd_bus_error_free(&error
);
1744 r
= sd_bus_message_read(reply
, "h", &fd
);
1746 return bus_log_parse_error(r
);
1748 acquired_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
1749 if (acquired_fd
< 0)
1750 return log_error_errno(errno
, "Failed to duplicate acquired fd: %m");
1752 reply
= sd_bus_message_unref(reply
);
1757 r
= bus_call_method(bus
, bus_home_mgr
, "GetHomeByName", &error
, &reply
, "s", argv
[1]);
1759 return log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
1761 r
= sd_bus_message_read(reply
, "usussso", NULL
, NULL
, NULL
, NULL
, &home
, NULL
, NULL
);
1763 return bus_log_parse_error(r
);
1765 r
= safe_fork("(with)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_RLIMIT_NOFILE_SAFE
|FORK_REOPEN_LOG
, &pid
);
1769 if (chdir(home
) < 0) {
1770 log_error_errno(errno
, "Failed to change to directory %s: %m", home
);
1774 execvp(cmdline
[0], cmdline
);
1775 log_error_errno(errno
, "Failed to execute %s: %m", cmdline
[0]);
1779 ret
= wait_for_terminate_and_check(cmdline
[0], pid
, WAIT_LOG_ABNORMAL
);
1781 /* Close the fd that pings the home now. */
1782 acquired_fd
= safe_close(acquired_fd
);
1784 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ReleaseHome");
1786 return bus_log_create_error(r
);
1788 r
= sd_bus_message_append(m
, "s", argv
[1]);
1790 return bus_log_create_error(r
);
1792 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1794 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_BUSY
))
1795 log_notice("Not deactivating home directory of %s, as it is still used.", argv
[1]);
1797 return log_error_errno(r
, "Failed to release user home: %s", bus_error_message(&error
, r
));
1803 static int lock_all_homes(int argc
, char *argv
[], void *userdata
) {
1804 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1805 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1806 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1809 r
= acquire_bus(&bus
);
1813 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "LockAllHomes");
1815 return bus_log_create_error(r
);
1817 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1819 return log_error_errno(r
, "Failed to lock home: %s", bus_error_message(&error
, r
));
1824 static int drop_from_identity(const char *field
) {
1829 /* If we are called to update an identity record and drop some field, let's keep track of what to
1830 * remove from the old record */
1831 r
= strv_extend(&arg_identity_filter
, field
);
1835 /* Let's also drop the field if it was previously set to a new value on the same command line */
1836 r
= json_variant_filter(&arg_identity_extra
, STRV_MAKE(field
));
1838 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
1840 r
= json_variant_filter(&arg_identity_extra_this_machine
, STRV_MAKE(field
));
1842 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
1844 r
= json_variant_filter(&arg_identity_extra_privileged
, STRV_MAKE(field
));
1846 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
1851 static int help(int argc
, char *argv
[], void *userdata
) {
1852 _cleanup_free_
char *link
= NULL
;
1855 (void) pager_open(arg_pager_flags
);
1857 r
= terminal_urlify_man("homectl", "1", &link
);
1861 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
1862 "%2$sCreate, manipulate or inspect home directories.%3$s\n"
1863 "\n%4$sCommands:%5$s\n"
1864 " list List home areas\n"
1865 " activate USER… Activate a home area\n"
1866 " deactivate USER… Deactivate a home area\n"
1867 " inspect USER… Inspect a home area\n"
1868 " authenticate USER… Authenticate a home area\n"
1869 " create USER Create a home area\n"
1870 " remove USER… Remove a home area\n"
1871 " update USER Update a home area\n"
1872 " passwd USER Change password of a home area\n"
1873 " resize USER SIZE Resize a home area\n"
1874 " lock USER… Temporarily lock an active home area\n"
1875 " unlock USER… Unlock a temporarily locked home area\n"
1876 " lock-all Lock all suitable home areas\n"
1877 " with USER [COMMAND…] Run shell or command with access to a home area\n"
1878 "\n%4$sOptions:%5$s\n"
1879 " -h --help Show this help\n"
1880 " --version Show package version\n"
1881 " --no-pager Do not pipe output into a pager\n"
1882 " --no-legend Do not show the headers and footers\n"
1883 " --no-ask-password Do not ask for system passwords\n"
1884 " -H --host=[USER@]HOST Operate on remote host\n"
1885 " -M --machine=CONTAINER Operate on local container\n"
1886 " --identity=PATH Read JSON identity from file\n"
1887 " --json=FORMAT Output inspection data in JSON (takes one of\n"
1888 " pretty, short, off)\n"
1889 " -j Equivalent to --json=pretty (on TTY) or\n"
1890 " --json=short (otherwise)\n"
1891 " --export-format= Strip JSON inspection data (full, stripped,\n"
1893 " -E When specified once equals -j --export-format=\n"
1894 " stripped, when specified twice equals\n"
1895 " -j --export-format=minimal\n"
1896 "\n%4$sGeneral User Record Properties:%5$s\n"
1897 " -c --real-name=REALNAME Real name for user\n"
1898 " --realm=REALM Realm to create user in\n"
1899 " --email-address=EMAIL Email address for user\n"
1900 " --location=LOCATION Set location of user on earth\n"
1901 " --icon-name=NAME Icon name for user\n"
1902 " -d --home-dir=PATH Home directory\n"
1903 " -u --uid=UID Numeric UID for user\n"
1904 " -G --member-of=GROUP Add user to group\n"
1905 " --skel=PATH Skeleton directory to use\n"
1906 " --shell=PATH Shell for account\n"
1907 " --setenv=VARIABLE=VALUE Set an environment variable at log-in\n"
1908 " --timezone=TIMEZONE Set a time-zone\n"
1909 " --language=LOCALE Set preferred language\n"
1910 " --ssh-authorized-keys=KEYS\n"
1911 " Specify SSH public keys\n"
1912 " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
1913 " private key and matching X.509 certificate\n"
1914 " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
1916 "\n%4$sAccount Management User Record Properties:%5$s\n"
1917 " --locked=BOOL Set locked account state\n"
1918 " --not-before=TIMESTAMP Do not allow logins before\n"
1919 " --not-after=TIMESTAMP Do not allow logins after\n"
1920 " --rate-limit-interval=SECS\n"
1921 " Login rate-limit interval in seconds\n"
1922 " --rate-limit-burst=NUMBER\n"
1923 " Login rate-limit attempts per interval\n"
1924 "\n%4$sPassword Policy User Record Properties:%5$s\n"
1925 " --password-hint=HINT Set Password hint\n"
1926 " --enforce-password-policy=BOOL\n"
1927 " Control whether to enforce system's password\n"
1928 " policy for this user\n"
1929 " -P Equivalent to --enforce-password-password=no\n"
1930 " --password-change-now=BOOL\n"
1931 " Require the password to be changed on next login\n"
1932 " --password-change-min=TIME\n"
1933 " Require minimum time between password changes\n"
1934 " --password-change-max=TIME\n"
1935 " Require maximum time between password changes\n"
1936 " --password-change-warn=TIME\n"
1937 " How much time to warn before password expiry\n"
1938 " --password-change-inactive=TIME\n"
1939 " How much time to block password after expiry\n"
1940 "\n%4$sResource Management User Record Properties:%5$s\n"
1941 " --disk-size=BYTES Size to assign the user on disk\n"
1942 " --access-mode=MODE User home directory access mode\n"
1943 " --umask=MODE Umask for user when logging in\n"
1944 " --nice=NICE Nice level for user\n"
1945 " --rlimit=LIMIT=VALUE[:VALUE]\n"
1946 " Set resource limits\n"
1947 " --tasks-max=MAX Set maximum number of per-user tasks\n"
1948 " --memory-high=BYTES Set high memory threshold in bytes\n"
1949 " --memory-max=BYTES Set maximum memory limit\n"
1950 " --cpu-weight=WEIGHT Set CPU weight\n"
1951 " --io-weight=WEIGHT Set IO weight\n"
1952 "\n%4$sStorage User Record Properties:%5$s\n"
1953 " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
1954 " subvolume, cifs)\n"
1955 " --image-path=PATH Path to image file/directory\n"
1956 "\n%4$sLUKS Storage User Record Properties:%5$s\n"
1957 " --fs-type=TYPE File system type to use in case of luks\n"
1958 " storage (ext4, xfs, btrfs)\n"
1959 " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
1960 " when activated (mounted)\n"
1961 " --luks-offline-discard=BOOL\n"
1962 " Whether to trim file on logout\n"
1963 " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
1964 " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
1965 " --luks-volume-key-size=BITS\n"
1966 " Volume key size to use for LUKS encryption\n"
1967 " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
1968 " --luks-pbkdf-hash-algorithm=ALGORITHM\n"
1969 " PBKDF hash algorithm to use\n"
1970 " --luks-pbkdf-time-cost=SECS\n"
1971 " Time cost for PBKDF in seconds\n"
1972 " --luks-pbkdf-memory-cost=BYTES\n"
1973 " Memory cost for PBKDF in bytes\n"
1974 " --luks-pbkdf-parallel-threads=NUMBER\n"
1975 " Number of parallel threads for PKBDF\n"
1976 "\n%4$sMounting User Record Properties:%5$s\n"
1977 " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
1978 " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
1979 " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
1980 "\n%4$sCIFS User Record Properties:%5$s\n"
1981 " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
1982 " --cifs-user-name=USER CIFS (Windows) user name\n"
1983 " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
1984 "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
1985 " --stop-delay=SECS How long to leave user services running after\n"
1987 " --kill-processes=BOOL Whether to kill user processes when sessions\n"
1989 " --auto-login=BOOL Try to log this user in automatically\n"
1990 "\nSee the %6$s for details.\n"
1991 , program_invocation_short_name
1992 , ansi_highlight(), ansi_normal()
1993 , ansi_underline(), ansi_normal()
2000 static int parse_argv(int argc
, char *argv
[]) {
2003 ARG_VERSION
= 0x100,
2006 ARG_NO_ASK_PASSWORD
,
2016 ARG_LUKS_OFFLINE_DISCARD
,
2022 ARG_SSH_AUTHORIZED_KEYS
,
2031 ARG_LUKS_CIPHER_MODE
,
2032 ARG_LUKS_VOLUME_KEY_SIZE
,
2044 ARG_LUKS_PBKDF_TYPE
,
2045 ARG_LUKS_PBKDF_HASH_ALGORITHM
,
2046 ARG_LUKS_PBKDF_TIME_COST
,
2047 ARG_LUKS_PBKDF_MEMORY_COST
,
2048 ARG_LUKS_PBKDF_PARALLEL_THREADS
,
2049 ARG_RATE_LIMIT_INTERVAL
,
2050 ARG_RATE_LIMIT_BURST
,
2053 ARG_ENFORCE_PASSWORD_POLICY
,
2054 ARG_PASSWORD_CHANGE_NOW
,
2055 ARG_PASSWORD_CHANGE_MIN
,
2056 ARG_PASSWORD_CHANGE_MAX
,
2057 ARG_PASSWORD_CHANGE_WARN
,
2058 ARG_PASSWORD_CHANGE_INACTIVE
,
2061 ARG_PKCS11_TOKEN_URI
,
2064 ARG_AND_CHANGE_PASSWORD
,
2067 static const struct option options
[] = {
2068 { "help", no_argument
, NULL
, 'h' },
2069 { "version", no_argument
, NULL
, ARG_VERSION
},
2070 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2071 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2072 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2073 { "host", required_argument
, NULL
, 'H' },
2074 { "machine", required_argument
, NULL
, 'M' },
2075 { "identity", required_argument
, NULL
, 'I' },
2076 { "real-name", required_argument
, NULL
, 'c' },
2077 { "comment", required_argument
, NULL
, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
2078 { "realm", required_argument
, NULL
, ARG_REALM
},
2079 { "email-address", required_argument
, NULL
, ARG_EMAIL_ADDRESS
},
2080 { "location", required_argument
, NULL
, ARG_LOCATION
},
2081 { "password-hint", required_argument
, NULL
, ARG_PASSWORD_HINT
},
2082 { "icon-name", required_argument
, NULL
, ARG_ICON_NAME
},
2083 { "home-dir", required_argument
, NULL
, 'd' }, /* Compatible with useradd(8) */
2084 { "uid", required_argument
, NULL
, 'u' }, /* Compatible with useradd(8) */
2085 { "member-of", required_argument
, NULL
, 'G' },
2086 { "groups", required_argument
, NULL
, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
2087 { "skel", required_argument
, NULL
, 'k' }, /* Compatible with useradd(8) */
2088 { "shell", required_argument
, NULL
, 's' }, /* Compatible with useradd(8) */
2089 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2090 { "timezone", required_argument
, NULL
, ARG_TIMEZONE
},
2091 { "language", required_argument
, NULL
, ARG_LANGUAGE
},
2092 { "locked", required_argument
, NULL
, ARG_LOCKED
},
2093 { "not-before", required_argument
, NULL
, ARG_NOT_BEFORE
},
2094 { "not-after", required_argument
, NULL
, ARG_NOT_AFTER
},
2095 { "expiredate", required_argument
, NULL
, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
2096 { "ssh-authorized-keys", required_argument
, NULL
, ARG_SSH_AUTHORIZED_KEYS
},
2097 { "disk-size", required_argument
, NULL
, ARG_DISK_SIZE
},
2098 { "access-mode", required_argument
, NULL
, ARG_ACCESS_MODE
},
2099 { "umask", required_argument
, NULL
, ARG_UMASK
},
2100 { "nice", required_argument
, NULL
, ARG_NICE
},
2101 { "rlimit", required_argument
, NULL
, ARG_RLIMIT
},
2102 { "tasks-max", required_argument
, NULL
, ARG_TASKS_MAX
},
2103 { "memory-high", required_argument
, NULL
, ARG_MEMORY_HIGH
},
2104 { "memory-max", required_argument
, NULL
, ARG_MEMORY_MAX
},
2105 { "cpu-weight", required_argument
, NULL
, ARG_CPU_WEIGHT
},
2106 { "io-weight", required_argument
, NULL
, ARG_IO_WEIGHT
},
2107 { "storage", required_argument
, NULL
, ARG_STORAGE
},
2108 { "image-path", required_argument
, NULL
, ARG_IMAGE_PATH
},
2109 { "fs-type", required_argument
, NULL
, ARG_FS_TYPE
},
2110 { "luks-discard", required_argument
, NULL
, ARG_LUKS_DISCARD
},
2111 { "luks-offline-discard", required_argument
, NULL
, ARG_LUKS_OFFLINE_DISCARD
},
2112 { "luks-cipher", required_argument
, NULL
, ARG_LUKS_CIPHER
},
2113 { "luks-cipher-mode", required_argument
, NULL
, ARG_LUKS_CIPHER_MODE
},
2114 { "luks-volume-key-size", required_argument
, NULL
, ARG_LUKS_VOLUME_KEY_SIZE
},
2115 { "luks-pbkdf-type", required_argument
, NULL
, ARG_LUKS_PBKDF_TYPE
},
2116 { "luks-pbkdf-hash-algorithm", required_argument
, NULL
, ARG_LUKS_PBKDF_HASH_ALGORITHM
},
2117 { "luks-pbkdf-time-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_TIME_COST
},
2118 { "luks-pbkdf-memory-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_MEMORY_COST
},
2119 { "luks-pbkdf-parallel-threads", required_argument
, NULL
, ARG_LUKS_PBKDF_PARALLEL_THREADS
},
2120 { "nosuid", required_argument
, NULL
, ARG_NOSUID
},
2121 { "nodev", required_argument
, NULL
, ARG_NODEV
},
2122 { "noexec", required_argument
, NULL
, ARG_NOEXEC
},
2123 { "cifs-user-name", required_argument
, NULL
, ARG_CIFS_USER_NAME
},
2124 { "cifs-domain", required_argument
, NULL
, ARG_CIFS_DOMAIN
},
2125 { "cifs-service", required_argument
, NULL
, ARG_CIFS_SERVICE
},
2126 { "rate-limit-interval", required_argument
, NULL
, ARG_RATE_LIMIT_INTERVAL
},
2127 { "rate-limit-burst", required_argument
, NULL
, ARG_RATE_LIMIT_BURST
},
2128 { "stop-delay", required_argument
, NULL
, ARG_STOP_DELAY
},
2129 { "kill-processes", required_argument
, NULL
, ARG_KILL_PROCESSES
},
2130 { "enforce-password-policy", required_argument
, NULL
, ARG_ENFORCE_PASSWORD_POLICY
},
2131 { "password-change-now", required_argument
, NULL
, ARG_PASSWORD_CHANGE_NOW
},
2132 { "password-change-min", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MIN
},
2133 { "password-change-max", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MAX
},
2134 { "password-change-warn", required_argument
, NULL
, ARG_PASSWORD_CHANGE_WARN
},
2135 { "password-change-inactive", required_argument
, NULL
, ARG_PASSWORD_CHANGE_INACTIVE
},
2136 { "auto-login", required_argument
, NULL
, ARG_AUTO_LOGIN
},
2137 { "json", required_argument
, NULL
, ARG_JSON
},
2138 { "export-format", required_argument
, NULL
, ARG_EXPORT_FORMAT
},
2139 { "pkcs11-token-uri", required_argument
, NULL
, ARG_PKCS11_TOKEN_URI
},
2140 { "fido2-device", required_argument
, NULL
, ARG_FIDO2_DEVICE
},
2141 { "and-resize", required_argument
, NULL
, ARG_AND_RESIZE
},
2142 { "and-change-password", required_argument
, NULL
, ARG_AND_CHANGE_PASSWORD
},
2154 c
= getopt_long(argc
, argv
, "hH:M:I:c:d:u:k:s:e:G:jPE", options
, NULL
);
2161 return help(0, NULL
, NULL
);
2167 arg_pager_flags
|= PAGER_DISABLE
;
2174 case ARG_NO_ASK_PASSWORD
:
2175 arg_ask_password
= false;
2179 arg_transport
= BUS_TRANSPORT_REMOTE
;
2184 arg_transport
= BUS_TRANSPORT_MACHINE
;
2189 arg_identity
= optarg
;
2193 if (isempty(optarg
)) {
2194 r
= drop_from_identity("realName");
2201 if (!valid_gecos(optarg
))
2202 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Real name '%s' not a valid GECOS field.", optarg
);
2204 r
= json_variant_set_field_string(&arg_identity_extra
, "realName", optarg
);
2206 return log_error_errno(r
, "Failed to set realName field: %m");
2211 _cleanup_free_
char *hd
= NULL
;
2213 if (isempty(optarg
)) {
2214 r
= drop_from_identity("homeDirectory");
2221 r
= parse_path_argument_and_warn(optarg
, false, &hd
);
2225 if (!valid_home(hd
))
2226 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Home directory '%s' not valid.", hd
);
2228 r
= json_variant_set_field_string(&arg_identity_extra
, "homeDirectory", hd
);
2230 return log_error_errno(r
, "Failed to set homeDirectory field: %m");
2236 if (isempty(optarg
)) {
2237 r
= drop_from_identity("realm");
2244 r
= dns_name_is_valid(optarg
);
2246 return log_error_errno(r
, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg
);
2248 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Realm '%s' is not a valid DNS domain: %m", optarg
);
2250 r
= json_variant_set_field_string(&arg_identity_extra
, "realm", optarg
);
2252 return log_error_errno(r
, "Failed to set realm field: %m");
2255 case ARG_EMAIL_ADDRESS
:
2258 case ARG_CIFS_USER_NAME
:
2259 case ARG_CIFS_DOMAIN
:
2260 case ARG_CIFS_SERVICE
: {
2263 c
== ARG_EMAIL_ADDRESS
? "emailAddress" :
2264 c
== ARG_LOCATION
? "location" :
2265 c
== ARG_ICON_NAME
? "iconName" :
2266 c
== ARG_CIFS_USER_NAME
? "cifsUserName" :
2267 c
== ARG_CIFS_DOMAIN
? "cifsDomain" :
2268 c
== ARG_CIFS_SERVICE
? "cifsService" :
2273 if (isempty(optarg
)) {
2274 r
= drop_from_identity(field
);
2281 r
= json_variant_set_field_string(&arg_identity_extra
, field
, optarg
);
2283 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2288 case ARG_PASSWORD_HINT
:
2289 if (isempty(optarg
)) {
2290 r
= drop_from_identity("passwordHint");
2297 r
= json_variant_set_field_string(&arg_identity_extra_privileged
, "passwordHint", optarg
);
2299 return log_error_errno(r
, "Failed to set passwordHint field: %m");
2301 string_erase(optarg
);
2307 if (isempty(optarg
)) {
2308 r
= drop_from_identity("niceLevel");
2314 r
= parse_nice(optarg
, &nc
);
2316 return log_error_errno(r
, "Failed to parse nice level: %s", optarg
);
2318 r
= json_variant_set_field_integer(&arg_identity_extra
, "niceLevel", nc
);
2320 return log_error_errno(r
, "Failed to set niceLevel field: %m");
2326 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *jcur
= NULL
, *jmax
= NULL
;
2327 _cleanup_free_
char *field
= NULL
, *t
= NULL
;
2332 if (isempty(optarg
)) {
2333 /* Remove all resource limits */
2335 r
= drop_from_identity("resourceLimits");
2339 arg_identity_filter_rlimits
= strv_free(arg_identity_filter_rlimits
);
2340 arg_identity_extra_rlimits
= json_variant_unref(arg_identity_extra_rlimits
);
2344 eq
= strchr(optarg
, '=');
2346 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Can't parse resource limit assignment: %s", optarg
);
2348 field
= strndup(optarg
, eq
- optarg
);
2352 l
= rlimit_from_string_harder(field
);
2354 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unknown resource limit type: %s", field
);
2356 if (isempty(eq
+ 1)) {
2357 /* Remove only the specific rlimit */
2359 r
= strv_extend(&arg_identity_filter_rlimits
, rlimit_to_string(l
));
2363 r
= json_variant_filter(&arg_identity_extra_rlimits
, STRV_MAKE(field
));
2365 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2370 r
= rlimit_parse(l
, eq
+ 1, &rl
);
2372 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to parse resource limit value: %s", eq
+ 1);
2374 r
= rl
.rlim_cur
== RLIM_INFINITY
? json_variant_new_null(&jcur
) : json_variant_new_unsigned(&jcur
, rl
.rlim_cur
);
2376 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to allocate current integer: %m");
2378 r
= rl
.rlim_max
== RLIM_INFINITY
? json_variant_new_null(&jmax
) : json_variant_new_unsigned(&jmax
, rl
.rlim_max
);
2380 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to allocate maximum integer: %m");
2384 JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur
)),
2385 JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax
))));
2387 return log_error_errno(r
, "Failed to build resource limit: %m");
2389 t
= strjoin("RLIMIT_", rlimit_to_string(l
));
2393 r
= json_variant_set_field(&arg_identity_extra_rlimits
, t
, v
);
2395 return log_error_errno(r
, "Failed to set %s field: %m", rlimit_to_string(l
));
2403 if (isempty(optarg
)) {
2404 r
= drop_from_identity("uid");
2411 r
= parse_uid(optarg
, &uid
);
2413 return log_error_errno(r
, "Failed to parse UID '%s'.", optarg
);
2415 if (uid_is_system(uid
))
2416 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in system range, refusing.", uid
);
2417 if (uid_is_dynamic(uid
))
2418 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in dynamic range, refusing.", uid
);
2419 if (uid
== UID_NOBODY
)
2420 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is nobody UID, refusing.", uid
);
2422 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "uid", uid
);
2424 return log_error_errno(r
, "Failed to set realm field: %m");
2430 case ARG_IMAGE_PATH
: {
2431 const char *field
= c
== 'k' ? "skeletonDirectory" : "imagePath";
2432 _cleanup_free_
char *v
= NULL
;
2434 if (isempty(optarg
)) {
2435 r
= drop_from_identity(field
);
2442 r
= parse_path_argument_and_warn(optarg
, false, &v
);
2446 r
= json_variant_set_field_string(&arg_identity_extra_this_machine
, field
, v
);
2448 return log_error_errno(r
, "Failed to set %s field: %m", v
);
2454 if (isempty(optarg
)) {
2455 r
= drop_from_identity("shell");
2462 if (!valid_shell(optarg
))
2463 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Shell '%s' not valid.", optarg
);
2465 r
= json_variant_set_field_string(&arg_identity_extra
, "shell", optarg
);
2467 return log_error_errno(r
, "Failed to set shell field: %m");
2472 _cleanup_free_
char **l
= NULL
, **k
= NULL
;
2473 _cleanup_(json_variant_unrefp
) JsonVariant
*ne
= NULL
;
2476 if (isempty(optarg
)) {
2477 r
= drop_from_identity("environment");
2484 if (!env_assignment_is_valid(optarg
))
2485 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Environment assignment '%s' not valid.", optarg
);
2487 e
= json_variant_by_key(arg_identity_extra
, "environment");
2489 r
= json_variant_strv(e
, &l
);
2491 return log_error_errno(r
, "Failed to parse JSON environment field: %m");
2494 k
= strv_env_set(l
, optarg
);
2500 r
= json_variant_new_array_strv(&ne
, k
);
2502 return log_error_errno(r
, "Failed to allocate environment list JSON: %m");
2504 r
= json_variant_set_field(&arg_identity_extra
, "environment", ne
);
2506 return log_error_errno(r
, "Failed to set environment list: %m");
2513 if (isempty(optarg
)) {
2514 r
= drop_from_identity("timeZone");
2521 if (!timezone_is_valid(optarg
, LOG_DEBUG
))
2522 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Timezone '%s' is not valid.", optarg
);
2524 r
= json_variant_set_field_string(&arg_identity_extra
, "timeZone", optarg
);
2526 return log_error_errno(r
, "Failed to set timezone field: %m");
2531 if (isempty(optarg
)) {
2532 r
= drop_from_identity("language");
2539 if (!locale_is_valid(optarg
))
2540 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Locale '%s' is not valid.", optarg
);
2542 if (locale_is_installed(optarg
) <= 0)
2543 log_warning("Locale '%s' is not installed, accepting anyway.", optarg
);
2545 r
= json_variant_set_field_string(&arg_identity_extra
, "preferredLanguage", optarg
);
2547 return log_error_errno(r
, "Failed to set preferredLanguage field: %m");
2555 case ARG_KILL_PROCESSES
:
2556 case ARG_ENFORCE_PASSWORD_POLICY
:
2557 case ARG_AUTO_LOGIN
:
2558 case ARG_PASSWORD_CHANGE_NOW
: {
2560 c
== ARG_LOCKED
? "locked" :
2561 c
== ARG_NOSUID
? "mountNoSuid" :
2562 c
== ARG_NODEV
? "mountNoDevices" :
2563 c
== ARG_NOEXEC
? "mountNoExecute" :
2564 c
== ARG_KILL_PROCESSES
? "killProcesses" :
2565 c
== ARG_ENFORCE_PASSWORD_POLICY
? "enforcePasswordPolicy" :
2566 c
== ARG_AUTO_LOGIN
? "autoLogin" :
2567 c
== ARG_PASSWORD_CHANGE_NOW
? "passwordChangeNow" :
2572 if (isempty(optarg
)) {
2573 r
= drop_from_identity(field
);
2580 r
= parse_boolean(optarg
);
2582 return log_error_errno(r
, "Failed to parse %s boolean: %m", field
);
2584 r
= json_variant_set_field_boolean(&arg_identity_extra
, field
, r
> 0);
2586 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2592 r
= json_variant_set_field_boolean(&arg_identity_extra
, "enforcePasswordPolicy", false);
2594 return log_error_errno(r
, "Failed to set enforcePasswordPolicy field: %m");
2599 if (isempty(optarg
)) {
2600 r
= drop_from_identity("diskSize");
2604 r
= drop_from_identity("diskSizeRelative");
2608 arg_disk_size
= arg_disk_size_relative
= UINT64_MAX
;
2612 r
= parse_permille(optarg
);
2614 r
= parse_size(optarg
, 1024, &arg_disk_size
);
2616 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Disk size '%s' not valid.", optarg
);
2618 r
= drop_from_identity("diskSizeRelative");
2622 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "diskSize", arg_disk_size
);
2624 return log_error_errno(r
, "Failed to set diskSize field: %m");
2626 arg_disk_size_relative
= UINT64_MAX
;
2628 /* Normalize to UINT32_MAX == 100% */
2629 arg_disk_size_relative
= (uint64_t) r
* UINT32_MAX
/ 1000U;
2631 r
= drop_from_identity("diskSize");
2635 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "diskSizeRelative", arg_disk_size_relative
);
2637 return log_error_errno(r
, "Failed to set diskSizeRelative field: %m");
2639 arg_disk_size
= UINT64_MAX
;
2644 case ARG_ACCESS_MODE
: {
2647 if (isempty(optarg
)) {
2648 r
= drop_from_identity("accessMode");
2655 r
= parse_mode(optarg
, &mode
);
2657 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Access mode '%s' not valid.", optarg
);
2659 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "accessMode", mode
);
2661 return log_error_errno(r
, "Failed to set access mode field: %m");
2666 case ARG_LUKS_DISCARD
:
2667 if (isempty(optarg
)) {
2668 r
= drop_from_identity("luksDiscard");
2675 r
= parse_boolean(optarg
);
2677 return log_error_errno(r
, "Failed to parse --luks-discard= parameter: %s", optarg
);
2679 r
= json_variant_set_field_boolean(&arg_identity_extra
, "luksDiscard", r
);
2681 return log_error_errno(r
, "Failed to set discard field: %m");
2685 case ARG_LUKS_OFFLINE_DISCARD
:
2686 if (isempty(optarg
)) {
2687 r
= drop_from_identity("luksOfflineDiscard");
2694 r
= parse_boolean(optarg
);
2696 return log_error_errno(r
, "Failed to parse --luks-offline-discard= parameter: %s", optarg
);
2698 r
= json_variant_set_field_boolean(&arg_identity_extra
, "luksOfflineDiscard", r
);
2700 return log_error_errno(r
, "Failed to set offline discard field: %m");
2704 case ARG_LUKS_VOLUME_KEY_SIZE
:
2705 case ARG_LUKS_PBKDF_PARALLEL_THREADS
:
2706 case ARG_RATE_LIMIT_BURST
: {
2708 c
== ARG_LUKS_VOLUME_KEY_SIZE
? "luksVolumeKeySize" :
2709 c
== ARG_LUKS_PBKDF_PARALLEL_THREADS
? "luksPbkdfParallelThreads" :
2710 c
== ARG_RATE_LIMIT_BURST
? "rateLimitBurst" : NULL
;
2715 if (isempty(optarg
)) {
2716 r
= drop_from_identity(field
);
2721 r
= safe_atou(optarg
, &n
);
2723 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
2725 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
2727 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2735 if (isempty(optarg
)) {
2736 r
= drop_from_identity("umask");
2743 r
= parse_mode(optarg
, &m
);
2745 return log_error_errno(r
, "Failed to parse umask: %m");
2747 r
= json_variant_set_field_integer(&arg_identity_extra
, "umask", m
);
2749 return log_error_errno(r
, "Failed to set umask field: %m");
2754 case ARG_SSH_AUTHORIZED_KEYS
: {
2755 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
2756 _cleanup_(strv_freep
) char **l
= NULL
, **add
= NULL
;
2758 if (isempty(optarg
)) {
2759 r
= drop_from_identity("sshAuthorizedKeys");
2766 if (optarg
[0] == '@') {
2767 _cleanup_fclose_
FILE *f
= NULL
;
2769 /* If prefixed with '@' read from a file */
2771 f
= fopen(optarg
+1, "re");
2773 return log_error_errno(errno
, "Failed to open '%s': %m", optarg
+1);
2776 _cleanup_free_
char *line
= NULL
;
2778 r
= read_line(f
, LONG_LINE_MAX
, &line
);
2780 return log_error_errno(r
, "Failed to read from '%s': %m", optarg
+1);
2790 r
= strv_consume(&add
, TAKE_PTR(line
));
2795 /* Otherwise, assume it's a literal key. Let's do some superficial checks
2796 * before accept it though. */
2798 if (string_has_cc(optarg
, NULL
))
2799 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Authorized key contains control characters, refusing.");
2800 if (optarg
[0] == '#')
2801 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specified key is a comment?");
2803 add
= strv_new(optarg
);
2808 v
= json_variant_ref(json_variant_by_key(arg_identity_extra_privileged
, "sshAuthorizedKeys"));
2810 r
= json_variant_strv(v
, &l
);
2812 return log_error_errno(r
, "Failed to parse SSH authorized keys list: %m");
2815 r
= strv_extend_strv(&l
, add
, true);
2819 v
= json_variant_unref(v
);
2821 r
= json_variant_new_array_strv(&v
, l
);
2825 r
= json_variant_set_field(&arg_identity_extra_privileged
, "sshAuthorizedKeys", v
);
2827 return log_error_errno(r
, "Failed to set authorized keys: %m");
2832 case ARG_NOT_BEFORE
:
2838 field
= c
== ARG_NOT_BEFORE
? "notBeforeUSec" :
2839 IN_SET(c
, ARG_NOT_AFTER
, 'e') ? "notAfterUSec" : NULL
;
2843 if (isempty(optarg
)) {
2844 r
= drop_from_identity(field
);
2851 /* Note the minor discrepancy regarding -e parsing here: we support that for compat
2852 * reasons, and in the original useradd(8) implementation it accepts dates in the
2853 * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
2854 * with greater precision. */
2855 r
= parse_timestamp(optarg
, &n
);
2857 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
2859 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
2861 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2865 case ARG_PASSWORD_CHANGE_MIN
:
2866 case ARG_PASSWORD_CHANGE_MAX
:
2867 case ARG_PASSWORD_CHANGE_WARN
:
2868 case ARG_PASSWORD_CHANGE_INACTIVE
: {
2872 field
= c
== ARG_PASSWORD_CHANGE_MIN
? "passwordChangeMinUSec" :
2873 c
== ARG_PASSWORD_CHANGE_MAX
? "passwordChangeMaxUSec" :
2874 c
== ARG_PASSWORD_CHANGE_WARN
? "passwordChangeWarnUSec" :
2875 c
== ARG_PASSWORD_CHANGE_INACTIVE
? "passwordChangeInactiveUSec" :
2880 if (isempty(optarg
)) {
2881 r
= drop_from_identity(field
);
2888 r
= parse_sec(optarg
, &n
);
2890 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
2892 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
2894 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2900 case ARG_LUKS_CIPHER
:
2901 case ARG_LUKS_CIPHER_MODE
:
2902 case ARG_LUKS_PBKDF_TYPE
:
2903 case ARG_LUKS_PBKDF_HASH_ALGORITHM
: {
2906 c
== ARG_STORAGE
? "storage" :
2907 c
== ARG_FS_TYPE
? "fileSystemType" :
2908 c
== ARG_LUKS_CIPHER
? "luksCipher" :
2909 c
== ARG_LUKS_CIPHER_MODE
? "luksCipherMode" :
2910 c
== ARG_LUKS_PBKDF_TYPE
? "luksPbkdfType" :
2911 c
== ARG_LUKS_PBKDF_HASH_ALGORITHM
? "luksPbkdfHashAlgorithm" : NULL
;
2915 if (isempty(optarg
)) {
2916 r
= drop_from_identity(field
);
2923 if (!string_is_safe(optarg
))
2924 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Parameter for %s field not valid: %s", field
, optarg
);
2926 r
= json_variant_set_field_string(
2927 IN_SET(c
, ARG_STORAGE
, ARG_FS_TYPE
) ?
2928 &arg_identity_extra_this_machine
:
2929 &arg_identity_extra
, field
, optarg
);
2931 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2936 case ARG_LUKS_PBKDF_TIME_COST
:
2937 case ARG_RATE_LIMIT_INTERVAL
:
2938 case ARG_STOP_DELAY
: {
2940 c
== ARG_LUKS_PBKDF_TIME_COST
? "luksPbkdfTimeCostUSec" :
2941 c
== ARG_RATE_LIMIT_INTERVAL
? "rateLimitIntervalUSec" :
2942 c
== ARG_STOP_DELAY
? "stopDelayUSec" :
2948 if (isempty(optarg
)) {
2949 r
= drop_from_identity(field
);
2956 r
= parse_sec(optarg
, &t
);
2958 return log_error_errno(r
, "Failed to parse %s field: %s", field
, optarg
);
2960 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, t
);
2962 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2968 const char *p
= optarg
;
2971 r
= drop_from_identity("memberOf");
2979 _cleanup_(json_variant_unrefp
) JsonVariant
*mo
= NULL
;
2980 _cleanup_strv_free_
char **list
= NULL
;
2981 _cleanup_free_
char *word
= NULL
;
2983 r
= extract_first_word(&p
, &word
, ",", 0);
2985 return log_error_errno(r
, "Failed to parse group list: %m");
2989 if (!valid_user_group_name(word
, 0))
2990 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid group name %s.", word
);
2992 mo
= json_variant_ref(json_variant_by_key(arg_identity_extra
, "memberOf"));
2994 r
= json_variant_strv(mo
, &list
);
2996 return log_error_errno(r
, "Failed to parse group list: %m");
2998 r
= strv_extend(&list
, word
);
3005 mo
= json_variant_unref(mo
);
3006 r
= json_variant_new_array_strv(&mo
, list
);
3008 return log_error_errno(r
, "Failed to create group list JSON: %m");
3010 r
= json_variant_set_field(&arg_identity_extra
, "memberOf", mo
);
3012 return log_error_errno(r
, "Failed to update group list: %m");
3018 case ARG_TASKS_MAX
: {
3021 if (isempty(optarg
)) {
3022 r
= drop_from_identity("tasksMax");
3028 r
= safe_atou64(optarg
, &u
);
3030 return log_error_errno(r
, "Failed to parse --tasks-max= parameter: %s", optarg
);
3032 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "tasksMax", u
);
3034 return log_error_errno(r
, "Failed to set tasksMax field: %m");
3039 case ARG_MEMORY_MAX
:
3040 case ARG_MEMORY_HIGH
:
3041 case ARG_LUKS_PBKDF_MEMORY_COST
: {
3043 c
== ARG_MEMORY_MAX
? "memoryMax" :
3044 c
== ARG_MEMORY_HIGH
? "memoryHigh" :
3045 c
== ARG_LUKS_PBKDF_MEMORY_COST
? "luksPbkdfMemoryCost" : NULL
;
3051 if (isempty(optarg
)) {
3052 r
= drop_from_identity(field
);
3058 r
= parse_size(optarg
, 1024, &u
);
3060 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
3062 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, field
, u
);
3064 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3069 case ARG_CPU_WEIGHT
:
3070 case ARG_IO_WEIGHT
: {
3071 const char *field
= c
== ARG_CPU_WEIGHT
? "cpuWeight" :
3072 c
== ARG_IO_WEIGHT
? "ioWeight" : NULL
;
3077 if (isempty(optarg
)) {
3078 r
= drop_from_identity(field
);
3084 r
= safe_atou64(optarg
, &u
);
3086 return log_error_errno(r
, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg
);
3088 if (!CGROUP_WEIGHT_IS_OK(u
))
3089 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Weight %" PRIu64
" is out of valid weight range.", u
);
3091 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, u
);
3093 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3098 case ARG_PKCS11_TOKEN_URI
: {
3101 if (streq(optarg
, "list"))
3102 return list_pkcs11_tokens();
3104 /* If --pkcs11-token-uri= is specified we always drop everything old */
3105 FOREACH_STRING(p
, "pkcs11TokenUri", "pkcs11EncryptedKey") {
3106 r
= drop_from_identity(p
);
3111 if (isempty(optarg
)) {
3112 arg_pkcs11_token_uri
= strv_free(arg_pkcs11_token_uri
);
3116 if (streq(optarg
, "auto")) {
3117 _cleanup_free_
char *found
= NULL
;
3119 r
= find_pkcs11_token_auto(&found
);
3122 r
= strv_consume(&arg_pkcs11_token_uri
, TAKE_PTR(found
));
3124 if (!pkcs11_uri_valid(optarg
))
3125 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Not a valid PKCS#11 URI: %s", optarg
);
3127 r
= strv_extend(&arg_pkcs11_token_uri
, optarg
);
3132 strv_uniq(arg_pkcs11_token_uri
);
3136 case ARG_FIDO2_DEVICE
: {
3139 if (streq(optarg
, "list"))
3140 return list_fido2_devices();
3142 FOREACH_STRING(p
, "fido2HmacCredential", "fido2HmacSalt") {
3143 r
= drop_from_identity(p
);
3148 if (isempty(optarg
)) {
3149 arg_fido2_device
= strv_free(arg_fido2_device
);
3153 if (streq(optarg
, "auto")) {
3154 _cleanup_free_
char *found
= NULL
;
3156 r
= find_fido2_auto(&found
);
3160 r
= strv_consume(&arg_fido2_device
, TAKE_PTR(found
));
3162 r
= strv_extend(&arg_fido2_device
, optarg
);
3167 strv_uniq(arg_fido2_device
);
3173 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
3177 if (streq(optarg
, "pretty")) {
3179 arg_json_format_flags
= JSON_FORMAT_PRETTY
|JSON_FORMAT_COLOR_AUTO
;
3180 } else if (streq(optarg
, "short")) {
3182 arg_json_format_flags
= JSON_FORMAT_NEWLINE
;
3183 } else if (streq(optarg
, "off")) {
3185 arg_json_format_flags
= 0;
3186 } else if (streq(optarg
, "help")) {
3192 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unknown argument to --json=: %s", optarg
);
3197 if (arg_export_format
== EXPORT_FORMAT_FULL
)
3198 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
3199 else if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
3200 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
3202 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specifying -E more than twice is not supported.");
3205 if (arg_json_format_flags
== 0)
3206 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
3209 case ARG_EXPORT_FORMAT
:
3210 if (streq(optarg
, "full"))
3211 arg_export_format
= EXPORT_FORMAT_FULL
;
3212 else if (streq(optarg
, "stripped"))
3213 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
3214 else if (streq(optarg
, "minimal"))
3215 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
3216 else if (streq(optarg
, "help")) {
3225 case ARG_AND_RESIZE
:
3226 arg_and_resize
= true;
3229 case ARG_AND_CHANGE_PASSWORD
:
3230 arg_and_change_password
= true;
3237 assert_not_reached("Unhandled option");
3241 if (!strv_isempty(arg_pkcs11_token_uri
) || !strv_isempty(arg_fido2_device
))
3242 arg_and_change_password
= true;
3244 if (arg_disk_size
!= UINT64_MAX
|| arg_disk_size_relative
!= UINT64_MAX
)
3245 arg_and_resize
= true;
3250 static int run(int argc
, char *argv
[]) {
3251 static const Verb verbs
[] = {
3252 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
3253 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_homes
},
3254 { "activate", 2, VERB_ANY
, 0, activate_home
},
3255 { "deactivate", 2, VERB_ANY
, 0, deactivate_home
},
3256 { "inspect", VERB_ANY
, VERB_ANY
, 0, inspect_home
},
3257 { "authenticate", VERB_ANY
, VERB_ANY
, 0, authenticate_home
},
3258 { "create", VERB_ANY
, 2, 0, create_home
},
3259 { "remove", 2, VERB_ANY
, 0, remove_home
},
3260 { "update", VERB_ANY
, 2, 0, update_home
},
3261 { "passwd", VERB_ANY
, 2, 0, passwd_home
},
3262 { "resize", 2, 3, 0, resize_home
},
3263 { "lock", 2, VERB_ANY
, 0, lock_home
},
3264 { "unlock", 2, VERB_ANY
, 0, unlock_home
},
3265 { "with", 2, VERB_ANY
, 0, with_home
},
3266 { "lock-all", VERB_ANY
, 1, 0, lock_all_homes
},
3274 r
= parse_argv(argc
, argv
);
3278 return dispatch_verb(argc
, argv
, verbs
, NULL
);
3281 DEFINE_MAIN_FUNCTION(run
);