1 /* SPDX-License-Identifier: LGPL-2.1+ */
7 #include "alloc-util.h"
8 #include "ask-password-api.h"
9 #include "bus-common-errors.h"
10 #include "bus-error.h"
12 #include "cgroup-util.h"
13 #include "dns-domain.h"
17 #include "format-table.h"
18 #include "format-util.h"
20 #include "hexdecoct.h"
21 #include "home-util.h"
22 #include "libcrypt-util.h"
23 #include "locale-util.h"
24 #include "main-func.h"
25 #include "memory-util.h"
26 #include "openssl-util.h"
28 #include "parse-util.h"
29 #include "path-util.h"
30 #include "pkcs11-util.h"
31 #include "pretty-print.h"
32 #include "process-util.h"
33 #include "pwquality-util.h"
34 #include "random-util.h"
35 #include "rlimit-util.h"
36 #include "spawn-polkit-agent.h"
37 #include "terminal-util.h"
38 #include "user-record-show.h"
39 #include "user-record-util.h"
40 #include "user-record.h"
41 #include "user-util.h"
44 static PagerFlags arg_pager_flags
= 0;
45 static bool arg_legend
= true;
46 static bool arg_ask_password
= true;
47 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
48 static const char *arg_host
= NULL
;
49 static const char *arg_identity
= NULL
;
50 static JsonVariant
*arg_identity_extra
= NULL
;
51 static JsonVariant
*arg_identity_extra_privileged
= NULL
;
52 static JsonVariant
*arg_identity_extra_this_machine
= NULL
;
53 static JsonVariant
*arg_identity_extra_rlimits
= NULL
;
54 static char **arg_identity_filter
= NULL
; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
55 static char **arg_identity_filter_rlimits
= NULL
;
56 static uint64_t arg_disk_size
= UINT64_MAX
;
57 static uint64_t arg_disk_size_relative
= UINT64_MAX
;
58 static char **arg_pkcs11_token_uri
= NULL
;
59 static bool arg_json
= false;
60 static JsonFormatFlags arg_json_format_flags
= 0;
61 static bool arg_and_resize
= false;
62 static bool arg_and_change_password
= false;
64 EXPORT_FORMAT_FULL
, /* export the full record */
65 EXPORT_FORMAT_STRIPPED
, /* strip "state" + "binding", but leave signature in place */
66 EXPORT_FORMAT_MINIMAL
, /* also strip signature */
67 } arg_export_format
= EXPORT_FORMAT_FULL
;
69 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra
, json_variant_unrefp
);
70 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine
, json_variant_unrefp
);
71 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged
, json_variant_unrefp
);
72 STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits
, json_variant_unrefp
);
73 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter
, strv_freep
);
74 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits
, strv_freep
);
75 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri
, strv_freep
);
77 static bool identity_properties_specified(void) {
80 !json_variant_is_blank_object(arg_identity_extra
) ||
81 !json_variant_is_blank_object(arg_identity_extra_privileged
) ||
82 !json_variant_is_blank_object(arg_identity_extra_this_machine
) ||
83 !json_variant_is_blank_object(arg_identity_extra_rlimits
) ||
84 !strv_isempty(arg_identity_filter
) ||
85 !strv_isempty(arg_identity_filter_rlimits
) ||
86 !strv_isempty(arg_pkcs11_token_uri
);
89 static int acquire_bus(sd_bus
**bus
) {
97 r
= bus_connect_transport(arg_transport
, arg_host
, false, bus
);
99 return log_error_errno(r
, "Failed to connect to bus: %m");
101 (void) sd_bus_set_allow_interactive_authorization(*bus
, arg_ask_password
);
106 static int list_homes(int argc
, char *argv
[], void *userdata
) {
107 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
108 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
109 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
110 _cleanup_(table_unrefp
) Table
*table
= NULL
;
113 (void) pager_open(arg_pager_flags
);
115 r
= acquire_bus(&bus
);
119 r
= sd_bus_call_method(
121 "org.freedesktop.home1",
122 "/org/freedesktop/home1",
123 "org.freedesktop.home1.Manager",
129 return log_error_errno(r
, "Failed to list homes: %s", bus_error_message(&error
, r
));
131 table
= table_new("name", "uid", "gid", "state", "realname", "home", "shell");
135 r
= sd_bus_message_enter_container(reply
, 'a', "(susussso)");
137 return bus_log_parse_error(r
);
140 const char *name
, *state
, *realname
, *home
, *shell
, *color
;
144 r
= sd_bus_message_read(reply
, "(susussso)", &name
, &uid
, &state
, &gid
, &realname
, &home
, &shell
, NULL
);
146 return bus_log_parse_error(r
);
150 r
= table_add_many(table
,
155 return log_error_errno(r
, "Failed to add row to table: %m");
158 r
= table_add_cell(table
, &cell
, TABLE_STRING
, state
);
160 return log_error_errno(r
, "Failed to add field to table: %m");
162 color
= user_record_state_color(state
);
164 (void) table_set_color(table
, cell
, color
);
166 r
= table_add_many(table
,
167 TABLE_STRING
, strna(empty_to_null(realname
)),
169 TABLE_STRING
, strna(empty_to_null(shell
)));
171 return log_error_errno(r
, "Failed to add row to table: %m");
174 r
= sd_bus_message_exit_container(reply
);
176 return bus_log_parse_error(r
);
178 if (table_get_rows(table
) > 1 || arg_json
) {
179 r
= table_set_sort(table
, (size_t) 0, (size_t) -1);
181 return log_error_errno(r
, "Failed to sort table: %m");
183 table_set_header(table
, arg_legend
);
186 r
= table_print_json(table
, stdout
, arg_json_format_flags
);
188 r
= table_print(table
, NULL
);
190 return log_error_errno(r
, "Failed to show table: %m");
193 if (arg_legend
&& !arg_json
) {
194 if (table_get_rows(table
) > 1)
195 printf("\n%zu homes listed.\n", table_get_rows(table
) - 1);
197 printf("No homes.\n");
203 static int acquire_existing_password(const char *user_name
, UserRecord
*hr
, bool emphasize_current
) {
204 _cleanup_(strv_free_erasep
) char **password
= NULL
;
205 _cleanup_free_
char *question
= NULL
;
212 e
= getenv("PASSWORD");
214 /* People really shouldn't use environment variables for passing passwords. We support this
215 * only for testing purposes, and do not document the behaviour, so that people won't
216 * actually use this outside of testing. */
218 r
= user_record_set_password(hr
, STRV_MAKE(e
), true);
220 return log_error_errno(r
, "Failed to store password: %m");
224 if (unsetenv("PASSWORD") < 0)
225 return log_error_errno(errno
, "Failed to unset $PASSWORD: %m");
230 if (asprintf(&question
, emphasize_current
?
231 "Please enter current password for user %s:" :
232 "Please enter password for user %s:",
236 r
= ask_password_auto(question
, "user-home", NULL
, "home-password", USEC_INFINITY
, ASK_PASSWORD_ACCEPT_CACHED
|ASK_PASSWORD_PUSH_CACHE
, &password
);
238 return log_error_errno(r
, "Failed to acquire password: %m");
240 r
= user_record_set_password(hr
, password
, true);
242 return log_error_errno(r
, "Failed to store password: %m");
247 static int acquire_pkcs11_pin(const char *user_name
, UserRecord
*hr
) {
248 _cleanup_(strv_free_erasep
) char **pin
= NULL
;
249 _cleanup_free_
char *question
= NULL
;
258 r
= user_record_set_pkcs11_pin(hr
, STRV_MAKE(e
), false);
260 return log_error_errno(r
, "Failed to store PKCS#11 PIN: %m");
264 if (unsetenv("PIN") < 0)
265 return log_error_errno(errno
, "Failed to unset $PIN: %m");
270 if (asprintf(&question
, "Please enter security token PIN for user %s:", user_name
) < 0)
273 /* We never cache or use cached PINs, since usually there are only very few attempts allowed before the PIN is blocked */
274 r
= ask_password_auto(question
, "user-home", NULL
, "pkcs11-pin", USEC_INFINITY
, 0, &pin
);
276 return log_error_errno(r
, "Failed to acquire security token PIN: %m");
278 r
= user_record_set_pkcs11_pin(hr
, pin
, false);
280 return log_error_errno(r
, "Failed to store security token PIN: %m");
285 static int handle_generic_user_record_error(
286 const char *user_name
,
288 const sd_bus_error
*error
,
290 bool emphasize_current_password
) {
296 if (sd_bus_error_has_name(error
, BUS_ERROR_HOME_ABSENT
))
297 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE
),
298 "Home of user %s is currently absent, please plug in the necessary stroage device or backing file system.", user_name
);
300 else if (sd_bus_error_has_name(error
, BUS_ERROR_AUTHENTICATION_LIMIT_HIT
))
301 return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS
),
302 "Too frequent unsuccessful login attempts for user %s, try again later.", user_name
);
304 else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD
)) {
306 if (!strv_isempty(hr
->password
))
307 log_notice("Password incorrect or not sufficient, please try again.");
309 r
= acquire_existing_password(user_name
, hr
, emphasize_current_password
);
313 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
)) {
315 if (strv_isempty(hr
->password
))
316 log_notice("Security token not inserted, please enter password.");
318 log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
320 r
= acquire_existing_password(user_name
, hr
, emphasize_current_password
);
324 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_NEEDED
)) {
326 r
= acquire_pkcs11_pin(user_name
, hr
);
330 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED
)) {
332 log_notice("Please authenticate physically on security token.");
334 r
= user_record_set_pkcs11_protected_authentication_path_permitted(hr
, true);
336 return log_error_errno(r
, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
338 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_LOCKED
))
339 return log_error_errno(SYNTHETIC_ERRNO(EPERM
), "Security token PIN is locked, please unlock security token PIN first.");
341 else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN
)) {
343 log_notice("Security token PIN incorrect, please try again.");
345 r
= acquire_pkcs11_pin(user_name
, hr
);
349 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT
)) {
351 log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
353 r
= acquire_pkcs11_pin(user_name
, hr
);
357 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT
)) {
359 log_notice("Security token PIN incorrect, please try again (only one try left!).");
361 r
= acquire_pkcs11_pin(user_name
, hr
);
365 return log_error_errno(ret
, "Operation on home %s failed: %s", user_name
, bus_error_message(error
, ret
));
370 static int activate_home(int argc
, char *argv
[], void *userdata
) {
371 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
375 r
= acquire_bus(&bus
);
379 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
380 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
382 secret
= user_record_new();
387 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
388 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
390 r
= sd_bus_message_new_method_call(
393 "org.freedesktop.home1",
394 "/org/freedesktop/home1",
395 "org.freedesktop.home1.Manager",
398 return bus_log_create_error(r
);
400 r
= sd_bus_message_append(m
, "s", *i
);
402 return bus_log_create_error(r
);
404 r
= bus_message_append_secret(m
, secret
);
406 return bus_log_create_error(r
);
408 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
410 r
= handle_generic_user_record_error(*i
, secret
, &error
, r
, false);
425 static int deactivate_home(int argc
, char *argv
[], void *userdata
) {
426 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
430 r
= acquire_bus(&bus
);
434 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
435 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
436 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
438 r
= sd_bus_message_new_method_call(
441 "org.freedesktop.home1",
442 "/org/freedesktop/home1",
443 "org.freedesktop.home1.Manager",
446 return bus_log_create_error(r
);
448 r
= sd_bus_message_append(m
, "s", *i
);
450 return bus_log_create_error(r
);
452 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
454 log_error_errno(r
, "Failed to deactivate user home: %s", bus_error_message(&error
, r
));
463 static void dump_home_record(UserRecord
*hr
) {
468 if (hr
->incomplete
) {
470 log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr
->user_name
);
474 _cleanup_(user_record_unrefp
) UserRecord
*stripped
= NULL
;
476 if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
477 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_EMBEDDED
, &stripped
);
478 else if (arg_export_format
== EXPORT_FORMAT_MINIMAL
)
479 r
= user_record_clone(hr
, USER_RECORD_EXTRACT_SIGNABLE
, &stripped
);
483 log_warning_errno(r
, "Failed to strip user record, ignoring: %m");
487 json_variant_dump(hr
->json
, arg_json_format_flags
, stdout
, NULL
);
489 user_record_show(hr
, true);
492 static char **mangle_user_list(char **list
, char ***ret_allocated
) {
493 _cleanup_free_
char *myself
= NULL
;
496 if (!strv_isempty(list
)) {
497 *ret_allocated
= NULL
;
501 myself
= getusername_malloc();
509 l
[0] = TAKE_PTR(myself
);
516 static int inspect_home(int argc
, char *argv
[], void *userdata
) {
517 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
518 _cleanup_(strv_freep
) char **mangled_list
= NULL
;
522 (void) pager_open(arg_pager_flags
);
524 r
= acquire_bus(&bus
);
528 items
= mangle_user_list(strv_skip(argv
, 1), &mangled_list
);
532 STRV_FOREACH(i
, items
) {
533 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
534 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
535 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
536 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
541 r
= parse_uid(*i
, &uid
);
543 if (!valid_user_group_name(*i
)) {
544 log_error("Invalid user name '%s'.", *i
);
551 r
= sd_bus_call_method(
553 "org.freedesktop.home1",
554 "/org/freedesktop/home1",
555 "org.freedesktop.home1.Manager",
556 "GetUserRecordByName",
562 r
= sd_bus_call_method(
564 "org.freedesktop.home1",
565 "/org/freedesktop/home1",
566 "org.freedesktop.home1.Manager",
567 "GetUserRecordByUID",
575 log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
582 r
= sd_bus_message_read(reply
, "sbo", &json
, &incomplete
, NULL
);
584 bus_log_parse_error(r
);
591 r
= json_parse(json
, JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
593 log_error_errno(r
, "Failed to parse JSON identity: %m");
600 hr
= user_record_new();
604 r
= user_record_load(hr
, v
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_LOG
);
612 hr
->incomplete
= incomplete
;
613 dump_home_record(hr
);
619 static int authenticate_home(int argc
, char *argv
[], void *userdata
) {
620 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
621 _cleanup_(strv_freep
) char **mangled_list
= NULL
;
625 items
= mangle_user_list(strv_skip(argv
, 1), &mangled_list
);
629 r
= acquire_bus(&bus
);
633 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
635 STRV_FOREACH(i
, items
) {
636 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
638 secret
= user_record_new();
643 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
644 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
646 r
= sd_bus_message_new_method_call(
649 "org.freedesktop.home1",
650 "/org/freedesktop/home1",
651 "org.freedesktop.home1.Manager",
654 return bus_log_create_error(r
);
656 r
= sd_bus_message_append(m
, "s", *i
);
658 return bus_log_create_error(r
);
660 r
= bus_message_append_secret(m
, secret
);
662 return bus_log_create_error(r
);
664 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
666 r
= handle_generic_user_record_error(*i
, secret
, &error
, r
, false);
681 static int update_last_change(JsonVariant
**v
, bool with_password
, bool override
) {
688 n
= now(CLOCK_REALTIME
);
690 c
= json_variant_by_key(*v
, "lastChangeUSec");
695 goto update_password
;
697 if (!json_variant_is_unsigned(c
))
698 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec field is not an unsigned integer, refusing.");
700 u
= json_variant_unsigned(c
);
702 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastChangeUSec is from the future, can't update.");
705 r
= json_variant_set_field_unsigned(v
, "lastChangeUSec", n
);
707 return log_error_errno(r
, "Failed to update lastChangeUSec: %m");
713 c
= json_variant_by_key(*v
, "lastPasswordChangeUSec");
720 if (!json_variant_is_unsigned(c
))
721 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec field is not an unsigned integer, refusing.");
723 u
= json_variant_unsigned(c
);
725 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "lastPasswordChangeUSec is from the future, can't update.");
728 r
= json_variant_set_field_unsigned(v
, "lastPasswordChangeUSec", n
);
730 return log_error_errno(r
, "Failed to update lastPasswordChangeUSec: %m");
735 static int apply_identity_changes(JsonVariant
**_v
) {
736 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
741 v
= json_variant_ref(*_v
);
743 r
= json_variant_filter(&v
, arg_identity_filter
);
745 return log_error_errno(r
, "Failed to filter identity: %m");
747 r
= json_variant_merge(&v
, arg_identity_extra
);
749 return log_error_errno(r
, "Failed to merge identities: %m");
751 if (arg_identity_extra_this_machine
|| !strv_isempty(arg_identity_filter
)) {
752 _cleanup_(json_variant_unrefp
) JsonVariant
*per_machine
= NULL
, *mmid
= NULL
;
753 char mids
[SD_ID128_STRING_MAX
];
756 r
= sd_id128_get_machine(&mid
);
758 return log_error_errno(r
, "Failed to acquire machine ID: %m");
760 r
= json_variant_new_string(&mmid
, sd_id128_to_string(mid
, mids
));
762 return log_error_errno(r
, "Failed to allocate matchMachineId object: %m");
764 per_machine
= json_variant_ref(json_variant_by_key(v
, "perMachine"));
766 _cleanup_(json_variant_unrefp
) JsonVariant
*npm
= NULL
, *add
= NULL
;
767 _cleanup_free_ JsonVariant
**array
= NULL
;
771 if (!json_variant_is_array(per_machine
))
772 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine field is not an array, refusing.");
774 array
= new(JsonVariant
*, json_variant_elements(per_machine
) + 1);
778 JSON_VARIANT_ARRAY_FOREACH(z
, per_machine
) {
781 if (!json_variant_is_object(z
))
782 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "perMachine entry is not an object, refusing.");
786 u
= json_variant_by_key(z
, "matchMachineId");
790 if (!json_variant_equal(u
, mmid
))
793 r
= json_variant_merge(&add
, z
);
795 return log_error_errno(r
, "Failed to merge perMachine entry: %m");
800 r
= json_variant_filter(&add
, arg_identity_filter
);
802 return log_error_errno(r
, "Failed to filter perMachine: %m");
804 r
= json_variant_merge(&add
, arg_identity_extra_this_machine
);
806 return log_error_errno(r
, "Failed to merge in perMachine fields: %m");
808 if (arg_identity_filter_rlimits
|| arg_identity_extra_rlimits
) {
809 _cleanup_(json_variant_unrefp
) JsonVariant
*rlv
= NULL
;
811 rlv
= json_variant_ref(json_variant_by_key(add
, "resourceLimits"));
813 r
= json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
815 return log_error_errno(r
, "Failed to filter resource limits: %m");
817 r
= json_variant_merge(&rlv
, arg_identity_extra_rlimits
);
819 return log_error_errno(r
, "Failed to set resource limits: %m");
821 if (json_variant_is_blank_object(rlv
)) {
822 r
= json_variant_filter(&add
, STRV_MAKE("resourceLimits"));
824 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
826 r
= json_variant_set_field(&add
, "resourceLimits", rlv
);
828 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
832 if (!json_variant_is_blank_object(add
)) {
833 r
= json_variant_set_field(&add
, "matchMachineId", mmid
);
835 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
840 r
= json_variant_new_array(&npm
, array
, i
);
842 return log_error_errno(r
, "Failed to allocate new perMachine array: %m");
844 json_variant_unref(per_machine
);
845 per_machine
= TAKE_PTR(npm
);
847 _cleanup_(json_variant_unrefp
) JsonVariant
*item
= json_variant_ref(arg_identity_extra_this_machine
);
849 if (arg_identity_extra_rlimits
) {
850 r
= json_variant_set_field(&item
, "resourceLimits", arg_identity_extra_rlimits
);
852 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
855 r
= json_variant_set_field(&item
, "matchMachineId", mmid
);
857 return log_error_errno(r
, "Failed to set matchMachineId field: %m");
859 r
= json_variant_append_array(&per_machine
, item
);
861 return log_error_errno(r
, "Failed to append to perMachine array: %m");
864 r
= json_variant_set_field(&v
, "perMachine", per_machine
);
866 return log_error_errno(r
, "Failed to update per machine record: %m");
869 if (arg_identity_extra_privileged
|| arg_identity_filter
) {
870 _cleanup_(json_variant_unrefp
) JsonVariant
*privileged
= NULL
;
872 privileged
= json_variant_ref(json_variant_by_key(v
, "privileged"));
874 r
= json_variant_filter(&privileged
, arg_identity_filter
);
876 return log_error_errno(r
, "Failed to filter identity (privileged part): %m");
878 r
= json_variant_merge(&privileged
, arg_identity_extra_privileged
);
880 return log_error_errno(r
, "Failed to merge identities (privileged part): %m");
882 if (json_variant_is_blank_object(privileged
)) {
883 r
= json_variant_filter(&v
, STRV_MAKE("privileged"));
885 return log_error_errno(r
, "Failed to drop privileged part from identity: %m");
887 r
= json_variant_set_field(&v
, "privileged", privileged
);
889 return log_error_errno(r
, "Failed to update privileged part of identity: %m");
893 if (arg_identity_filter_rlimits
) {
894 _cleanup_(json_variant_unrefp
) JsonVariant
*rlv
= NULL
;
896 rlv
= json_variant_ref(json_variant_by_key(v
, "resourceLimits"));
898 r
= json_variant_filter(&rlv
, arg_identity_filter_rlimits
);
900 return log_error_errno(r
, "Failed to filter resource limits: %m");
902 /* Note that we only filter resource limits here, but don't apply them. We do that in the perMachine section */
904 if (json_variant_is_blank_object(rlv
)) {
905 r
= json_variant_filter(&v
, STRV_MAKE("resourceLimits"));
907 return log_error_errno(r
, "Failed to drop resource limits field from identity: %m");
909 r
= json_variant_set_field(&v
, "resourceLimits", rlv
);
911 return log_error_errno(r
, "Failed to update resource limits of identity: %m");
915 json_variant_unref(*_v
);
921 static int add_disposition(JsonVariant
**v
) {
926 if (json_variant_by_key(*v
, "disposition"))
929 /* Set the disposition to regular, if not configured explicitly */
930 r
= json_variant_set_field_string(v
, "disposition", "regular");
932 return log_error_errno(r
, "Failed to set disposition field: %m");
937 struct pkcs11_callback_data
{
942 static void pkcs11_callback_data_release(struct pkcs11_callback_data
*data
) {
943 erase_and_free(data
->pin_used
);
944 X509_free(data
->cert
);
948 static int pkcs11_callback(
950 CK_SESSION_HANDLE session
,
952 const CK_SLOT_INFO
*slot_info
,
953 const CK_TOKEN_INFO
*token_info
,
957 _cleanup_(erase_and_freep
) char *pin_used
= NULL
;
958 struct pkcs11_callback_data
*data
= userdata
;
959 CK_OBJECT_HANDLE object
;
968 /* Called for every token matching our URI */
970 r
= pkcs11_token_login(m
, session
, slot_id
, token_info
, "home directory operation", "user-home", "pkcs11-pin", UINT64_MAX
, &pin_used
);
974 r
= pkcs11_token_find_x509_certificate(m
, session
, uri
, &object
);
978 r
= pkcs11_token_read_x509_certificate(m
, session
, object
, &data
->cert
);
982 /* Let's read some random data off the token and write it to the kernel pool before we generate our
983 * random key from it. This way we can claim the quality of the RNG is at least as good as the
984 * kernel's and the token's pool */
985 (void) pkcs11_token_acquire_rng(m
, session
);
987 data
->pin_used
= TAKE_PTR(pin_used
);
992 static int acquire_pkcs11_certificate(
995 char **ret_pin_used
) {
998 _cleanup_(pkcs11_callback_data_release
) struct pkcs11_callback_data data
= {};
1001 r
= pkcs11_find_token(uri
, pkcs11_callback
, &data
);
1002 if (r
== -EAGAIN
) /* pkcs11_find_token() doesn't log about this error, but all others */
1003 return log_error_errno(ENXIO
, "Specified PKCS#11 token with URI '%s' not found.", uri
);
1007 *ret_cert
= TAKE_PTR(data
.cert
);
1008 *ret_pin_used
= TAKE_PTR(data
.pin_used
);
1012 return log_error_errno(EOPNOTSUPP
, "PKCS#11 tokens not supported on this build.");
1016 static int encrypt_bytes(
1018 const void *decrypted_key
,
1019 size_t decrypted_key_size
,
1020 void **ret_encrypt_key
,
1021 size_t *ret_encrypt_key_size
) {
1023 _cleanup_(EVP_PKEY_CTX_freep
) EVP_PKEY_CTX
*ctx
= NULL
;
1024 _cleanup_free_
void *b
= NULL
;
1027 ctx
= EVP_PKEY_CTX_new(pkey
, NULL
);
1029 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to allocate public key context");
1031 if (EVP_PKEY_encrypt_init(ctx
) <= 0)
1032 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to initialize public key context");
1034 if (EVP_PKEY_CTX_set_rsa_padding(ctx
, RSA_PKCS1_PADDING
) <= 0)
1035 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to configure PKCS#1 padding");
1037 if (EVP_PKEY_encrypt(ctx
, NULL
, &l
, decrypted_key
, decrypted_key_size
) <= 0)
1038 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to determine encrypted key size");
1044 if (EVP_PKEY_encrypt(ctx
, b
, &l
, decrypted_key
, decrypted_key_size
) <= 0)
1045 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to determine encrypted key size");
1047 *ret_encrypt_key
= TAKE_PTR(b
);
1048 *ret_encrypt_key_size
= l
;
1053 static int add_pkcs11_pin(JsonVariant
**v
, const char *pin
) {
1054 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
, *l
= NULL
;
1055 _cleanup_(strv_free_erasep
) char **pins
= NULL
;
1063 w
= json_variant_ref(json_variant_by_key(*v
, "secret"));
1064 l
= json_variant_ref(json_variant_by_key(w
, "pkcs11Pin"));
1066 r
= json_variant_strv(l
, &pins
);
1068 return log_error_errno(r
, "Failed to convert PIN array: %m");
1070 if (strv_find(pins
, pin
))
1073 r
= strv_extend(&pins
, pin
);
1079 l
= json_variant_unref(l
);
1081 r
= json_variant_new_array_strv(&l
, pins
);
1083 return log_error_errno(r
, "Failed to allocate new PIN array JSON: %m");
1085 json_variant_sensitive(l
);
1087 r
= json_variant_set_field(&w
, "pkcs11Pin", l
);
1089 return log_error_errno(r
, "Failed to update PIN field: %m");
1091 r
= json_variant_set_field(v
, "secret", w
);
1093 return log_error_errno(r
, "Failed to update secret object: %m");
1098 static int add_pkcs11_encrypted_key(
1101 const void *encrypted_key
, size_t encrypted_key_size
,
1102 const void *decrypted_key
, size_t decrypted_key_size
) {
1104 _cleanup_(json_variant_unrefp
) JsonVariant
*l
= NULL
, *w
= NULL
, *e
= NULL
;
1105 _cleanup_(erase_and_freep
) char *base64_encoded
= NULL
;
1106 _cleanup_free_
char *salt
= NULL
;
1107 struct crypt_data cd
= {};
1113 assert(encrypted_key
);
1114 assert(encrypted_key_size
> 0);
1115 assert(decrypted_key
);
1116 assert(decrypted_key_size
> 0);
1118 r
= make_salt(&salt
);
1120 return log_error_errno(r
, "Failed to generate salt: %m");
1122 /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
1123 * expect a NUL terminated string, and we use a binary key */
1124 r
= base64mem(decrypted_key
, decrypted_key_size
, &base64_encoded
);
1126 return log_error_errno(r
, "Failed to base64 encode secret key: %m");
1129 k
= crypt_r(base64_encoded
, salt
, &cd
);
1131 return log_error_errno(errno_or_else(EINVAL
), "Failed to UNIX hash secret key: %m");
1133 r
= json_build(&e
, JSON_BUILD_OBJECT(
1134 JSON_BUILD_PAIR("uri", JSON_BUILD_STRING(uri
)),
1135 JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(encrypted_key
, encrypted_key_size
)),
1136 JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(k
))));
1138 return log_error_errno(r
, "Failed to build encrypted JSON key object: %m");
1140 w
= json_variant_ref(json_variant_by_key(*v
, "privileged"));
1141 l
= json_variant_ref(json_variant_by_key(w
, "pkcs11EncryptedKey"));
1143 r
= json_variant_append_array(&l
, e
);
1145 return log_error_errno(r
, "Failed append PKCS#11 encrypted key: %m");
1147 r
= json_variant_set_field(&w
, "pkcs11EncryptedKey", l
);
1149 return log_error_errno(r
, "Failed to set PKCS#11 encrypted key: %m");
1151 r
= json_variant_set_field(v
, "privileged", w
);
1153 return log_error_errno(r
, "Failed to update privileged field: %m");
1158 static int add_pkcs11_token_uri(JsonVariant
**v
, const char *uri
) {
1159 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
1160 _cleanup_strv_free_
char **l
= NULL
;
1166 w
= json_variant_ref(json_variant_by_key(*v
, "pkcs11TokenUri"));
1168 r
= json_variant_strv(w
, &l
);
1170 return log_error_errno(r
, "Failed to parse PKCS#11 token list: %m");
1172 if (strv_contains(l
, uri
))
1176 r
= strv_extend(&l
, uri
);
1180 w
= json_variant_unref(w
);
1181 r
= json_variant_new_array_strv(&w
, l
);
1183 return log_error_errno(r
, "Failed to create PKCS#11 token URI JSON: %m");
1185 r
= json_variant_set_field(v
, "pkcs11TokenUri", w
);
1187 return log_error_errno(r
, "Failed to update PKCS#11 token URI list: %m");
1192 static int add_pkcs11_key_data(JsonVariant
**v
, const char *uri
) {
1193 _cleanup_(erase_and_freep
) void *decrypted_key
= NULL
, *encrypted_key
= NULL
;
1194 _cleanup_(erase_and_freep
) char *pin
= NULL
;
1195 size_t decrypted_key_size
, encrypted_key_size
;
1196 _cleanup_(X509_freep
) X509
*cert
= NULL
;
1204 r
= acquire_pkcs11_certificate(uri
, &cert
, &pin
);
1208 pkey
= X509_get0_pubkey(cert
);
1210 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to exract public key from X.509 certificate.");
1212 if (EVP_PKEY_base_id(pkey
) != EVP_PKEY_RSA
)
1213 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "X.509 certificate does not refer to RSA key.");
1215 rsa
= EVP_PKEY_get0_RSA(pkey
);
1217 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Failed to acquire RSA public key from X.509 certificate.");
1219 bits
= RSA_bits(rsa
);
1220 log_debug("Bits in RSA key: %i", bits
);
1222 /* We use PKCS#1 padding for the RSA cleartext, hence let's leave some extra space for it, hence only
1223 * generate a random key half the size of the RSA length */
1224 decrypted_key_size
= bits
/ 8 / 2;
1226 if (decrypted_key_size
< 1)
1227 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Uh, RSA key size too short?");
1229 log_debug("Generating %zu bytes random key.", decrypted_key_size
);
1231 decrypted_key
= malloc(decrypted_key_size
);
1235 r
= genuine_random_bytes(decrypted_key
, decrypted_key_size
, RANDOM_BLOCK
);
1237 return log_error_errno(r
, "Failed to generate random key: %m");
1239 r
= encrypt_bytes(pkey
, decrypted_key
, decrypted_key_size
, &encrypted_key
, &encrypted_key_size
);
1241 return log_error_errno(r
, "Failed to encrypt key: %m");
1243 /* Add the token URI to the public part of the record. */
1244 r
= add_pkcs11_token_uri(v
, uri
);
1248 /* Include the encrypted version of the random key we just generated in the privileged part of the record */
1249 r
= add_pkcs11_encrypted_key(
1252 encrypted_key
, encrypted_key_size
,
1253 decrypted_key
, decrypted_key_size
);
1257 /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed
1258 * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or
1260 r
= add_pkcs11_pin(v
, pin
);
1267 static int acquire_new_home_record(UserRecord
**ret
) {
1268 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
1269 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1276 unsigned line
, column
;
1278 r
= json_parse_file(
1279 streq(arg_identity
, "-") ? stdin
: NULL
,
1280 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, JSON_PARSE_SENSITIVE
, &v
, &line
, &column
);
1282 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
1285 r
= apply_identity_changes(&v
);
1289 r
= add_disposition(&v
);
1293 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
1294 r
= add_pkcs11_key_data(&v
, *i
);
1299 r
= update_last_change(&v
, true, false);
1304 json_variant_dump(v
, JSON_FORMAT_PRETTY
, NULL
, NULL
);
1306 hr
= user_record_new();
1310 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
);
1314 *ret
= TAKE_PTR(hr
);
1318 static int acquire_new_password(
1319 const char *user_name
,
1330 e
= getenv("NEWPASSWORD");
1332 /* As above, this is not for use, just for testing */
1334 r
= user_record_set_password(hr
, STRV_MAKE(e
), /* prepend = */ false);
1336 return log_error_errno(r
, "Failed to store password: %m");
1340 if (unsetenv("NEWPASSWORD") < 0)
1341 return log_error_errno(errno
, "Failed to unse $NEWPASSWORD: %m");
1347 (void) suggest_passwords();
1350 _cleanup_(strv_free_erasep
) char **first
= NULL
, **second
= NULL
;
1351 _cleanup_free_
char *question
= NULL
;
1354 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY
), "Too many attempts, giving up:");
1356 if (asprintf(&question
, "Please enter new password for user %s:", user_name
) < 0)
1359 r
= ask_password_auto(question
, "user-home", NULL
, "home-password", USEC_INFINITY
, 0, &first
);
1361 return log_error_errno(r
, "Failed to acquire password: %m");
1363 question
= mfree(question
);
1364 if (asprintf(&question
, "Please enter new password for user %s (repeat):", user_name
) < 0)
1367 r
= ask_password_auto(question
, "user-home", NULL
, "home-password", USEC_INFINITY
, 0, &second
);
1369 return log_error_errno(r
, "Failed to acquire password: %m");
1371 if (strv_equal(first
, second
)) {
1372 r
= user_record_set_password(hr
, first
, /* prepend = */ false);
1374 return log_error_errno(r
, "Failed to store password: %m");
1379 log_error("Password didn't mach, try again.");
1383 static int create_home(int argc
, char *argv
[], void *userdata
) {
1384 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1385 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1386 _cleanup_strv_free_
char **original_hashed_passwords
= NULL
;
1389 r
= acquire_bus(&bus
);
1393 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1396 /* If a username was specified, use it */
1398 if (valid_user_group_name(argv
[1]))
1399 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", argv
[1]);
1401 _cleanup_free_
char *un
= NULL
, *rr
= NULL
;
1403 /* Before we consider the user name invalid, let's check if we can split it? */
1404 r
= split_user_name_realm(argv
[1], &un
, &rr
);
1406 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name '%s' is not valid: %m", argv
[1]);
1409 r
= json_variant_set_field_string(&arg_identity_extra
, "realm", rr
);
1411 return log_error_errno(r
, "Failed to set realm field: %m");
1414 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", un
);
1417 return log_error_errno(r
, "Failed to set userName field: %m");
1419 /* If neither a username nor an identity have been specified we cannot operate. */
1421 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name required.");
1424 r
= acquire_new_home_record(&hr
);
1428 /* Remember the original hashed paswords before we add our own, so that we can return to them later,
1429 * should the entered password turn out not to be acceptable. */
1430 original_hashed_passwords
= strv_copy(hr
->hashed_password
);
1431 if (!original_hashed_passwords
)
1434 /* If the JSON record carries no plain text password, then let's query it manually. */
1435 if (!hr
->password
) {
1437 if (strv_isempty(hr
->hashed_password
)) {
1438 /* No regular (i.e. non-PKCS#11) hashed passwords set in the record, let's fix that. */
1439 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ true);
1443 r
= user_record_make_hashed_password(hr
, hr
->password
, /* extend = */ true);
1445 return log_error_errno(r
, "Failed to hash password: %m");
1447 /* There's a hash password set in the record, acquire the unhashed version of it. */
1448 r
= acquire_existing_password(hr
->user_name
, hr
, false);
1454 if (hr
->enforce_password_policy
== 0) {
1455 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1457 /* If password quality enforcement is disabled, let's at least warn client side */
1459 r
= quality_check_password(hr
, hr
, &error
);
1461 log_warning_errno(r
, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error
, r
));
1465 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1466 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1467 _cleanup_(erase_and_freep
) char *formatted
= NULL
;
1469 r
= json_variant_format(hr
->json
, 0, &formatted
);
1473 r
= sd_bus_message_new_method_call(
1476 "org.freedesktop.home1",
1477 "/org/freedesktop/home1",
1478 "org.freedesktop.home1.Manager",
1481 return bus_log_create_error(r
);
1483 r
= sd_bus_message_append(m
, "s", formatted
);
1485 return bus_log_create_error(r
);
1487 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1489 if (!sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
))
1490 return log_error_errno(r
, "Failed to create user home: %s", bus_error_message(&error
, r
));
1492 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
1493 log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
1497 r
= user_record_set_hashed_password(hr
, original_hashed_passwords
);
1501 r
= acquire_new_password(hr
->user_name
, hr
, /* suggest = */ false);
1505 r
= user_record_make_hashed_password(hr
, hr
->password
, /* extend = */ true);
1507 return log_error_errno(r
, "Failed to hash passwords: %m");
1513 static int remove_home(int argc
, char *argv
[], void *userdata
) {
1514 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1518 r
= acquire_bus(&bus
);
1522 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1524 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1525 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1526 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1528 r
= sd_bus_message_new_method_call(
1531 "org.freedesktop.home1",
1532 "/org/freedesktop/home1",
1533 "org.freedesktop.home1.Manager",
1536 return bus_log_create_error(r
);
1538 r
= sd_bus_message_append(m
, "s", *i
);
1540 return bus_log_create_error(r
);
1542 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1544 log_error_errno(r
, "Failed to remove home: %s", bus_error_message(&error
, r
));
1553 static int acquire_updated_home_record(
1555 const char *username
,
1558 _cleanup_(json_variant_unrefp
) JsonVariant
*json
= NULL
;
1559 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1566 unsigned line
, column
;
1569 r
= json_parse_file(
1570 streq(arg_identity
, "-") ? stdin
: NULL
,
1571 streq(arg_identity
, "-") ? "<stdin>" : arg_identity
, JSON_PARSE_SENSITIVE
, &json
, &line
, &column
);
1573 return log_error_errno(r
, "Failed to parse identity at %u:%u: %m", line
, column
);
1575 un
= json_variant_by_key(json
, "userName");
1577 if (!json_variant_is_string(un
) || (username
&& !streq(json_variant_string(un
), username
)))
1578 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "User name specified on command line and in JSON record do not match.");
1581 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No username specified.");
1583 r
= json_variant_set_field_string(&arg_identity_extra
, "userName", username
);
1585 return log_error_errno(r
, "Failed to set userName field: %m");
1589 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1590 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1594 if (!identity_properties_specified())
1595 return log_error_errno(SYNTHETIC_ERRNO(EALREADY
), "No field to change specified.");
1597 r
= sd_bus_call_method(
1599 "org.freedesktop.home1",
1600 "/org/freedesktop/home1",
1601 "org.freedesktop.home1.Manager",
1602 "GetUserRecordByName",
1608 return log_error_errno(r
, "Failed to acquire user home record: %s", bus_error_message(&error
, r
));
1610 r
= sd_bus_message_read(reply
, "sbo", &text
, &incomplete
, NULL
);
1612 return bus_log_parse_error(r
);
1615 return log_error_errno(SYNTHETIC_ERRNO(EACCES
), "Lacking rights to acquire user record including privileged metadata, can't update record.");
1617 r
= json_parse(text
, JSON_PARSE_SENSITIVE
, &json
, NULL
, NULL
);
1619 return log_error_errno(r
, "Failed to parse JSON identity: %m");
1621 reply
= sd_bus_message_unref(reply
);
1623 r
= json_variant_filter(&json
, STRV_MAKE("binding", "status", "signature"));
1625 return log_error_errno(r
, "Failed to strip binding and status from record to update: %m");
1628 r
= apply_identity_changes(&json
);
1632 STRV_FOREACH(i
, arg_pkcs11_token_uri
) {
1633 r
= add_pkcs11_key_data(&json
, *i
);
1638 /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
1640 r
= update_last_change(&json
, !!arg_pkcs11_token_uri
, !arg_identity
);
1645 json_variant_dump(json
, JSON_FORMAT_PRETTY
, NULL
, NULL
);
1647 hr
= user_record_new();
1651 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
);
1655 *ret
= TAKE_PTR(hr
);
1659 static int update_home(int argc
, char *argv
[], void *userdata
) {
1660 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1661 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
1662 _cleanup_free_
char *buffer
= NULL
;
1663 const char *username
;
1668 else if (!arg_identity
) {
1669 buffer
= getusername_malloc();
1677 r
= acquire_bus(&bus
);
1681 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1683 r
= acquire_updated_home_record(bus
, username
, &hr
);
1688 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1689 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1690 _cleanup_free_
char *formatted
= NULL
;
1692 r
= sd_bus_message_new_method_call(
1695 "org.freedesktop.home1",
1696 "/org/freedesktop/home1",
1697 "org.freedesktop.home1.Manager",
1700 return bus_log_create_error(r
);
1702 r
= json_variant_format(hr
->json
, 0, &formatted
);
1706 r
= sd_bus_message_append(m
, "s", formatted
);
1708 return bus_log_create_error(r
);
1710 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1712 if (arg_and_change_password
&&
1713 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1714 /* In the generic handler we'd ask for a password in this case, but when
1715 * changing passwords that's not sufficient, as we need to acquire all keys
1717 return log_error_errno(r
, "Security token not inserted, refusing.");
1719 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1726 /* Also sync down disk size to underlying LUKS/fscrypt/quota */
1727 while (arg_and_resize
) {
1728 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1729 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1731 log_debug("Resizing");
1733 r
= sd_bus_message_new_method_call(
1736 "org.freedesktop.home1",
1737 "/org/freedesktop/home1",
1738 "org.freedesktop.home1.Manager",
1741 return bus_log_create_error(r
);
1743 /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
1744 r
= sd_bus_message_append(m
, "st", hr
->user_name
, UINT64_MAX
);
1746 return bus_log_create_error(r
);
1748 r
= bus_message_append_secret(m
, hr
);
1750 return bus_log_create_error(r
);
1752 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1754 if (arg_and_change_password
&&
1755 sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1756 return log_error_errno(r
, "Security token not inserted, refusing.");
1758 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1765 /* Also sync down passwords to underlying LUKS/fscrypt */
1766 while (arg_and_change_password
) {
1767 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1768 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1770 log_debug("Propagating password");
1772 r
= sd_bus_message_new_method_call(
1775 "org.freedesktop.home1",
1776 "/org/freedesktop/home1",
1777 "org.freedesktop.home1.Manager",
1778 "ChangePasswordHome");
1780 return bus_log_create_error(r
);
1782 /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
1783 r
= sd_bus_message_append(m
, "ss", hr
->user_name
, "{}");
1785 return bus_log_create_error(r
);
1787 r
= bus_message_append_secret(m
, hr
);
1789 return bus_log_create_error(r
);
1791 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1793 if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1794 return log_error_errno(r
, "Security token not inserted, refusing.");
1796 r
= handle_generic_user_record_error(hr
->user_name
, hr
, &error
, r
, false);
1806 static int passwd_home(int argc
, char *argv
[], void *userdata
) {
1807 _cleanup_(user_record_unrefp
) UserRecord
*old_secret
= NULL
, *new_secret
= NULL
;
1808 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1809 _cleanup_free_
char *buffer
= NULL
;
1810 const char *username
;
1813 if (arg_pkcs11_token_uri
)
1814 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=…'.");
1815 if (identity_properties_specified())
1816 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "The 'passwd' verb does not permit changing other record properties at the same time.");
1821 buffer
= getusername_malloc();
1828 r
= acquire_bus(&bus
);
1832 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1834 old_secret
= user_record_new();
1838 new_secret
= user_record_new();
1842 r
= acquire_new_password(username
, new_secret
, /* suggest = */ true);
1847 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1848 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1850 r
= sd_bus_message_new_method_call(
1853 "org.freedesktop.home1",
1854 "/org/freedesktop/home1",
1855 "org.freedesktop.home1.Manager",
1856 "ChangePasswordHome");
1858 return bus_log_create_error(r
);
1860 r
= sd_bus_message_append(m
, "s", username
);
1862 return bus_log_create_error(r
);
1864 r
= bus_message_append_secret(m
, new_secret
);
1866 return bus_log_create_error(r
);
1868 r
= bus_message_append_secret(m
, old_secret
);
1870 return bus_log_create_error(r
);
1872 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1874 if (sd_bus_error_has_name(&error
, BUS_ERROR_LOW_PASSWORD_QUALITY
)) {
1876 log_error_errno(r
, "%s", bus_error_message(&error
, r
));
1878 r
= acquire_new_password(username
, new_secret
, /* suggest = */ false);
1880 } else if (sd_bus_error_has_name(&error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
))
1882 /* In the generic handler we'd ask for a password in this case, but when
1883 * changing passwords that's not sufficeint, as we need to acquire all keys
1885 return log_error_errno(r
, "Security token not inserted, refusing.");
1887 r
= handle_generic_user_record_error(username
, old_secret
, &error
, r
, true);
1897 static int resize_home(int argc
, char *argv
[], void *userdata
) {
1898 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1899 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
1900 uint64_t ds
= UINT64_MAX
;
1903 r
= acquire_bus(&bus
);
1907 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1909 if (arg_disk_size_relative
!= UINT64_MAX
||
1910 (argc
> 2 && parse_percent(argv
[2]) >= 0))
1911 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1912 "Relative disk size specification currently not supported when resizing.");
1915 r
= parse_size(argv
[2], 1024, &ds
);
1917 return log_error_errno(r
, "Failed to parse disk size parameter: %s", argv
[2]);
1920 if (arg_disk_size
!= UINT64_MAX
) {
1921 if (ds
!= UINT64_MAX
&& ds
!= arg_disk_size
)
1922 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Disk size specified twice and doesn't match, refusing.");
1927 secret
= user_record_new();
1932 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1933 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1935 r
= sd_bus_message_new_method_call(
1938 "org.freedesktop.home1",
1939 "/org/freedesktop/home1",
1940 "org.freedesktop.home1.Manager",
1943 return bus_log_create_error(r
);
1945 r
= sd_bus_message_append(m
, "st", argv
[1], ds
);
1947 return bus_log_create_error(r
);
1949 r
= bus_message_append_secret(m
, secret
);
1951 return bus_log_create_error(r
);
1953 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1955 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
1965 static int lock_home(int argc
, char *argv
[], void *userdata
) {
1966 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1970 r
= acquire_bus(&bus
);
1974 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
1975 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1976 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1978 r
= sd_bus_message_new_method_call(
1981 "org.freedesktop.home1",
1982 "/org/freedesktop/home1",
1983 "org.freedesktop.home1.Manager",
1986 return bus_log_create_error(r
);
1988 r
= sd_bus_message_append(m
, "s", *i
);
1990 return bus_log_create_error(r
);
1992 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1994 log_error_errno(r
, "Failed to lock home: %s", bus_error_message(&error
, r
));
2003 static int unlock_home(int argc
, char *argv
[], void *userdata
) {
2004 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2008 r
= acquire_bus(&bus
);
2012 STRV_FOREACH(i
, strv_skip(argv
, 1)) {
2013 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
2015 secret
= user_record_new();
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
= sd_bus_message_new_method_call(
2026 "org.freedesktop.home1",
2027 "/org/freedesktop/home1",
2028 "org.freedesktop.home1.Manager",
2031 return bus_log_create_error(r
);
2033 r
= sd_bus_message_append(m
, "s", *i
);
2035 return bus_log_create_error(r
);
2037 r
= bus_message_append_secret(m
, secret
);
2039 return bus_log_create_error(r
);
2041 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2043 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2058 static int with_home(int argc
, char *argv
[], void *userdata
) {
2059 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2060 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
2061 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2062 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
2063 _cleanup_close_
int acquired_fd
= -1;
2064 _cleanup_strv_free_
char **cmdline
= NULL
;
2069 r
= acquire_bus(&bus
);
2074 _cleanup_free_
char *shell
= NULL
;
2076 /* If no command is specified, spawn a shell */
2077 r
= get_shell(&shell
);
2079 return log_error_errno(r
, "Failed to acquire shell: %m");
2081 cmdline
= strv_new(shell
);
2083 cmdline
= strv_copy(argv
+ 2);
2087 secret
= user_record_new();
2092 r
= sd_bus_message_new_method_call(
2095 "org.freedesktop.home1",
2096 "/org/freedesktop/home1",
2097 "org.freedesktop.home1.Manager",
2100 return bus_log_create_error(r
);
2102 r
= sd_bus_message_append(m
, "s", argv
[1]);
2104 return bus_log_create_error(r
);
2106 r
= bus_message_append_secret(m
, secret
);
2108 return bus_log_create_error(r
);
2110 r
= sd_bus_message_append(m
, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
2112 return bus_log_create_error(r
);
2114 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
2115 m
= sd_bus_message_unref(m
);
2117 r
= handle_generic_user_record_error(argv
[1], secret
, &error
, r
, false);
2121 sd_bus_error_free(&error
);
2125 r
= sd_bus_message_read(reply
, "h", &fd
);
2127 return bus_log_parse_error(r
);
2129 acquired_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
2130 if (acquired_fd
< 0)
2131 return log_error_errno(errno
, "Failed to duplicate acquired fd: %m");
2133 reply
= sd_bus_message_unref(reply
);
2138 r
= sd_bus_call_method(
2140 "org.freedesktop.home1",
2141 "/org/freedesktop/home1",
2142 "org.freedesktop.home1.Manager",
2149 return log_error_errno(r
, "Failed to inspect home: %s", bus_error_message(&error
, r
));
2151 r
= sd_bus_message_read(reply
, "usussso", NULL
, NULL
, NULL
, NULL
, &home
, NULL
, NULL
);
2153 return bus_log_parse_error(r
);
2155 r
= safe_fork("(with)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_RLIMIT_NOFILE_SAFE
|FORK_REOPEN_LOG
, &pid
);
2159 if (chdir(home
) < 0) {
2160 log_error_errno(errno
, "Failed to change to directory %s: %m", home
);
2164 execvp(cmdline
[0], cmdline
);
2165 log_error_errno(errno
, "Failed to execute %s: %m", cmdline
[0]);
2169 ret
= wait_for_terminate_and_check(cmdline
[0], pid
, WAIT_LOG_ABNORMAL
);
2171 /* Close the fd that pings the home now. */
2172 acquired_fd
= safe_close(acquired_fd
);
2174 r
= sd_bus_message_new_method_call(
2177 "org.freedesktop.home1",
2178 "/org/freedesktop/home1",
2179 "org.freedesktop.home1.Manager",
2182 return bus_log_create_error(r
);
2184 r
= sd_bus_message_append(m
, "s", argv
[1]);
2186 return bus_log_create_error(r
);
2188 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2190 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_BUSY
))
2191 log_notice("Not deactivating home directory of %s, as it is still used.", argv
[1]);
2193 return log_error_errno(r
, "Failed to release user home: %s", bus_error_message(&error
, r
));
2199 static int lock_all_homes(int argc
, char *argv
[], void *userdata
) {
2200 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2201 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2202 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2205 r
= acquire_bus(&bus
);
2209 r
= sd_bus_message_new_method_call(
2212 "org.freedesktop.home1",
2213 "/org/freedesktop/home1",
2214 "org.freedesktop.home1.Manager",
2217 return bus_log_create_error(r
);
2219 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
2221 return log_error_errno(r
, "Failed to lock home: %s", bus_error_message(&error
, r
));
2226 static int drop_from_identity(const char *field
) {
2231 /* If we are called to update an identity record and drop some field, let's keep track of what to
2232 * remove from the old record */
2233 r
= strv_extend(&arg_identity_filter
, field
);
2237 /* Let's also drop the field if it was previously set to a new value on the same command line */
2238 r
= json_variant_filter(&arg_identity_extra
, STRV_MAKE(field
));
2240 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2242 r
= json_variant_filter(&arg_identity_extra_this_machine
, STRV_MAKE(field
));
2244 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2246 r
= json_variant_filter(&arg_identity_extra_privileged
, STRV_MAKE(field
));
2248 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2253 static int help(int argc
, char *argv
[], void *userdata
) {
2254 _cleanup_free_
char *link
= NULL
;
2257 (void) pager_open(arg_pager_flags
);
2259 r
= terminal_urlify_man("homectl", "1", &link
);
2263 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
2264 "%2$sCreate, manipulate or inspect home directories.%3$s\n"
2265 "\n%4$sCommands:%5$s\n"
2266 " list List homes\n"
2267 " activate USER… Activate home\n"
2268 " deactivate USER… Deactivate home\n"
2269 " inspect USER… Inspect home\n"
2270 " authenticate USER… Authenticate home\n"
2271 " create USER Create a home area\n"
2272 " remove USER… Remove a home area\n"
2273 " update USER Update a home area\n"
2274 " passwd USER Change password of a home area\n"
2275 " resize USER SIZE Resize a home area\n"
2276 " lock USER… Temporarily lock an active home\n"
2277 " unlock USER… Unlock a temporarily locked home\n"
2278 " lock-all Lock all suitable homes\n"
2279 " with USER [COMMAND…] Run shell or command with access to home\n"
2280 "\n%4$sOptions:%5$s\n"
2281 " -h --help Show this help\n"
2282 " --version Show package version\n"
2283 " --no-pager Do not pipe output into a pager\n"
2284 " --no-legend Do not show the headers and footers\n"
2285 " --no-ask-password Do not ask for system passwords\n"
2286 " -H --host=[USER@]HOST Operate on remote host\n"
2287 " -M --machine=CONTAINER Operate on local container\n"
2288 " --identity=PATH Read JSON identity from file\n"
2289 " --json=FORMAT Output inspection data in JSON (takes one of\n"
2290 " pretty, short, off)\n"
2291 " -j Equivalent to --json=pretty (on TTY) or\n"
2292 " --json=short (otherwise)\n"
2293 " --export-format= Strip JSON inspection data (full, stripped,\n"
2295 " -E When specified once equals -j --export-format=\n"
2296 " stripped, when specified twice equals\n"
2297 " -j --export-format=minimal\n"
2298 "\n%4$sGeneral User Record Properties:%5$s\n"
2299 " -c --real-name=REALNAME Real name for user\n"
2300 " --realm=REALM Realm to create user in\n"
2301 " --email-address=EMAIL Email address for user\n"
2302 " --location=LOCATION Set location of user on earth\n"
2303 " --icon-name=NAME Icon name for user\n"
2304 " -d --home-dir=PATH Home directory\n"
2305 " --uid=UID Numeric UID for user\n"
2306 " -G --member-of=GROUP Add user to group\n"
2307 " --skel=PATH Skeleton directory to use\n"
2308 " --shell=PATH Shell for account\n"
2309 " --setenv=VARIABLE=VALUE Set an environment variable at log-in\n"
2310 " --timezone=TIMEZONE Set a time-zone\n"
2311 " --language=LOCALE Set preferred language\n"
2312 " --ssh-authorized-keys=KEYS\n"
2313 " Specify SSH public keys\n"
2314 " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
2315 " private key and matching X.509 certificate\n"
2316 "\n%4$sAccount Management User Record Properties:%5$s\n"
2317 " --locked=BOOL Set locked account state\n"
2318 " --not-before=TIMESTAMP Do not allow logins before\n"
2319 " --not-after=TIMESTAMP Do not allow logins after\n"
2320 " --rate-limit-interval=SECS\n"
2321 " Login rate-limit interval in seconds\n"
2322 " --rate-limit-burst=NUMBER\n"
2323 " Login rate-limit attempts per interval\n"
2324 "\n%4$sPassword Policy User Record Properties:%5$s\n"
2325 " --password-hint=HINT Set Password hint\n"
2326 " --enforce-password-policy=BOOL\n"
2327 " Control whether to enforce system's password\n"
2328 " policy for this user\n"
2329 " -P Equivalent to --enforce-password-password=no\n"
2330 " --password-change-now=BOOL\n"
2331 " Require the password to be changed on next login\n"
2332 " --password-change-min=TIME\n"
2333 " Require minimum time between password changes\n"
2334 " --password-change-max=TIME\n"
2335 " Require maximum time between password changes\n"
2336 " --password-change-warn=TIME\n"
2337 " How much time to warn before password expiry\n"
2338 " --password-change-inactive=TIME\n"
2339 " How much time to block password after expiry\n"
2340 "\n%4$sResource Management User Record Properties:%5$s\n"
2341 " --disk-size=BYTES Size to assign the user on disk\n"
2342 " --access-mode=MODE User home directory access mode\n"
2343 " --umask=MODE Umask for user when logging in\n"
2344 " --nice=NICE Nice level for user\n"
2345 " --rlimit=LIMIT=VALUE[:VALUE]\n"
2346 " Set resource limits\n"
2347 " --tasks-max=MAX Set maximum number of per-user tasks\n"
2348 " --memory-high=BYTES Set high memory threshold in bytes\n"
2349 " --memory-max=BYTES Set maximum memory limit\n"
2350 " --cpu-weight=WEIGHT Set CPU weight\n"
2351 " --io-weight=WEIGHT Set IO weight\n"
2352 "\n%4$sStorage User Record Properties:%5$s\n"
2353 " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
2354 " subvolume, cifs)\n"
2355 " --image-path=PATH Path to image file/directory\n"
2356 "\n%4$sLUKS Storage User Record Properties:%5$s\n"
2357 " --fs-type=TYPE File system type to use in case of luks\n"
2358 " storage (ext4, xfs, btrfs)\n"
2359 " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
2360 " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
2361 " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
2362 " --luks-volume-key-size=BITS\n"
2363 " Volume key size to use for LUKS encryption\n"
2364 " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
2365 " --luks-pbkdf-hash-algorithm=ALGORITHM\n"
2366 " PBKDF hash algorithm to use\n"
2367 " --luks-pbkdf-time-cost=SECS\n"
2368 " Time cost for PBKDF in seconds\n"
2369 " --luks-pbkdf-memory-cost=BYTES\n"
2370 " Memory cost for PBKDF in bytes\n"
2371 " --luks-pbkdf-parallel-threads=NUMBER\n"
2372 " Number of parallel threads for PKBDF\n"
2373 "\n%4$sMounting User Record Properties:%5$s\n"
2374 " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
2375 " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
2376 " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
2377 "\n%4$sCIFS User Record Properties:%5$s\n"
2378 " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
2379 " --cifs-user-name=USER CIFS (Windows) user name\n"
2380 " --cifs-service=SERVICE CIFS (Windows) service to mount as home\n"
2381 "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
2382 " --stop-delay=SECS How long to leave user services running after\n"
2384 " --kill-processes=BOOL Whether to kill user processes when sessions\n"
2386 " --auto-login=BOOL Try to log this user in automatically\n"
2387 "\nSee the %6$s for details.\n"
2388 , program_invocation_short_name
2389 , ansi_highlight(), ansi_normal()
2390 , ansi_underline(), ansi_normal()
2397 static int parse_argv(int argc
, char *argv
[]) {
2400 ARG_VERSION
= 0x100,
2403 ARG_NO_ASK_PASSWORD
,
2418 ARG_SSH_AUTHORIZED_KEYS
,
2427 ARG_LUKS_CIPHER_MODE
,
2428 ARG_LUKS_VOLUME_KEY_SIZE
,
2440 ARG_LUKS_PBKDF_TYPE
,
2441 ARG_LUKS_PBKDF_HASH_ALGORITHM
,
2442 ARG_LUKS_PBKDF_TIME_COST
,
2443 ARG_LUKS_PBKDF_MEMORY_COST
,
2444 ARG_LUKS_PBKDF_PARALLEL_THREADS
,
2445 ARG_RATE_LIMIT_INTERVAL
,
2446 ARG_RATE_LIMIT_BURST
,
2449 ARG_ENFORCE_PASSWORD_POLICY
,
2450 ARG_PASSWORD_CHANGE_NOW
,
2451 ARG_PASSWORD_CHANGE_MIN
,
2452 ARG_PASSWORD_CHANGE_MAX
,
2453 ARG_PASSWORD_CHANGE_WARN
,
2454 ARG_PASSWORD_CHANGE_INACTIVE
,
2457 ARG_PKCS11_TOKEN_URI
,
2459 ARG_AND_CHANGE_PASSWORD
,
2462 static const struct option options
[] = {
2463 { "help", no_argument
, NULL
, 'h' },
2464 { "version", no_argument
, NULL
, ARG_VERSION
},
2465 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2466 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2467 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2468 { "host", required_argument
, NULL
, 'H' },
2469 { "machine", required_argument
, NULL
, 'M' },
2470 { "identity", required_argument
, NULL
, 'I' },
2471 { "real-name", required_argument
, NULL
, 'c' },
2472 { "comment", required_argument
, NULL
, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
2473 { "realm", required_argument
, NULL
, ARG_REALM
},
2474 { "email-address", required_argument
, NULL
, ARG_EMAIL_ADDRESS
},
2475 { "location", required_argument
, NULL
, ARG_LOCATION
},
2476 { "password-hint", required_argument
, NULL
, ARG_PASSWORD_HINT
},
2477 { "icon-name", required_argument
, NULL
, ARG_ICON_NAME
},
2478 { "home-dir", required_argument
, NULL
, 'd' }, /* Compatible with useradd(8) */
2479 { "uid", required_argument
, NULL
, 'u' }, /* Compatible with useradd(8) */
2480 { "member-of", required_argument
, NULL
, 'G' },
2481 { "groups", required_argument
, NULL
, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
2482 { "skel", required_argument
, NULL
, 'k' }, /* Compatible with useradd(8) */
2483 { "shell", required_argument
, NULL
, 's' }, /* Compatible with useradd(8) */
2484 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2485 { "timezone", required_argument
, NULL
, ARG_TIMEZONE
},
2486 { "language", required_argument
, NULL
, ARG_LANGUAGE
},
2487 { "locked", required_argument
, NULL
, ARG_LOCKED
},
2488 { "not-before", required_argument
, NULL
, ARG_NOT_BEFORE
},
2489 { "not-after", required_argument
, NULL
, ARG_NOT_AFTER
},
2490 { "expiredate", required_argument
, NULL
, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
2491 { "ssh-authorized-keys", required_argument
, NULL
, ARG_SSH_AUTHORIZED_KEYS
},
2492 { "disk-size", required_argument
, NULL
, ARG_DISK_SIZE
},
2493 { "access-mode", required_argument
, NULL
, ARG_ACCESS_MODE
},
2494 { "umask", required_argument
, NULL
, ARG_UMASK
},
2495 { "nice", required_argument
, NULL
, ARG_NICE
},
2496 { "rlimit", required_argument
, NULL
, ARG_RLIMIT
},
2497 { "tasks-max", required_argument
, NULL
, ARG_TASKS_MAX
},
2498 { "memory-high", required_argument
, NULL
, ARG_MEMORY_HIGH
},
2499 { "memory-max", required_argument
, NULL
, ARG_MEMORY_MAX
},
2500 { "cpu-weight", required_argument
, NULL
, ARG_CPU_WEIGHT
},
2501 { "io-weight", required_argument
, NULL
, ARG_IO_WEIGHT
},
2502 { "storage", required_argument
, NULL
, ARG_STORAGE
},
2503 { "image-path", required_argument
, NULL
, ARG_IMAGE_PATH
},
2504 { "fs-type", required_argument
, NULL
, ARG_FS_TYPE
},
2505 { "luks-discard", required_argument
, NULL
, ARG_LUKS_DISCARD
},
2506 { "luks-cipher", required_argument
, NULL
, ARG_LUKS_CIPHER
},
2507 { "luks-cipher-mode", required_argument
, NULL
, ARG_LUKS_CIPHER_MODE
},
2508 { "luks-volume-key-size", required_argument
, NULL
, ARG_LUKS_VOLUME_KEY_SIZE
},
2509 { "luks-pbkdf-type", required_argument
, NULL
, ARG_LUKS_PBKDF_TYPE
},
2510 { "luks-pbkdf-hash-algorithm", required_argument
, NULL
, ARG_LUKS_PBKDF_HASH_ALGORITHM
},
2511 { "luks-pbkdf-time-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_TIME_COST
},
2512 { "luks-pbkdf-memory-cost", required_argument
, NULL
, ARG_LUKS_PBKDF_MEMORY_COST
},
2513 { "luks-pbkdf-parallel-threads", required_argument
, NULL
, ARG_LUKS_PBKDF_PARALLEL_THREADS
},
2514 { "nosuid", required_argument
, NULL
, ARG_NOSUID
},
2515 { "nodev", required_argument
, NULL
, ARG_NODEV
},
2516 { "noexec", required_argument
, NULL
, ARG_NOEXEC
},
2517 { "cifs-user-name", required_argument
, NULL
, ARG_CIFS_USER_NAME
},
2518 { "cifs-domain", required_argument
, NULL
, ARG_CIFS_DOMAIN
},
2519 { "cifs-service", required_argument
, NULL
, ARG_CIFS_SERVICE
},
2520 { "rate-limit-interval", required_argument
, NULL
, ARG_RATE_LIMIT_INTERVAL
},
2521 { "rate-limit-burst", required_argument
, NULL
, ARG_RATE_LIMIT_BURST
},
2522 { "stop-delay", required_argument
, NULL
, ARG_STOP_DELAY
},
2523 { "kill-processes", required_argument
, NULL
, ARG_KILL_PROCESSES
},
2524 { "enforce-password-policy", required_argument
, NULL
, ARG_ENFORCE_PASSWORD_POLICY
},
2525 { "password-change-now", required_argument
, NULL
, ARG_PASSWORD_CHANGE_NOW
},
2526 { "password-change-min", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MIN
},
2527 { "password-change-max", required_argument
, NULL
, ARG_PASSWORD_CHANGE_MAX
},
2528 { "password-change-warn", required_argument
, NULL
, ARG_PASSWORD_CHANGE_WARN
},
2529 { "password-change-inactive", required_argument
, NULL
, ARG_PASSWORD_CHANGE_INACTIVE
},
2530 { "auto-login", required_argument
, NULL
, ARG_AUTO_LOGIN
},
2531 { "json", required_argument
, NULL
, ARG_JSON
},
2532 { "export-format", required_argument
, NULL
, ARG_EXPORT_FORMAT
},
2533 { "pkcs11-token-uri", required_argument
, NULL
, ARG_PKCS11_TOKEN_URI
},
2534 { "and-resize", required_argument
, NULL
, ARG_AND_RESIZE
},
2535 { "and-change-password", required_argument
, NULL
, ARG_AND_CHANGE_PASSWORD
},
2547 c
= getopt_long(argc
, argv
, "hH:M:I:c:d:u:k:s:e:G:jPE", options
, NULL
);
2554 return help(0, NULL
, NULL
);
2560 arg_pager_flags
|= PAGER_DISABLE
;
2567 case ARG_NO_ASK_PASSWORD
:
2568 arg_ask_password
= false;
2572 arg_transport
= BUS_TRANSPORT_REMOTE
;
2577 arg_transport
= BUS_TRANSPORT_MACHINE
;
2582 arg_identity
= optarg
;
2586 if (isempty(optarg
)) {
2587 r
= drop_from_identity("realName");
2594 if (!valid_gecos(optarg
))
2595 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Real name '%s' not a valid GECOS field.", optarg
);
2597 r
= json_variant_set_field_string(&arg_identity_extra
, "realName", optarg
);
2599 return log_error_errno(r
, "Failed to set realName field: %m");
2604 _cleanup_free_
char *hd
= NULL
;
2606 if (isempty(optarg
)) {
2607 r
= drop_from_identity("homeDirectory");
2614 r
= parse_path_argument_and_warn(optarg
, false, &hd
);
2618 if (!valid_home(hd
))
2619 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Home directory '%s' not valid.", hd
);
2621 r
= json_variant_set_field_string(&arg_identity_extra
, "homeDirectory", hd
);
2623 return log_error_errno(r
, "Failed to set homeDirectory field: %m");
2629 if (isempty(optarg
)) {
2630 r
= drop_from_identity("realm");
2637 r
= dns_name_is_valid(optarg
);
2639 return log_error_errno(r
, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg
);
2641 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Realm '%s' is not a valid DNS domain: %m", optarg
);
2643 r
= json_variant_set_field_string(&arg_identity_extra
, "realm", optarg
);
2645 return log_error_errno(r
, "Failed to set realm field: %m");
2648 case ARG_EMAIL_ADDRESS
:
2651 case ARG_CIFS_USER_NAME
:
2652 case ARG_CIFS_DOMAIN
:
2653 case ARG_CIFS_SERVICE
: {
2656 c
== ARG_EMAIL_ADDRESS
? "emailAddress" :
2657 c
== ARG_LOCATION
? "location" :
2658 c
== ARG_ICON_NAME
? "iconName" :
2659 c
== ARG_CIFS_USER_NAME
? "cifsUserName" :
2660 c
== ARG_CIFS_DOMAIN
? "cifsDomain" :
2661 c
== ARG_CIFS_SERVICE
? "cifsService" :
2666 if (isempty(optarg
)) {
2667 r
= drop_from_identity(field
);
2674 r
= json_variant_set_field_string(&arg_identity_extra
, field
, optarg
);
2676 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2681 case ARG_PASSWORD_HINT
:
2682 if (isempty(optarg
)) {
2683 r
= drop_from_identity("passwordHint");
2690 r
= json_variant_set_field_string(&arg_identity_extra_privileged
, "passwordHint", optarg
);
2692 return log_error_errno(r
, "Failed to set passwordHint field: %m");
2694 string_erase(optarg
);
2700 if (isempty(optarg
)) {
2701 r
= drop_from_identity("niceLevel");
2707 r
= parse_nice(optarg
, &nc
);
2709 return log_error_errno(r
, "Failed to parse nice level: %s", optarg
);
2711 r
= json_variant_set_field_integer(&arg_identity_extra
, "niceLevel", nc
);
2713 return log_error_errno(r
, "Failed to set niceLevel field: %m");
2719 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *jcur
= NULL
, *jmax
= NULL
;
2720 _cleanup_free_
char *field
= NULL
, *t
= NULL
;
2725 if (isempty(optarg
)) {
2726 /* Remove all resource limits */
2728 r
= drop_from_identity("resourceLimits");
2732 arg_identity_filter_rlimits
= strv_free(arg_identity_filter_rlimits
);
2733 arg_identity_extra_rlimits
= json_variant_unref(arg_identity_extra_rlimits
);
2737 eq
= strchr(optarg
, '=');
2739 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Can't parse resource limit assignment: %s", optarg
);
2741 field
= strndup(optarg
, eq
- optarg
);
2745 l
= rlimit_from_string_harder(field
);
2747 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unknown resource limit type: %s", field
);
2749 if (isempty(eq
+ 1)) {
2750 /* Remove only the specific rlimit */
2752 r
= strv_extend(&arg_identity_filter_rlimits
, rlimit_to_string(l
));
2756 r
= json_variant_filter(&arg_identity_extra_rlimits
, STRV_MAKE(field
));
2758 return log_error_errno(r
, "Failed to filter JSON identity data: %m");
2763 r
= rlimit_parse(l
, eq
+ 1, &rl
);
2765 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to parse resource limit value: %s", eq
+ 1);
2767 r
= rl
.rlim_cur
== RLIM_INFINITY
? json_variant_new_null(&jcur
) : json_variant_new_unsigned(&jcur
, rl
.rlim_cur
);
2769 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to allocate current integer: %m");
2771 r
= rl
.rlim_max
== RLIM_INFINITY
? json_variant_new_null(&jmax
) : json_variant_new_unsigned(&jmax
, rl
.rlim_max
);
2773 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to allocate maximum integer: %m");
2777 JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur
)),
2778 JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax
))));
2780 return log_error_errno(r
, "Failed to build resource limit: %m");
2782 t
= strjoin("RLIMIT_", rlimit_to_string(l
));
2786 r
= json_variant_set_field(&arg_identity_extra_rlimits
, t
, v
);
2788 return log_error_errno(r
, "Failed to set %s field: %m", rlimit_to_string(l
));
2796 if (isempty(optarg
)) {
2797 r
= drop_from_identity("uid");
2804 r
= parse_uid(optarg
, &uid
);
2806 return log_error_errno(r
, "Failed to parse UID '%s'.", optarg
);
2808 if (uid_is_system(uid
))
2809 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in system range, refusing.", uid
);
2810 if (uid_is_dynamic(uid
))
2811 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is in dynamic range, refusing.", uid
);
2812 if (uid
== UID_NOBODY
)
2813 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "UID " UID_FMT
" is nobody UID, refusing.", uid
);
2815 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "uid", uid
);
2817 return log_error_errno(r
, "Failed to set realm field: %m");
2823 case ARG_IMAGE_PATH
: {
2824 const char *field
= c
== 'k' ? "skeletonDirectory" : "imagePath";
2825 _cleanup_free_
char *v
= NULL
;
2827 if (isempty(optarg
)) {
2828 r
= drop_from_identity(field
);
2835 r
= parse_path_argument_and_warn(optarg
, false, &v
);
2839 r
= json_variant_set_field_string(&arg_identity_extra_this_machine
, field
, v
);
2841 return log_error_errno(r
, "Failed to set %s field: %m", v
);
2847 if (isempty(optarg
)) {
2848 r
= drop_from_identity("shell");
2855 if (!valid_shell(optarg
))
2856 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Shell '%s' not valid.", optarg
);
2858 r
= json_variant_set_field_string(&arg_identity_extra
, "shell", optarg
);
2860 return log_error_errno(r
, "Failed to set shell field: %m");
2865 _cleanup_free_
char **l
= NULL
, **k
= NULL
;
2866 _cleanup_(json_variant_unrefp
) JsonVariant
*ne
= NULL
;
2869 if (isempty(optarg
)) {
2870 r
= drop_from_identity("environment");
2877 if (!env_assignment_is_valid(optarg
))
2878 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Environment assignment '%s' not valid.", optarg
);
2880 e
= json_variant_by_key(arg_identity_extra
, "environment");
2882 r
= json_variant_strv(e
, &l
);
2884 return log_error_errno(r
, "Failed to parse JSON environment field: %m");
2887 k
= strv_env_set(l
, optarg
);
2893 r
= json_variant_new_array_strv(&ne
, k
);
2895 return log_error_errno(r
, "Failed to allocate environment list JSON: %m");
2897 r
= json_variant_set_field(&arg_identity_extra
, "environment", ne
);
2899 return log_error_errno(r
, "Failed to set environent list: %m");
2906 if (isempty(optarg
)) {
2907 r
= drop_from_identity("timeZone");
2914 if (!timezone_is_valid(optarg
, LOG_DEBUG
))
2915 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Timezone '%s' is not valid.", optarg
);
2917 r
= json_variant_set_field_string(&arg_identity_extra
, "timeZone", optarg
);
2919 return log_error_errno(r
, "Failed to set timezone field: %m");
2924 if (isempty(optarg
)) {
2925 r
= drop_from_identity("language");
2932 if (!locale_is_valid(optarg
))
2933 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Locale '%s' is not valid.", optarg
);
2935 r
= json_variant_set_field_string(&arg_identity_extra
, "preferredLanguage", optarg
);
2937 return log_error_errno(r
, "Failed to set preferredLanguage field: %m");
2945 case ARG_KILL_PROCESSES
:
2946 case ARG_ENFORCE_PASSWORD_POLICY
:
2947 case ARG_AUTO_LOGIN
:
2948 case ARG_PASSWORD_CHANGE_NOW
: {
2950 c
== ARG_LOCKED
? "locked" :
2951 c
== ARG_NOSUID
? "mountNoSuid" :
2952 c
== ARG_NODEV
? "mountNoDevices" :
2953 c
== ARG_NOEXEC
? "mountNoExecute" :
2954 c
== ARG_KILL_PROCESSES
? "killProcesses" :
2955 c
== ARG_ENFORCE_PASSWORD_POLICY
? "enforcePasswordPolicy" :
2956 c
== ARG_AUTO_LOGIN
? "autoLogin" :
2957 c
== ARG_PASSWORD_CHANGE_NOW
? "passwordChangeNow" :
2962 if (isempty(optarg
)) {
2963 r
= drop_from_identity(field
);
2970 r
= parse_boolean(optarg
);
2972 return log_error_errno(r
, "Failed to parse %s boolean: %m", field
);
2974 r
= json_variant_set_field_boolean(&arg_identity_extra
, field
, r
> 0);
2976 return log_error_errno(r
, "Failed to set %s field: %m", field
);
2982 r
= json_variant_set_field_boolean(&arg_identity_extra
, "enforcePasswordPolicy", false);
2984 return log_error_errno(r
, "Failed to set enforcePasswordPolicy field: %m");
2989 if (isempty(optarg
)) {
2990 r
= drop_from_identity("diskSize");
2994 r
= drop_from_identity("diskSizeRelative");
2998 arg_disk_size
= arg_disk_size_relative
= UINT64_MAX
;
3002 r
= parse_permille(optarg
);
3004 r
= parse_size(optarg
, 1024, &arg_disk_size
);
3006 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Disk size '%s' not valid.", optarg
);
3008 r
= drop_from_identity("diskSizeRelative");
3012 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "diskSize", arg_disk_size
);
3014 return log_error_errno(r
, "Failed to set diskSize field: %m");
3016 arg_disk_size_relative
= UINT64_MAX
;
3018 /* Normalize to UINT32_MAX == 100% */
3019 arg_disk_size_relative
= (uint64_t) r
* UINT32_MAX
/ 1000U;
3021 r
= drop_from_identity("diskSize");
3025 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, "diskSizeRelative", arg_disk_size_relative
);
3027 return log_error_errno(r
, "Failed to set diskSizeRelative field: %m");
3029 arg_disk_size
= UINT64_MAX
;
3034 case ARG_ACCESS_MODE
: {
3037 if (isempty(optarg
)) {
3038 r
= drop_from_identity("accessMode");
3045 r
= parse_mode(optarg
, &mode
);
3047 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Access mode '%s' not valid.", optarg
);
3049 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "accessMode", mode
);
3051 return log_error_errno(r
, "Failed to set access mode field: %m");
3056 case ARG_LUKS_DISCARD
:
3057 if (isempty(optarg
)) {
3058 r
= drop_from_identity("luksDiscard");
3065 r
= parse_boolean(optarg
);
3067 return log_error_errno(r
, "Failed to parse --luks-discard= parameter: %s", optarg
);
3069 r
= json_variant_set_field_boolean(&arg_identity_extra
, "luksDiscard", r
);
3071 return log_error_errno(r
, "Failed to set discard field: %m");
3075 case ARG_LUKS_VOLUME_KEY_SIZE
:
3076 case ARG_LUKS_PBKDF_PARALLEL_THREADS
:
3077 case ARG_RATE_LIMIT_BURST
: {
3079 c
== ARG_LUKS_VOLUME_KEY_SIZE
? "luksVolumeKeySize" :
3080 c
== ARG_LUKS_PBKDF_PARALLEL_THREADS
? "luksPbkdfParallelThreads" :
3081 c
== ARG_RATE_LIMIT_BURST
? "rateLimitBurst" : NULL
;
3086 if (isempty(optarg
)) {
3087 r
= drop_from_identity(field
);
3092 r
= safe_atou(optarg
, &n
);
3094 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
3096 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3098 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3106 if (isempty(optarg
)) {
3107 r
= drop_from_identity("umask");
3114 r
= parse_mode(optarg
, &m
);
3116 return log_error_errno(r
, "Failed to parse umask: %m");
3118 r
= json_variant_set_field_integer(&arg_identity_extra
, "umask", m
);
3120 return log_error_errno(r
, "Failed to set umask field: %m");
3125 case ARG_SSH_AUTHORIZED_KEYS
: {
3126 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
3127 _cleanup_(strv_freep
) char **l
= NULL
, **add
= NULL
;
3129 if (isempty(optarg
)) {
3130 r
= drop_from_identity("sshAuthorizedKeys");
3137 if (optarg
[0] == '@') {
3138 _cleanup_fclose_
FILE *f
= NULL
;
3140 /* If prefixed with '@' read from a file */
3142 f
= fopen(optarg
+1, "re");
3144 return log_error_errno(errno
, "Failed to open '%s': %m", optarg
+1);
3147 _cleanup_free_
char *line
= NULL
;
3149 r
= read_line(f
, LONG_LINE_MAX
, &line
);
3151 return log_error_errno(r
, "Faile dto read from '%s': %m", optarg
+1);
3161 r
= strv_consume(&add
, TAKE_PTR(line
));
3166 /* Otherwise, assume it's a literal key. Let's do some superficial checks
3167 * before accept it though. */
3169 if (string_has_cc(optarg
, NULL
))
3170 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Authorized key contains control characters, refusing.");
3171 if (optarg
[0] == '#')
3172 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specified key is a comment?");
3174 add
= strv_new(optarg
);
3179 v
= json_variant_ref(json_variant_by_key(arg_identity_extra_privileged
, "sshAuthorizedKeys"));
3181 r
= json_variant_strv(v
, &l
);
3183 return log_error_errno(r
, "Failed to parse SSH authorized keys list: %m");
3186 r
= strv_extend_strv(&l
, add
, true);
3190 v
= json_variant_unref(v
);
3192 r
= json_variant_new_array_strv(&v
, l
);
3196 r
= json_variant_set_field(&arg_identity_extra_privileged
, "sshAuthorizedKeys", v
);
3198 return log_error_errno(r
, "Failed to set authorized keys: %m");
3203 case ARG_NOT_BEFORE
:
3209 field
= c
== ARG_NOT_BEFORE
? "notBeforeUSec" :
3210 IN_SET(c
, ARG_NOT_AFTER
, 'e') ? "notAfterUSec" : NULL
;
3214 if (isempty(optarg
)) {
3215 r
= drop_from_identity(field
);
3222 /* Note the minor discrepancy regarding -e parsing here: we support that for compat
3223 * reasons, and in the original useradd(8) implementation it accepts dates in the
3224 * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
3225 * with greater precision. */
3226 r
= parse_timestamp(optarg
, &n
);
3228 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
3230 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3232 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3236 case ARG_PASSWORD_CHANGE_MIN
:
3237 case ARG_PASSWORD_CHANGE_MAX
:
3238 case ARG_PASSWORD_CHANGE_WARN
:
3239 case ARG_PASSWORD_CHANGE_INACTIVE
: {
3243 field
= c
== ARG_PASSWORD_CHANGE_MIN
? "passwordChangeMinUSec" :
3244 c
== ARG_PASSWORD_CHANGE_MAX
? "passwordChangeMaxUSec" :
3245 c
== ARG_PASSWORD_CHANGE_WARN
? "passwordChangeWarnUSec" :
3246 c
== ARG_PASSWORD_CHANGE_INACTIVE
? "passwordChangeInactiveUSec" :
3251 if (isempty(optarg
)) {
3252 r
= drop_from_identity(field
);
3259 r
= parse_sec(optarg
, &n
);
3261 return log_error_errno(r
, "Failed to parse %s parameter: %m", field
);
3263 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, n
);
3265 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3271 case ARG_LUKS_CIPHER
:
3272 case ARG_LUKS_CIPHER_MODE
:
3273 case ARG_LUKS_PBKDF_TYPE
:
3274 case ARG_LUKS_PBKDF_HASH_ALGORITHM
: {
3277 c
== ARG_STORAGE
? "storage" :
3278 c
== ARG_FS_TYPE
? "fileSystemType" :
3279 c
== ARG_LUKS_CIPHER
? "luksCipher" :
3280 c
== ARG_LUKS_CIPHER_MODE
? "luksCipherMode" :
3281 c
== ARG_LUKS_PBKDF_TYPE
? "luksPbkdfType" :
3282 c
== ARG_LUKS_PBKDF_HASH_ALGORITHM
? "luksPbkdfHashAlgorithm" : NULL
;
3286 if (isempty(optarg
)) {
3287 r
= drop_from_identity(field
);
3294 if (!string_is_safe(optarg
))
3295 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Parameter for %s field not valid: %s", field
, optarg
);
3297 r
= json_variant_set_field_string(
3298 IN_SET(c
, ARG_STORAGE
, ARG_FS_TYPE
) ?
3299 &arg_identity_extra_this_machine
:
3300 &arg_identity_extra
, field
, optarg
);
3302 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3307 case ARG_LUKS_PBKDF_TIME_COST
:
3308 case ARG_RATE_LIMIT_INTERVAL
:
3309 case ARG_STOP_DELAY
: {
3311 c
== ARG_LUKS_PBKDF_TIME_COST
? "luksPbkdfTimeCostUSec" :
3312 c
== ARG_RATE_LIMIT_INTERVAL
? "rateLimitIntervalUSec" :
3313 c
== ARG_STOP_DELAY
? "stopDelayUSec" :
3319 if (isempty(optarg
)) {
3320 r
= drop_from_identity(field
);
3327 r
= parse_sec(optarg
, &t
);
3329 return log_error_errno(r
, "Failed to parse %s field: %s", field
, optarg
);
3331 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, t
);
3333 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3339 const char *p
= optarg
;
3342 r
= drop_from_identity("memberOf");
3350 _cleanup_(json_variant_unrefp
) JsonVariant
*mo
= NULL
;
3351 _cleanup_strv_free_
char **list
= NULL
;
3352 _cleanup_free_
char *word
= NULL
;
3354 r
= extract_first_word(&p
, &word
, ",", 0);
3356 return log_error_errno(r
, "Failed to parse group list: %m");
3360 if (!valid_user_group_name(word
))
3361 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid group name %s.", word
);
3363 mo
= json_variant_ref(json_variant_by_key(arg_identity_extra
, "memberOf"));
3365 r
= json_variant_strv(mo
, &list
);
3367 return log_error_errno(r
, "Failed to parse group list: %m");
3369 r
= strv_extend(&list
, word
);
3376 mo
= json_variant_unref(mo
);
3377 r
= json_variant_new_array_strv(&mo
, list
);
3379 return log_error_errno(r
, "Failed to create group list JSON: %m");
3381 r
= json_variant_set_field(&arg_identity_extra
, "memberOf", mo
);
3383 return log_error_errno(r
, "Failed to update group list: %m");
3389 case ARG_TASKS_MAX
: {
3392 if (isempty(optarg
)) {
3393 r
= drop_from_identity("tasksMax");
3399 r
= safe_atou64(optarg
, &u
);
3401 return log_error_errno(r
, "Failed to parse --tasks-max= parameter: %s", optarg
);
3403 r
= json_variant_set_field_unsigned(&arg_identity_extra
, "tasksMax", u
);
3405 return log_error_errno(r
, "Failed to set tasksMax field: %m");
3410 case ARG_MEMORY_MAX
:
3411 case ARG_MEMORY_HIGH
:
3412 case ARG_LUKS_PBKDF_MEMORY_COST
: {
3414 c
== ARG_MEMORY_MAX
? "memoryMax" :
3415 c
== ARG_MEMORY_HIGH
? "memoryHigh" :
3416 c
== ARG_LUKS_PBKDF_MEMORY_COST
? "luksPbkdfMemoryCost" : NULL
;
3422 if (isempty(optarg
)) {
3423 r
= drop_from_identity(field
);
3429 r
= parse_size(optarg
, 1024, &u
);
3431 return log_error_errno(r
, "Failed to parse %s parameter: %s", field
, optarg
);
3433 r
= json_variant_set_field_unsigned(&arg_identity_extra_this_machine
, field
, u
);
3435 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3440 case ARG_CPU_WEIGHT
:
3441 case ARG_IO_WEIGHT
: {
3442 const char *field
= c
== ARG_CPU_WEIGHT
? "cpuWeight" :
3443 c
== ARG_IO_WEIGHT
? "ioWeight" : NULL
;
3448 if (isempty(optarg
)) {
3449 r
= drop_from_identity(field
);
3455 r
= safe_atou64(optarg
, &u
);
3457 return log_error_errno(r
, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg
);
3459 if (!CGROUP_WEIGHT_IS_OK(u
))
3460 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Weight %" PRIu64
" is out of valid weight range.", u
);
3462 r
= json_variant_set_field_unsigned(&arg_identity_extra
, field
, u
);
3464 return log_error_errno(r
, "Failed to set %s field: %m", field
);
3469 case ARG_PKCS11_TOKEN_URI
: {
3472 /* If --pkcs11-token-uri= is specified we always drop everything old */
3473 FOREACH_STRING(p
, "pkcs11TokenUri", "pkcs11EncryptedKey") {
3474 r
= drop_from_identity(p
);
3479 if (isempty(optarg
)) {
3480 arg_pkcs11_token_uri
= strv_free(arg_pkcs11_token_uri
);
3484 if (!pkcs11_uri_valid(optarg
))
3485 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Not a valid PKCS#11 URI: %s", optarg
);
3487 r
= strv_extend(&arg_pkcs11_token_uri
, optarg
);
3491 strv_uniq(arg_pkcs11_token_uri
);
3497 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
3501 if (streq(optarg
, "pretty")) {
3503 arg_json_format_flags
= JSON_FORMAT_PRETTY
|JSON_FORMAT_COLOR_AUTO
;
3504 } else if (streq(optarg
, "short")) {
3506 arg_json_format_flags
= JSON_FORMAT_NEWLINE
;
3507 } else if (streq(optarg
, "off")) {
3509 arg_json_format_flags
= 0;
3510 } else if (streq(optarg
, "help")) {
3516 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unknown argument to --json=: %s", optarg
);
3521 if (arg_export_format
== EXPORT_FORMAT_FULL
)
3522 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
3523 else if (arg_export_format
== EXPORT_FORMAT_STRIPPED
)
3524 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
3526 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Specifying -E more than twice is not supported.");
3529 if (arg_json_format_flags
== 0)
3530 arg_json_format_flags
= JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
;
3533 case ARG_EXPORT_FORMAT
:
3534 if (streq(optarg
, "full"))
3535 arg_export_format
= EXPORT_FORMAT_FULL
;
3536 else if (streq(optarg
, "stripped"))
3537 arg_export_format
= EXPORT_FORMAT_STRIPPED
;
3538 else if (streq(optarg
, "minimal"))
3539 arg_export_format
= EXPORT_FORMAT_MINIMAL
;
3540 else if (streq(optarg
, "help")) {
3549 case ARG_AND_RESIZE
:
3550 arg_and_resize
= true;
3553 case ARG_AND_CHANGE_PASSWORD
:
3554 arg_and_change_password
= true;
3561 assert_not_reached("Unhandled option");
3565 if (!strv_isempty(arg_pkcs11_token_uri
))
3566 arg_and_change_password
= true;
3568 if (arg_disk_size
!= UINT64_MAX
|| arg_disk_size_relative
!= UINT64_MAX
)
3569 arg_and_resize
= true;
3574 static int run(int argc
, char *argv
[]) {
3575 static const Verb verbs
[] = {
3576 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
3577 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_homes
},
3578 { "activate", 2, VERB_ANY
, 0, activate_home
},
3579 { "deactivate", 2, VERB_ANY
, 0, deactivate_home
},
3580 { "inspect", VERB_ANY
, VERB_ANY
, 0, inspect_home
},
3581 { "authenticate", VERB_ANY
, VERB_ANY
, 0, authenticate_home
},
3582 { "create", VERB_ANY
, 2, 0, create_home
},
3583 { "remove", 2, VERB_ANY
, 0, remove_home
},
3584 { "update", VERB_ANY
, 2, 0, update_home
},
3585 { "passwd", VERB_ANY
, 2, 0, passwd_home
},
3586 { "resize", 2, 3, 0, resize_home
},
3587 { "lock", 2, VERB_ANY
, 0, lock_home
},
3588 { "unlock", 2, VERB_ANY
, 0, unlock_home
},
3589 { "with", 2, VERB_ANY
, 0, with_home
},
3590 { "lock-all", VERB_ANY
, 1, 0, lock_all_homes
},
3596 log_show_color(true);
3597 log_parse_environment();
3600 r
= parse_argv(argc
, argv
);
3604 return dispatch_verb(argc
, argv
, verbs
, NULL
);
3607 DEFINE_MAIN_FUNCTION(run
);