]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/home/homectl.c
homed: add a ActivateHomeIfReferenced() bus call
[thirdparty/systemd.git] / src / home / homectl.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
4aa0a8ac
LP
2
3#include <getopt.h>
4
5#include "sd-bus.h"
6
4aa0a8ac 7#include "ask-password-api.h"
d6b4d1c7 8#include "build.h"
4aa0a8ac
LP
9#include "bus-common-errors.h"
10#include "bus-error.h"
9b71e4ab 11#include "bus-locator.h"
fada2c75
LP
12#include "cap-list.h"
13#include "capability-util.h"
4aa0a8ac 14#include "cgroup-util.h"
3ccadbce 15#include "creds-util.h"
4aa0a8ac
LP
16#include "dns-domain.h"
17#include "env-util.h"
18#include "fd-util.h"
19#include "fileio.h"
20#include "format-table.h"
16b81da6 21#include "fs-util.h"
d8e32c47 22#include "glyph-util.h"
4aa0a8ac 23#include "home-util.h"
1c0c4a43 24#include "homectl-fido2.h"
93295a25 25#include "homectl-pkcs11.h"
80c41552 26#include "homectl-recovery-key.h"
fb2d839c 27#include "libfido2-util.h"
4aa0a8ac
LP
28#include "locale-util.h"
29#include "main-func.h"
30#include "memory-util.h"
4aa0a8ac 31#include "pager.h"
614b022c 32#include "parse-argument.h"
4aa0a8ac 33#include "parse-util.h"
d34b1823 34#include "password-quality-util.h"
4aa0a8ac 35#include "path-util.h"
ed5033fd 36#include "percent-util.h"
4aa0a8ac
LP
37#include "pkcs11-util.h"
38#include "pretty-print.h"
3ccadbce 39#include "proc-cmdline.h"
4aa0a8ac 40#include "process-util.h"
3ccadbce 41#include "recurse-dir.h"
4aa0a8ac
LP
42#include "rlimit-util.h"
43#include "spawn-polkit-agent.h"
44#include "terminal-util.h"
8e1ac16b 45#include "uid-classification.h"
fada2c75 46#include "user-record.h"
d34b1823 47#include "user-record-password-quality.h"
4aa0a8ac
LP
48#include "user-record-show.h"
49#include "user-record-util.h"
4aa0a8ac 50#include "user-util.h"
3ccadbce 51#include "userdb.h"
4aa0a8ac
LP
52#include "verbs.h"
53
54static PagerFlags arg_pager_flags = 0;
55static bool arg_legend = true;
56static bool arg_ask_password = true;
57static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
58static const char *arg_host = NULL;
59static const char *arg_identity = NULL;
60static JsonVariant *arg_identity_extra = NULL;
61static JsonVariant *arg_identity_extra_privileged = NULL;
62static JsonVariant *arg_identity_extra_this_machine = NULL;
63static JsonVariant *arg_identity_extra_rlimits = NULL;
64static char **arg_identity_filter = NULL; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
65static char **arg_identity_filter_rlimits = NULL;
66static uint64_t arg_disk_size = UINT64_MAX;
67static uint64_t arg_disk_size_relative = UINT64_MAX;
68static char **arg_pkcs11_token_uri = NULL;
1c0c4a43 69static char **arg_fido2_device = NULL;
17e7561a 70static Fido2EnrollFlags arg_fido2_lock_with = FIDO2ENROLL_PIN | FIDO2ENROLL_UP;
70e723c0
M
71#if HAVE_LIBFIDO2
72static int arg_fido2_cred_alg = COSE_ES256;
73#else
74static int arg_fido2_cred_alg = 0;
75#endif
80c41552 76static bool arg_recovery_key = false;
6a01ea4a 77static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
4aa0a8ac
LP
78static bool arg_and_resize = false;
79static bool arg_and_change_password = false;
80static enum {
81 EXPORT_FORMAT_FULL, /* export the full record */
82 EXPORT_FORMAT_STRIPPED, /* strip "state" + "binding", but leave signature in place */
83 EXPORT_FORMAT_MINIMAL, /* also strip signature */
84} arg_export_format = EXPORT_FORMAT_FULL;
fada2c75
LP
85static uint64_t arg_capability_bounding_set = UINT64_MAX;
86static uint64_t arg_capability_ambient_set = UINT64_MAX;
3ccadbce 87static bool arg_prompt_new_user = false;
4aa0a8ac
LP
88
89STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, json_variant_unrefp);
90STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, json_variant_unrefp);
91STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged, json_variant_unrefp);
92STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits, json_variant_unrefp);
93STATIC_DESTRUCTOR_REGISTER(arg_identity_filter, strv_freep);
94STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits, strv_freep);
95STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, strv_freep);
1c0c4a43 96STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, strv_freep);
4aa0a8ac 97
cc9886bc
LP
98static const BusLocator *bus_mgr;
99
4aa0a8ac
LP
100static bool identity_properties_specified(void) {
101 return
102 arg_identity ||
103 !json_variant_is_blank_object(arg_identity_extra) ||
104 !json_variant_is_blank_object(arg_identity_extra_privileged) ||
105 !json_variant_is_blank_object(arg_identity_extra_this_machine) ||
106 !json_variant_is_blank_object(arg_identity_extra_rlimits) ||
107 !strv_isempty(arg_identity_filter) ||
108 !strv_isempty(arg_identity_filter_rlimits) ||
1c0c4a43
LP
109 !strv_isempty(arg_pkcs11_token_uri) ||
110 !strv_isempty(arg_fido2_device);
4aa0a8ac
LP
111}
112
113static int acquire_bus(sd_bus **bus) {
114 int r;
115
116 assert(bus);
117
118 if (*bus)
119 return 0;
120
4870133b 121 r = bus_connect_transport(arg_transport, arg_host, RUNTIME_SCOPE_SYSTEM, bus);
4aa0a8ac 122 if (r < 0)
10a7340a 123 return bus_log_connect_error(r, arg_transport);
4aa0a8ac
LP
124
125 (void) sd_bus_set_allow_interactive_authorization(*bus, arg_ask_password);
126
127 return 0;
128}
129
130static int list_homes(int argc, char *argv[], void *userdata) {
131 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
132 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
133 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
134 _cleanup_(table_unrefp) Table *table = NULL;
135 int r;
136
4aa0a8ac
LP
137 r = acquire_bus(&bus);
138 if (r < 0)
139 return r;
140
cc9886bc 141 r = bus_call_method(bus, bus_mgr, "ListHomes", &error, &reply, NULL);
4aa0a8ac
LP
142 if (r < 0)
143 return log_error_errno(r, "Failed to list homes: %s", bus_error_message(&error, r));
144
145 table = table_new("name", "uid", "gid", "state", "realname", "home", "shell");
146 if (!table)
147 return log_oom();
148
149 r = sd_bus_message_enter_container(reply, 'a', "(susussso)");
150 if (r < 0)
151 return bus_log_parse_error(r);
152
153 for (;;) {
154 const char *name, *state, *realname, *home, *shell, *color;
155 TableCell *cell;
156 uint32_t uid, gid;
157
158 r = sd_bus_message_read(reply, "(susussso)", &name, &uid, &state, &gid, &realname, &home, &shell, NULL);
159 if (r < 0)
160 return bus_log_parse_error(r);
161 if (r == 0)
162 break;
163
164 r = table_add_many(table,
165 TABLE_STRING, name,
166 TABLE_UID, uid,
167 TABLE_GID, gid);
168 if (r < 0)
f987a261 169 return table_log_add_error(r);
4aa0a8ac
LP
170
171
172 r = table_add_cell(table, &cell, TABLE_STRING, state);
173 if (r < 0)
f987a261 174 return table_log_add_error(r);
4aa0a8ac
LP
175
176 color = user_record_state_color(state);
177 if (color)
178 (void) table_set_color(table, cell, color);
179
180 r = table_add_many(table,
181 TABLE_STRING, strna(empty_to_null(realname)),
182 TABLE_STRING, home,
183 TABLE_STRING, strna(empty_to_null(shell)));
184 if (r < 0)
f987a261 185 return table_log_add_error(r);
4aa0a8ac
LP
186 }
187
188 r = sd_bus_message_exit_container(reply);
189 if (r < 0)
190 return bus_log_parse_error(r);
191
2413a0fa 192 if (!table_isempty(table) || !FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
ef1e0b9a 193 r = table_set_sort(table, (size_t) 0);
4aa0a8ac 194 if (r < 0)
df83eb54 195 return table_log_sort_error(r);
4aa0a8ac 196
665ffc7f 197 r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
4aa0a8ac 198 if (r < 0)
665ffc7f 199 return r;
4aa0a8ac
LP
200 }
201
2413a0fa
MY
202 if (arg_legend && !FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
203 if (table_isempty(table))
c11428ad 204 printf("No home areas.\n");
2413a0fa
MY
205 else
206 printf("\n%zu home areas listed.\n", table_get_rows(table) - 1);
4aa0a8ac
LP
207 }
208
209 return 0;
210}
211
ea086f06
LP
212static int acquire_existing_password(
213 const char *user_name,
214 UserRecord *hr,
215 bool emphasize_current,
216 AskPasswordFlags flags) {
217
5d2a48da 218 _cleanup_strv_free_erase_ char **password = NULL;
e99ca147 219 _cleanup_(erase_and_freep) char *envpw = NULL;
4aa0a8ac 220 _cleanup_free_ char *question = NULL;
4aa0a8ac
LP
221 int r;
222
223 assert(user_name);
224 assert(hr);
225
e99ca147
LP
226 r = getenv_steal_erase("PASSWORD", &envpw);
227 if (r < 0)
228 return log_error_errno(r, "Failed to acquire password from environment: %m");
229 if (r > 0) {
4aa0a8ac
LP
230 /* People really shouldn't use environment variables for passing passwords. We support this
231 * only for testing purposes, and do not document the behaviour, so that people won't
232 * actually use this outside of testing. */
233
e99ca147 234 r = user_record_set_password(hr, STRV_MAKE(envpw), true);
4aa0a8ac
LP
235 if (r < 0)
236 return log_error_errno(r, "Failed to store password: %m");
237
ea086f06 238 return 1;
4aa0a8ac
LP
239 }
240
7bdbafc2
LP
241 /* If this is not our own user, then don't use the password cache */
242 if (is_this_me(user_name) <= 0)
243 SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, false);
244
4aa0a8ac
LP
245 if (asprintf(&question, emphasize_current ?
246 "Please enter current password for user %s:" :
247 "Please enter password for user %s:",
248 user_name) < 0)
249 return log_oom();
250
ea086f06
LP
251 r = ask_password_auto(question,
252 /* icon= */ "user-home",
253 NULL,
254 /* key_name= */ "home-password",
255 /* credential_name= */ "home.password",
256 USEC_INFINITY,
257 flags,
258 &password);
57bb9bcb
LP
259 if (r == -EUNATCH) { /* EUNATCH is returned if no password was found and asking interactively was
260 * disabled via the flags. Not an error for us. */
261 log_debug_errno(r, "No passwords acquired.");
262 return 0;
263 }
4aa0a8ac
LP
264 if (r < 0)
265 return log_error_errno(r, "Failed to acquire password: %m");
266
267 r = user_record_set_password(hr, password, true);
268 if (r < 0)
269 return log_error_errno(r, "Failed to store password: %m");
270
ea086f06 271 return 1;
4aa0a8ac
LP
272}
273
c7b6051f
LP
274static int acquire_recovery_key(
275 const char *user_name,
276 UserRecord *hr,
277 AskPasswordFlags flags) {
278
5d2a48da 279 _cleanup_strv_free_erase_ char **recovery_key = NULL;
e99ca147 280 _cleanup_(erase_and_freep) char *envpw = NULL;
c7b6051f 281 _cleanup_free_ char *question = NULL;
c7b6051f
LP
282 int r;
283
284 assert(user_name);
285 assert(hr);
286
e99ca147
LP
287 r = getenv_steal_erase("PASSWORD", &envpw);
288 if (r < 0)
289 return log_error_errno(r, "Failed to acquire password from environment: %m");
290 if (r > 0) {
c7b6051f
LP
291 /* People really shouldn't use environment variables for passing secrets. We support this
292 * only for testing purposes, and do not document the behaviour, so that people won't
293 * actually use this outside of testing. */
294
e99ca147 295 r = user_record_set_password(hr, STRV_MAKE(envpw), true); /* recovery keys are stored in the record exactly like regular passwords! */
c7b6051f
LP
296 if (r < 0)
297 return log_error_errno(r, "Failed to store recovery key: %m");
298
c7b6051f
LP
299 return 1;
300 }
301
302 /* If this is not our own user, then don't use the password cache */
303 if (is_this_me(user_name) <= 0)
304 SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, false);
305
306 if (asprintf(&question, "Please enter recovery key for user %s:", user_name) < 0)
307 return log_oom();
308
309 r = ask_password_auto(question,
310 /* icon= */ "user-home",
311 NULL,
312 /* key_name= */ "home-recovery-key",
313 /* credential_name= */ "home.recovery-key",
314 USEC_INFINITY,
315 flags,
316 &recovery_key);
317 if (r == -EUNATCH) { /* EUNATCH is returned if no recovery key was found and asking interactively was
318 * disabled via the flags. Not an error for us. */
319 log_debug_errno(r, "No recovery keys acquired.");
320 return 0;
321 }
322 if (r < 0)
323 return log_error_errno(r, "Failed to acquire recovery keys: %m");
324
325 r = user_record_set_password(hr, recovery_key, true);
326 if (r < 0)
327 return log_error_errno(r, "Failed to store recovery keys: %m");
328
329 return 1;
330}
331
ea086f06
LP
332static int acquire_token_pin(
333 const char *user_name,
334 UserRecord *hr,
335 AskPasswordFlags flags) {
336
5d2a48da 337 _cleanup_strv_free_erase_ char **pin = NULL;
e99ca147 338 _cleanup_(erase_and_freep) char *envpin = NULL;
4aa0a8ac 339 _cleanup_free_ char *question = NULL;
4aa0a8ac
LP
340 int r;
341
342 assert(user_name);
343 assert(hr);
344
e99ca147
LP
345 r = getenv_steal_erase("PIN", &envpin);
346 if (r < 0)
347 return log_error_errno(r, "Failed to acquire PIN from environment: %m");
348 if (r > 0) {
349 r = user_record_set_token_pin(hr, STRV_MAKE(envpin), false);
4aa0a8ac 350 if (r < 0)
c0bde0d2 351 return log_error_errno(r, "Failed to store token PIN: %m");
4aa0a8ac 352
ea086f06 353 return 1;
4aa0a8ac
LP
354 }
355
7bdbafc2
LP
356 /* If this is not our own user, then don't use the password cache */
357 if (is_this_me(user_name) <= 0)
358 SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, false);
359
4aa0a8ac
LP
360 if (asprintf(&question, "Please enter security token PIN for user %s:", user_name) < 0)
361 return log_oom();
362
ea086f06
LP
363 r = ask_password_auto(
364 question,
365 /* icon= */ "user-home",
366 NULL,
367 /* key_name= */ "token-pin",
368 /* credential_name= */ "home.token-pin",
369 USEC_INFINITY,
370 flags,
371 &pin);
57bb9bcb
LP
372 if (r == -EUNATCH) { /* EUNATCH is returned if no PIN was found and asking interactively was disabled
373 * via the flags. Not an error for us. */
374 log_debug_errno(r, "No security token PINs acquired.");
375 return 0;
376 }
4aa0a8ac
LP
377 if (r < 0)
378 return log_error_errno(r, "Failed to acquire security token PIN: %m");
379
c0bde0d2 380 r = user_record_set_token_pin(hr, pin, false);
4aa0a8ac
LP
381 if (r < 0)
382 return log_error_errno(r, "Failed to store security token PIN: %m");
383
ea086f06 384 return 1;
4aa0a8ac
LP
385}
386
387static int handle_generic_user_record_error(
388 const char *user_name,
389 UserRecord *hr,
390 const sd_bus_error *error,
391 int ret,
392 bool emphasize_current_password) {
393 int r;
394
395 assert(user_name);
396 assert(hr);
397
398 if (sd_bus_error_has_name(error, BUS_ERROR_HOME_ABSENT))
399 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE),
36e4a8f2 400 "Home of user %s is currently absent, please plug in the necessary storage device or backing file system.", user_name);
4aa0a8ac
LP
401
402 else if (sd_bus_error_has_name(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT))
403 return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS),
367649ee 404 "Too frequent login attempts for user %s, try again later.", user_name);
4aa0a8ac
LP
405
406 else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD)) {
407
408 if (!strv_isempty(hr->password))
409 log_notice("Password incorrect or not sufficient, please try again.");
410
ea086f06
LP
411 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
412 * let's push what we acquire here into the cache */
413 r = acquire_existing_password(
414 user_name,
415 hr,
416 emphasize_current_password,
417 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
4aa0a8ac
LP
418 if (r < 0)
419 return r;
420
c7b6051f
LP
421 } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_RECOVERY_KEY)) {
422
423 if (!strv_isempty(hr->password))
424 log_notice("Recovery key incorrect or not sufficient, please try again.");
425
426 /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
427 * let's push what we acquire here into the cache */
428 r = acquire_recovery_key(
429 user_name,
430 hr,
431 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
432 if (r < 0)
433 return r;
434
4aa0a8ac
LP
435 } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) {
436
437 if (strv_isempty(hr->password))
438 log_notice("Security token not inserted, please enter password.");
439 else
440 log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
441
ea086f06
LP
442 r = acquire_existing_password(
443 user_name,
444 hr,
445 emphasize_current_password,
446 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
4aa0a8ac
LP
447 if (r < 0)
448 return r;
449
450 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
451
ea086f06
LP
452 /* First time the PIN is requested, let's accept cached data, and allow using credential store */
453 r = acquire_token_pin(
454 user_name,
455 hr,
456 ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_PUSH_CACHE);
4aa0a8ac
LP
457 if (r < 0)
458 return r;
459
460 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) {
461
f737186a
LP
462 log_notice("%s%sPlease authenticate physically on security token.",
463 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
464 emoji_enabled() ? " " : "");
4aa0a8ac
LP
465
466 r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, true);
467 if (r < 0)
468 return log_error_errno(r, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
469
7b78db28
LP
470 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
471
17e7561a 472 log_notice("%s%sPlease confirm presence on security token.",
7b78db28
LP
473 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
474 emoji_enabled() ? " " : "");
475
476 r = user_record_set_fido2_user_presence_permitted(hr, true);
477 if (r < 0)
478 return log_error_errno(r, "Failed to set FIDO2 user presence permitted flag: %m");
479
17e7561a
LP
480 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED)) {
481
482 log_notice("%s%sPlease verify user on security token.",
483 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
484 emoji_enabled() ? " " : "");
485
486 r = user_record_set_fido2_user_verification_permitted(hr, true);
487 if (r < 0)
488 return log_error_errno(r, "Failed to set FIDO2 user verification permitted flag: %m");
489
4aa0a8ac 490 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED))
7b78db28 491 return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
4aa0a8ac
LP
492
493 else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
494
495 log_notice("Security token PIN incorrect, please try again.");
496
ea086f06
LP
497 /* If the previous PIN was wrong don't accept cached info anymore, but add to cache. Also, don't use the credential data */
498 r = acquire_token_pin(
499 user_name,
500 hr,
501 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
4aa0a8ac
LP
502 if (r < 0)
503 return r;
504
505 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT)) {
506
507 log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
508
ea086f06
LP
509 r = acquire_token_pin(
510 user_name,
511 hr,
512 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
4aa0a8ac
LP
513 if (r < 0)
514 return r;
515
516 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT)) {
517
518 log_notice("Security token PIN incorrect, please try again (only one try left!).");
519
ea086f06
LP
520 r = acquire_token_pin(
521 user_name,
522 hr,
523 ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
4aa0a8ac
LP
524 if (r < 0)
525 return r;
526 } else
527 return log_error_errno(ret, "Operation on home %s failed: %s", user_name, bus_error_message(error, ret));
528
529 return 0;
530}
531
57bb9bcb
LP
532static int acquire_passed_secrets(const char *user_name, UserRecord **ret) {
533 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
534 int r;
535
536 assert(ret);
537
538 /* Generates an initial secret objects that contains passwords supplied via $PASSWORD, the password
539 * cache or the credentials subsystem, but excluding any interactive stuff. If nothing is passed,
540 * returns an empty secret object. */
541
542 secret = user_record_new();
543 if (!secret)
544 return log_oom();
545
546 r = acquire_existing_password(
547 user_name,
548 secret,
549 /* emphasize_current_password = */ false,
550 ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_NO_TTY | ASK_PASSWORD_NO_AGENT);
551 if (r < 0)
552 return r;
553
554 r = acquire_token_pin(
555 user_name,
556 secret,
557 ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_NO_TTY | ASK_PASSWORD_NO_AGENT);
558 if (r < 0)
559 return r;
560
c7b6051f
LP
561 r = acquire_recovery_key(
562 user_name,
563 secret,
564 ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_NO_TTY | ASK_PASSWORD_NO_AGENT);
565 if (r < 0)
566 return r;
567
57bb9bcb
LP
568 *ret = TAKE_PTR(secret);
569 return 0;
570}
571
4aa0a8ac
LP
572static int activate_home(int argc, char *argv[], void *userdata) {
573 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
574 int r, ret = 0;
4aa0a8ac
LP
575
576 r = acquire_bus(&bus);
577 if (r < 0)
578 return r;
579
580 STRV_FOREACH(i, strv_skip(argv, 1)) {
581 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
582
57bb9bcb
LP
583 r = acquire_passed_secrets(*i, &secret);
584 if (r < 0)
585 return r;
4aa0a8ac
LP
586
587 for (;;) {
588 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
589 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
590
cc9886bc 591 r = bus_message_new_method_call(bus, &m, bus_mgr, "ActivateHome");
4aa0a8ac
LP
592 if (r < 0)
593 return bus_log_create_error(r);
594
595 r = sd_bus_message_append(m, "s", *i);
596 if (r < 0)
597 return bus_log_create_error(r);
598
599 r = bus_message_append_secret(m, secret);
600 if (r < 0)
601 return bus_log_create_error(r);
602
603 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
604 if (r < 0) {
ea086f06 605 r = handle_generic_user_record_error(*i, secret, &error, r, /* emphasize_current_password= */ false);
4aa0a8ac
LP
606 if (r < 0) {
607 if (ret == 0)
608 ret = r;
609
610 break;
611 }
612 } else
613 break;
614 }
615 }
616
617 return ret;
618}
619
620static int deactivate_home(int argc, char *argv[], void *userdata) {
621 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
622 int r, ret = 0;
4aa0a8ac
LP
623
624 r = acquire_bus(&bus);
625 if (r < 0)
626 return r;
627
628 STRV_FOREACH(i, strv_skip(argv, 1)) {
629 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
630 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
631
cc9886bc 632 r = bus_message_new_method_call(bus, &m, bus_mgr, "DeactivateHome");
4aa0a8ac
LP
633 if (r < 0)
634 return bus_log_create_error(r);
635
636 r = sd_bus_message_append(m, "s", *i);
637 if (r < 0)
638 return bus_log_create_error(r);
639
640 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
641 if (r < 0) {
642 log_error_errno(r, "Failed to deactivate user home: %s", bus_error_message(&error, r));
643 if (ret == 0)
644 ret = r;
645 }
646 }
647
648 return ret;
649}
650
651static void dump_home_record(UserRecord *hr) {
652 int r;
653
654 assert(hr);
655
656 if (hr->incomplete) {
657 fflush(stdout);
658 log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr->user_name);
659 }
660
6a01ea4a
LP
661 if (arg_json_format_flags & JSON_FORMAT_OFF)
662 user_record_show(hr, true);
663 else {
4aa0a8ac
LP
664 _cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
665
666 if (arg_export_format == EXPORT_FORMAT_STRIPPED)
bfc0cc1a 667 r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED|USER_RECORD_PERMISSIVE, &stripped);
4aa0a8ac 668 else if (arg_export_format == EXPORT_FORMAT_MINIMAL)
bfc0cc1a 669 r = user_record_clone(hr, USER_RECORD_EXTRACT_SIGNABLE|USER_RECORD_PERMISSIVE, &stripped);
4aa0a8ac
LP
670 else
671 r = 0;
672 if (r < 0)
673 log_warning_errno(r, "Failed to strip user record, ignoring: %m");
674 if (stripped)
675 hr = stripped;
676
677 json_variant_dump(hr->json, arg_json_format_flags, stdout, NULL);
6a01ea4a 678 }
4aa0a8ac
LP
679}
680
681static char **mangle_user_list(char **list, char ***ret_allocated) {
682 _cleanup_free_ char *myself = NULL;
683 char **l;
684
685 if (!strv_isempty(list)) {
686 *ret_allocated = NULL;
687 return list;
688 }
689
690 myself = getusername_malloc();
691 if (!myself)
692 return NULL;
693
694 l = new(char*, 2);
695 if (!l)
696 return NULL;
697
698 l[0] = TAKE_PTR(myself);
699 l[1] = NULL;
700
701 *ret_allocated = l;
702 return l;
703}
704
705static int inspect_home(int argc, char *argv[], void *userdata) {
706 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
5d2a48da 707 _cleanup_strv_free_ char **mangled_list = NULL;
4aa0a8ac 708 int r, ret = 0;
de010b0b 709 char **items;
4aa0a8ac 710
384c2c32 711 pager_open(arg_pager_flags);
4aa0a8ac
LP
712
713 r = acquire_bus(&bus);
714 if (r < 0)
715 return r;
716
717 items = mangle_user_list(strv_skip(argv, 1), &mangled_list);
718 if (!items)
719 return log_oom();
720
721 STRV_FOREACH(i, items) {
722 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
723 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
724 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
725 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
726 const char *json;
727 int incomplete;
728 uid_t uid;
729
730 r = parse_uid(*i, &uid);
731 if (r < 0) {
7a8867ab 732 if (!valid_user_group_name(*i, 0)) {
4aa0a8ac
LP
733 log_error("Invalid user name '%s'.", *i);
734 if (ret == 0)
735 ret = -EINVAL;
736
737 continue;
738 }
739
cc9886bc 740 r = bus_call_method(bus, bus_mgr, "GetUserRecordByName", &error, &reply, "s", *i);
38cd55b0 741 } else
cc9886bc 742 r = bus_call_method(bus, bus_mgr, "GetUserRecordByUID", &error, &reply, "u", (uint32_t) uid);
4aa0a8ac
LP
743
744 if (r < 0) {
745 log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
746 if (ret == 0)
747 ret = r;
748
749 continue;
750 }
751
752 r = sd_bus_message_read(reply, "sbo", &json, &incomplete, NULL);
753 if (r < 0) {
754 bus_log_parse_error(r);
755 if (ret == 0)
756 ret = r;
757
758 continue;
759 }
760
761 r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
762 if (r < 0) {
763 log_error_errno(r, "Failed to parse JSON identity: %m");
764 if (ret == 0)
765 ret = r;
766
767 continue;
768 }
769
770 hr = user_record_new();
771 if (!hr)
772 return log_oom();
773
bfc0cc1a 774 r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG|USER_RECORD_PERMISSIVE);
4aa0a8ac
LP
775 if (r < 0) {
776 if (ret == 0)
777 ret = r;
778
779 continue;
780 }
781
782 hr->incomplete = incomplete;
783 dump_home_record(hr);
784 }
785
786 return ret;
787}
788
789static int authenticate_home(int argc, char *argv[], void *userdata) {
790 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
5d2a48da 791 _cleanup_strv_free_ char **mangled_list = NULL;
4aa0a8ac 792 int r, ret = 0;
de010b0b 793 char **items;
4aa0a8ac
LP
794
795 items = mangle_user_list(strv_skip(argv, 1), &mangled_list);
796 if (!items)
797 return log_oom();
798
799 r = acquire_bus(&bus);
800 if (r < 0)
801 return r;
802
803 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
804
805 STRV_FOREACH(i, items) {
806 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
807
57bb9bcb
LP
808 r = acquire_passed_secrets(*i, &secret);
809 if (r < 0)
810 return r;
4aa0a8ac
LP
811
812 for (;;) {
813 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
814 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
815
cc9886bc 816 r = bus_message_new_method_call(bus, &m, bus_mgr, "AuthenticateHome");
4aa0a8ac
LP
817 if (r < 0)
818 return bus_log_create_error(r);
819
820 r = sd_bus_message_append(m, "s", *i);
821 if (r < 0)
822 return bus_log_create_error(r);
823
824 r = bus_message_append_secret(m, secret);
825 if (r < 0)
826 return bus_log_create_error(r);
827
828 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
829 if (r < 0) {
830 r = handle_generic_user_record_error(*i, secret, &error, r, false);
831 if (r < 0) {
832 if (ret == 0)
833 ret = r;
834
835 break;
836 }
837 } else
838 break;
839 }
840 }
841
842 return ret;
843}
844
845static int update_last_change(JsonVariant **v, bool with_password, bool override) {
846 JsonVariant *c;
847 usec_t n;
848 int r;
849
850 assert(v);
851
852 n = now(CLOCK_REALTIME);
853
854 c = json_variant_by_key(*v, "lastChangeUSec");
855 if (c) {
718ca772 856 uint64_t u;
4aa0a8ac
LP
857
858 if (!override)
859 goto update_password;
860
861 if (!json_variant_is_unsigned(c))
862 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastChangeUSec field is not an unsigned integer, refusing.");
863
864 u = json_variant_unsigned(c);
865 if (u >= n)
866 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastChangeUSec is from the future, can't update.");
867 }
868
869 r = json_variant_set_field_unsigned(v, "lastChangeUSec", n);
870 if (r < 0)
871 return log_error_errno(r, "Failed to update lastChangeUSec: %m");
872
873update_password:
874 if (!with_password)
875 return 0;
876
877 c = json_variant_by_key(*v, "lastPasswordChangeUSec");
878 if (c) {
718ca772 879 uint64_t u;
4aa0a8ac
LP
880
881 if (!override)
882 return 0;
883
884 if (!json_variant_is_unsigned(c))
885 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastPasswordChangeUSec field is not an unsigned integer, refusing.");
886
887 u = json_variant_unsigned(c);
888 if (u >= n)
889 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastPasswordChangeUSec is from the future, can't update.");
890 }
891
892 r = json_variant_set_field_unsigned(v, "lastPasswordChangeUSec", n);
893 if (r < 0)
894 return log_error_errno(r, "Failed to update lastPasswordChangeUSec: %m");
895
896 return 1;
897}
898
899static int apply_identity_changes(JsonVariant **_v) {
900 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
901 int r;
902
903 assert(_v);
904
905 v = json_variant_ref(*_v);
906
907 r = json_variant_filter(&v, arg_identity_filter);
908 if (r < 0)
909 return log_error_errno(r, "Failed to filter identity: %m");
910
e931768e 911 r = json_variant_merge_object(&v, arg_identity_extra);
4aa0a8ac
LP
912 if (r < 0)
913 return log_error_errno(r, "Failed to merge identities: %m");
914
915 if (arg_identity_extra_this_machine || !strv_isempty(arg_identity_filter)) {
916 _cleanup_(json_variant_unrefp) JsonVariant *per_machine = NULL, *mmid = NULL;
4aa0a8ac
LP
917 sd_id128_t mid;
918
919 r = sd_id128_get_machine(&mid);
920 if (r < 0)
921 return log_error_errno(r, "Failed to acquire machine ID: %m");
922
85b55869 923 r = json_variant_new_string(&mmid, SD_ID128_TO_STRING(mid));
4aa0a8ac
LP
924 if (r < 0)
925 return log_error_errno(r, "Failed to allocate matchMachineId object: %m");
926
927 per_machine = json_variant_ref(json_variant_by_key(v, "perMachine"));
928 if (per_machine) {
929 _cleanup_(json_variant_unrefp) JsonVariant *npm = NULL, *add = NULL;
930 _cleanup_free_ JsonVariant **array = NULL;
931 JsonVariant *z;
932 size_t i = 0;
933
934 if (!json_variant_is_array(per_machine))
935 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "perMachine field is not an array, refusing.");
936
937 array = new(JsonVariant*, json_variant_elements(per_machine) + 1);
938 if (!array)
939 return log_oom();
940
941 JSON_VARIANT_ARRAY_FOREACH(z, per_machine) {
942 JsonVariant *u;
943
944 if (!json_variant_is_object(z))
945 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "perMachine entry is not an object, refusing.");
946
947 array[i++] = z;
948
949 u = json_variant_by_key(z, "matchMachineId");
950 if (!u)
951 continue;
952
953 if (!json_variant_equal(u, mmid))
954 continue;
955
e931768e 956 r = json_variant_merge_object(&add, z);
4aa0a8ac
LP
957 if (r < 0)
958 return log_error_errno(r, "Failed to merge perMachine entry: %m");
959
960 i--;
961 }
962
963 r = json_variant_filter(&add, arg_identity_filter);
964 if (r < 0)
965 return log_error_errno(r, "Failed to filter perMachine: %m");
966
e931768e 967 r = json_variant_merge_object(&add, arg_identity_extra_this_machine);
4aa0a8ac
LP
968 if (r < 0)
969 return log_error_errno(r, "Failed to merge in perMachine fields: %m");
970
971 if (arg_identity_filter_rlimits || arg_identity_extra_rlimits) {
972 _cleanup_(json_variant_unrefp) JsonVariant *rlv = NULL;
973
974 rlv = json_variant_ref(json_variant_by_key(add, "resourceLimits"));
975
976 r = json_variant_filter(&rlv, arg_identity_filter_rlimits);
977 if (r < 0)
978 return log_error_errno(r, "Failed to filter resource limits: %m");
979
e931768e 980 r = json_variant_merge_object(&rlv, arg_identity_extra_rlimits);
4aa0a8ac
LP
981 if (r < 0)
982 return log_error_errno(r, "Failed to set resource limits: %m");
983
984 if (json_variant_is_blank_object(rlv)) {
985 r = json_variant_filter(&add, STRV_MAKE("resourceLimits"));
986 if (r < 0)
987 return log_error_errno(r, "Failed to drop resource limits field from identity: %m");
988 } else {
989 r = json_variant_set_field(&add, "resourceLimits", rlv);
990 if (r < 0)
991 return log_error_errno(r, "Failed to update resource limits of identity: %m");
992 }
993 }
994
995 if (!json_variant_is_blank_object(add)) {
996 r = json_variant_set_field(&add, "matchMachineId", mmid);
997 if (r < 0)
998 return log_error_errno(r, "Failed to set matchMachineId field: %m");
999
1000 array[i++] = add;
1001 }
1002
1003 r = json_variant_new_array(&npm, array, i);
1004 if (r < 0)
1005 return log_error_errno(r, "Failed to allocate new perMachine array: %m");
1006
1007 json_variant_unref(per_machine);
1008 per_machine = TAKE_PTR(npm);
1009 } else {
1010 _cleanup_(json_variant_unrefp) JsonVariant *item = json_variant_ref(arg_identity_extra_this_machine);
1011
1012 if (arg_identity_extra_rlimits) {
1013 r = json_variant_set_field(&item, "resourceLimits", arg_identity_extra_rlimits);
1014 if (r < 0)
1015 return log_error_errno(r, "Failed to update resource limits of identity: %m");
1016 }
1017
1018 r = json_variant_set_field(&item, "matchMachineId", mmid);
1019 if (r < 0)
1020 return log_error_errno(r, "Failed to set matchMachineId field: %m");
1021
1022 r = json_variant_append_array(&per_machine, item);
1023 if (r < 0)
1024 return log_error_errno(r, "Failed to append to perMachine array: %m");
1025 }
1026
1027 r = json_variant_set_field(&v, "perMachine", per_machine);
1028 if (r < 0)
1029 return log_error_errno(r, "Failed to update per machine record: %m");
1030 }
1031
1032 if (arg_identity_extra_privileged || arg_identity_filter) {
1033 _cleanup_(json_variant_unrefp) JsonVariant *privileged = NULL;
1034
1035 privileged = json_variant_ref(json_variant_by_key(v, "privileged"));
1036
1037 r = json_variant_filter(&privileged, arg_identity_filter);
1038 if (r < 0)
1039 return log_error_errno(r, "Failed to filter identity (privileged part): %m");
1040
e931768e 1041 r = json_variant_merge_object(&privileged, arg_identity_extra_privileged);
4aa0a8ac
LP
1042 if (r < 0)
1043 return log_error_errno(r, "Failed to merge identities (privileged part): %m");
1044
1045 if (json_variant_is_blank_object(privileged)) {
1046 r = json_variant_filter(&v, STRV_MAKE("privileged"));
1047 if (r < 0)
1048 return log_error_errno(r, "Failed to drop privileged part from identity: %m");
1049 } else {
1050 r = json_variant_set_field(&v, "privileged", privileged);
1051 if (r < 0)
1052 return log_error_errno(r, "Failed to update privileged part of identity: %m");
1053 }
1054 }
1055
1056 if (arg_identity_filter_rlimits) {
1057 _cleanup_(json_variant_unrefp) JsonVariant *rlv = NULL;
1058
1059 rlv = json_variant_ref(json_variant_by_key(v, "resourceLimits"));
1060
1061 r = json_variant_filter(&rlv, arg_identity_filter_rlimits);
1062 if (r < 0)
1063 return log_error_errno(r, "Failed to filter resource limits: %m");
1064
1065 /* Note that we only filter resource limits here, but don't apply them. We do that in the perMachine section */
1066
1067 if (json_variant_is_blank_object(rlv)) {
1068 r = json_variant_filter(&v, STRV_MAKE("resourceLimits"));
1069 if (r < 0)
1070 return log_error_errno(r, "Failed to drop resource limits field from identity: %m");
1071 } else {
1072 r = json_variant_set_field(&v, "resourceLimits", rlv);
1073 if (r < 0)
1074 return log_error_errno(r, "Failed to update resource limits of identity: %m");
1075 }
1076 }
1077
1078 json_variant_unref(*_v);
1079 *_v = TAKE_PTR(v);
1080
1081 return 0;
1082}
1083
1084static int add_disposition(JsonVariant **v) {
1085 int r;
1086
1087 assert(v);
1088
1089 if (json_variant_by_key(*v, "disposition"))
1090 return 0;
1091
1092 /* Set the disposition to regular, if not configured explicitly */
1093 r = json_variant_set_field_string(v, "disposition", "regular");
1094 if (r < 0)
1095 return log_error_errno(r, "Failed to set disposition field: %m");
1096
1097 return 1;
1098}
1099
3ccadbce 1100static int acquire_new_home_record(JsonVariant *input, UserRecord **ret) {
4aa0a8ac
LP
1101 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1102 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
4aa0a8ac
LP
1103 int r;
1104
1105 assert(ret);
1106
1107 if (arg_identity) {
1108 unsigned line, column;
1109
3ccadbce
LP
1110 if (input)
1111 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Two identity records specified, refusing.");
1112
4aa0a8ac
LP
1113 r = json_parse_file(
1114 streq(arg_identity, "-") ? stdin : NULL,
1115 streq(arg_identity, "-") ? "<stdin>" : arg_identity, JSON_PARSE_SENSITIVE, &v, &line, &column);
1116 if (r < 0)
1117 return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
3ccadbce
LP
1118 } else
1119 v = json_variant_ref(input);
4aa0a8ac
LP
1120
1121 r = apply_identity_changes(&v);
1122 if (r < 0)
1123 return r;
1124
1125 r = add_disposition(&v);
1126 if (r < 0)
1127 return r;
1128
1129 STRV_FOREACH(i, arg_pkcs11_token_uri) {
93295a25 1130 r = identity_add_pkcs11_key_data(&v, *i);
4aa0a8ac
LP
1131 if (r < 0)
1132 return r;
1133 }
1134
1c0c4a43 1135 STRV_FOREACH(i, arg_fido2_device) {
70e723c0 1136 r = identity_add_fido2_parameters(&v, *i, arg_fido2_lock_with, arg_fido2_cred_alg);
1c0c4a43
LP
1137 if (r < 0)
1138 return r;
1139 }
1140
80c41552
LP
1141 if (arg_recovery_key) {
1142 r = identity_add_recovery_key(&v);
1143 if (r < 0)
1144 return r;
1145 }
1146
4aa0a8ac
LP
1147 r = update_last_change(&v, true, false);
1148 if (r < 0)
1149 return r;
1150
1151 if (DEBUG_LOGGING)
1152 json_variant_dump(v, JSON_FORMAT_PRETTY, NULL, NULL);
1153
1154 hr = user_record_new();
1155 if (!hr)
1156 return log_oom();
1157
6f9dd369
LP
1158 r = user_record_load(
1159 hr,
1160 v,
1161 USER_RECORD_REQUIRE_REGULAR|
1162 USER_RECORD_ALLOW_SECRET|
1163 USER_RECORD_ALLOW_PRIVILEGED|
1164 USER_RECORD_ALLOW_PER_MACHINE|
1165 USER_RECORD_STRIP_BINDING|
1166 USER_RECORD_STRIP_STATUS|
1167 USER_RECORD_STRIP_SIGNATURE|
1168 USER_RECORD_LOG|
1169 USER_RECORD_PERMISSIVE);
4aa0a8ac
LP
1170 if (r < 0)
1171 return r;
1172
1173 *ret = TAKE_PTR(hr);
1174 return 0;
1175}
1176
1177static int acquire_new_password(
1178 const char *user_name,
1179 UserRecord *hr,
80c41552
LP
1180 bool suggest,
1181 char **ret) {
4aa0a8ac 1182
e99ca147 1183 _cleanup_(erase_and_freep) char *envpw = NULL;
4aa0a8ac 1184 unsigned i = 5;
4aa0a8ac
LP
1185 int r;
1186
1187 assert(user_name);
1188 assert(hr);
1189
e99ca147
LP
1190 r = getenv_steal_erase("NEWPASSWORD", &envpw);
1191 if (r < 0)
1192 return log_error_errno(r, "Failed to acquire password from environment: %m");
1193 if (r > 0) {
4aa0a8ac
LP
1194 /* As above, this is not for use, just for testing */
1195
e99ca147 1196 r = user_record_set_password(hr, STRV_MAKE(envpw), /* prepend = */ true);
4aa0a8ac
LP
1197 if (r < 0)
1198 return log_error_errno(r, "Failed to store password: %m");
1199
80c41552 1200 if (ret)
e99ca147 1201 *ret = TAKE_PTR(envpw);
80c41552 1202
4aa0a8ac
LP
1203 return 0;
1204 }
1205
1206 if (suggest)
1207 (void) suggest_passwords();
1208
1209 for (;;) {
5d2a48da 1210 _cleanup_strv_free_erase_ char **first = NULL, **second = NULL;
4aa0a8ac
LP
1211 _cleanup_free_ char *question = NULL;
1212
1213 if (--i == 0)
1214 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up:");
1215
1216 if (asprintf(&question, "Please enter new password for user %s:", user_name) < 0)
1217 return log_oom();
1218
ea086f06
LP
1219 r = ask_password_auto(
1220 question,
1221 /* icon= */ "user-home",
1222 NULL,
1223 /* key_name= */ "home-password",
1224 /* credential_name= */ "home.new-password",
1225 USEC_INFINITY,
1226 0, /* no caching, we want to collect a new password here after all */
1227 &first);
4aa0a8ac
LP
1228 if (r < 0)
1229 return log_error_errno(r, "Failed to acquire password: %m");
1230
1231 question = mfree(question);
1232 if (asprintf(&question, "Please enter new password for user %s (repeat):", user_name) < 0)
1233 return log_oom();
1234
ea086f06
LP
1235 r = ask_password_auto(
1236 question,
1237 /* icon= */ "user-home",
1238 NULL,
1239 /* key_name= */ "home-password",
1240 /* credential_name= */ "home.new-password",
1241 USEC_INFINITY,
1242 0, /* no caching */
1243 &second);
4aa0a8ac
LP
1244 if (r < 0)
1245 return log_error_errno(r, "Failed to acquire password: %m");
1246
1247 if (strv_equal(first, second)) {
80c41552
LP
1248 _cleanup_(erase_and_freep) char *copy = NULL;
1249
1250 if (ret) {
1251 copy = strdup(first[0]);
1252 if (!copy)
1253 return log_oom();
1254 }
1255
1256 r = user_record_set_password(hr, first, /* prepend = */ true);
4aa0a8ac
LP
1257 if (r < 0)
1258 return log_error_errno(r, "Failed to store password: %m");
1259
80c41552
LP
1260 if (ret)
1261 *ret = TAKE_PTR(copy);
1262
4aa0a8ac
LP
1263 return 0;
1264 }
1265
80ace4f2 1266 log_error("Password didn't match, try again.");
4aa0a8ac
LP
1267 }
1268}
1269
3ccadbce 1270static int create_home_common(JsonVariant *input) {
4aa0a8ac
LP
1271 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1272 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
4aa0a8ac
LP
1273 int r;
1274
1275 r = acquire_bus(&bus);
1276 if (r < 0)
1277 return r;
1278
1279 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1280
3ccadbce 1281 r = acquire_new_home_record(input, &hr);
4aa0a8ac
LP
1282 if (r < 0)
1283 return r;
1284
80c41552
LP
1285 /* If the JSON record carries no plain text password (besides the recovery key), then let's query it
1286 * manually. */
1287 if (strv_length(hr->password) <= arg_recovery_key) {
4aa0a8ac
LP
1288
1289 if (strv_isempty(hr->hashed_password)) {
80c41552
LP
1290 _cleanup_(erase_and_freep) char *new_password = NULL;
1291
4aa0a8ac 1292 /* No regular (i.e. non-PKCS#11) hashed passwords set in the record, let's fix that. */
80c41552 1293 r = acquire_new_password(hr->user_name, hr, /* suggest = */ true, &new_password);
4aa0a8ac
LP
1294 if (r < 0)
1295 return r;
1296
80c41552 1297 r = user_record_make_hashed_password(hr, STRV_MAKE(new_password), /* extend = */ false);
4aa0a8ac
LP
1298 if (r < 0)
1299 return log_error_errno(r, "Failed to hash password: %m");
1300 } else {
1301 /* There's a hash password set in the record, acquire the unhashed version of it. */
ea086f06
LP
1302 r = acquire_existing_password(
1303 hr->user_name,
1304 hr,
1305 /* emphasize_current= */ false,
1306 ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_PUSH_CACHE);
4aa0a8ac
LP
1307 if (r < 0)
1308 return r;
1309 }
1310 }
1311
1312 if (hr->enforce_password_policy == 0) {
1313 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1314
1315 /* If password quality enforcement is disabled, let's at least warn client side */
1316
d34b1823 1317 r = user_record_check_password_quality(hr, hr, &error);
4aa0a8ac
LP
1318 if (r < 0)
1319 log_warning_errno(r, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error, r));
1320 }
1321
1322 for (;;) {
1323 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1324 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1325 _cleanup_(erase_and_freep) char *formatted = NULL;
1326
1327 r = json_variant_format(hr->json, 0, &formatted);
1328 if (r < 0)
7b8d55b7 1329 return log_error_errno(r, "Failed to format user record: %m");
4aa0a8ac 1330
cc9886bc 1331 r = bus_message_new_method_call(bus, &m, bus_mgr, "CreateHome");
4aa0a8ac
LP
1332 if (r < 0)
1333 return bus_log_create_error(r);
1334
2ffee2c9
LP
1335 (void) sd_bus_message_sensitive(m);
1336
4aa0a8ac
LP
1337 r = sd_bus_message_append(m, "s", formatted);
1338 if (r < 0)
1339 return bus_log_create_error(r);
1340
1341 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1342 if (r < 0) {
8e62dfb1 1343 if (sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY)) {
80c41552
LP
1344 _cleanup_(erase_and_freep) char *new_password = NULL;
1345
8e62dfb1
LP
1346 log_error_errno(r, "%s", bus_error_message(&error, r));
1347 log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
4aa0a8ac 1348
80c41552 1349 r = acquire_new_password(hr->user_name, hr, /* suggest = */ false, &new_password);
8e62dfb1
LP
1350 if (r < 0)
1351 return r;
4aa0a8ac 1352
80c41552 1353 r = user_record_make_hashed_password(hr, STRV_MAKE(new_password), /* extend = */ false);
8e62dfb1
LP
1354 if (r < 0)
1355 return log_error_errno(r, "Failed to hash passwords: %m");
1356 } else {
1357 r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
1358 if (r < 0)
1359 return r;
1360 }
1361 } else
1362 break; /* done */
4aa0a8ac
LP
1363 }
1364
1365 return 0;
1366}
1367
3ccadbce
LP
1368static int create_home(int argc, char *argv[], void *userdata) {
1369 int r;
1370
1371 if (argc >= 2) {
1372 /* If a username was specified, use it */
1373
1374 if (valid_user_group_name(argv[1], 0))
1375 r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]);
1376 else {
1377 _cleanup_free_ char *un = NULL, *rr = NULL;
1378
1379 /* Before we consider the user name invalid, let's check if we can split it? */
1380 r = split_user_name_realm(argv[1], &un, &rr);
1381 if (r < 0)
1382 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]);
1383
1384 if (rr) {
1385 r = json_variant_set_field_string(&arg_identity_extra, "realm", rr);
1386 if (r < 0)
1387 return log_error_errno(r, "Failed to set realm field: %m");
1388 }
1389
1390 r = json_variant_set_field_string(&arg_identity_extra, "userName", un);
1391 }
1392 if (r < 0)
1393 return log_error_errno(r, "Failed to set userName field: %m");
1394 } else {
1395 /* If neither a username nor an identity have been specified we cannot operate. */
1396 if (!arg_identity)
1397 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required.");
1398 }
1399
1400 return create_home_common(/* input= */ NULL);
1401}
1402
4aa0a8ac
LP
1403static int remove_home(int argc, char *argv[], void *userdata) {
1404 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1405 int r, ret = 0;
4aa0a8ac
LP
1406
1407 r = acquire_bus(&bus);
1408 if (r < 0)
1409 return r;
1410
1411 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1412
1413 STRV_FOREACH(i, strv_skip(argv, 1)) {
1414 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1415 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1416
cc9886bc 1417 r = bus_message_new_method_call(bus, &m, bus_mgr, "RemoveHome");
4aa0a8ac
LP
1418 if (r < 0)
1419 return bus_log_create_error(r);
1420
1421 r = sd_bus_message_append(m, "s", *i);
1422 if (r < 0)
1423 return bus_log_create_error(r);
1424
1425 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1426 if (r < 0) {
1427 log_error_errno(r, "Failed to remove home: %s", bus_error_message(&error, r));
1428 if (ret == 0)
1429 ret = r;
1430 }
1431 }
1432
1433 return ret;
1434}
1435
1436static int acquire_updated_home_record(
1437 sd_bus *bus,
1438 const char *username,
1439 UserRecord **ret) {
1440
1441 _cleanup_(json_variant_unrefp) JsonVariant *json = NULL;
1442 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
4aa0a8ac
LP
1443 int r;
1444
1445 assert(ret);
1446
1447 if (arg_identity) {
1448 unsigned line, column;
1449 JsonVariant *un;
1450
1451 r = json_parse_file(
1452 streq(arg_identity, "-") ? stdin : NULL,
1453 streq(arg_identity, "-") ? "<stdin>" : arg_identity, JSON_PARSE_SENSITIVE, &json, &line, &column);
1454 if (r < 0)
1455 return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
1456
1457 un = json_variant_by_key(json, "userName");
1458 if (un) {
1459 if (!json_variant_is_string(un) || (username && !streq(json_variant_string(un), username)))
1460 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name specified on command line and in JSON record do not match.");
1461 } else {
1462 if (!username)
1463 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No username specified.");
1464
1465 r = json_variant_set_field_string(&arg_identity_extra, "userName", username);
1466 if (r < 0)
1467 return log_error_errno(r, "Failed to set userName field: %m");
1468 }
1469
1470 } else {
1471 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1472 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1473 int incomplete;
1474 const char *text;
1475
1476 if (!identity_properties_specified())
1477 return log_error_errno(SYNTHETIC_ERRNO(EALREADY), "No field to change specified.");
1478
cc9886bc 1479 r = bus_call_method(bus, bus_mgr, "GetUserRecordByName", &error, &reply, "s", username);
4aa0a8ac
LP
1480 if (r < 0)
1481 return log_error_errno(r, "Failed to acquire user home record: %s", bus_error_message(&error, r));
1482
1483 r = sd_bus_message_read(reply, "sbo", &text, &incomplete, NULL);
1484 if (r < 0)
1485 return bus_log_parse_error(r);
1486
1487 if (incomplete)
1488 return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Lacking rights to acquire user record including privileged metadata, can't update record.");
1489
1490 r = json_parse(text, JSON_PARSE_SENSITIVE, &json, NULL, NULL);
1491 if (r < 0)
1492 return log_error_errno(r, "Failed to parse JSON identity: %m");
1493
1494 reply = sd_bus_message_unref(reply);
1495
1496 r = json_variant_filter(&json, STRV_MAKE("binding", "status", "signature"));
1497 if (r < 0)
1498 return log_error_errno(r, "Failed to strip binding and status from record to update: %m");
1499 }
1500
1501 r = apply_identity_changes(&json);
1502 if (r < 0)
1503 return r;
1504
1505 STRV_FOREACH(i, arg_pkcs11_token_uri) {
93295a25 1506 r = identity_add_pkcs11_key_data(&json, *i);
4aa0a8ac
LP
1507 if (r < 0)
1508 return r;
1509 }
1510
1c0c4a43 1511 STRV_FOREACH(i, arg_fido2_device) {
70e723c0 1512 r = identity_add_fido2_parameters(&json, *i, arg_fido2_lock_with, arg_fido2_cred_alg);
1c0c4a43
LP
1513 if (r < 0)
1514 return r;
1515 }
1516
4aa0a8ac
LP
1517 /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
1518 * override. */
1c0c4a43 1519 r = update_last_change(&json, arg_pkcs11_token_uri || arg_fido2_device, !arg_identity);
4aa0a8ac
LP
1520 if (r < 0)
1521 return r;
1522
1523 if (DEBUG_LOGGING)
1524 json_variant_dump(json, JSON_FORMAT_PRETTY, NULL, NULL);
1525
1526 hr = user_record_new();
1527 if (!hr)
1528 return log_oom();
1529
bfc0cc1a 1530 r = user_record_load(hr, json, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG|USER_RECORD_PERMISSIVE);
4aa0a8ac
LP
1531 if (r < 0)
1532 return r;
1533
1534 *ret = TAKE_PTR(hr);
1535 return 0;
1536}
1537
c98811d8
LP
1538static int home_record_reset_human_interaction_permission(UserRecord *hr) {
1539 int r;
1540
1541 assert(hr);
1542
1543 /* When we execute multiple operations one after the other, let's reset the permission to ask the
1544 * user each time, so that if interaction is necessary we will be told so again and thus can print a
1545 * nice message to the user, telling the user so. */
1546
1547 r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, -1);
1548 if (r < 0)
1549 return log_error_errno(r, "Failed to reset PKCS#11 protected authentication path permission flag: %m");
1550
1551 r = user_record_set_fido2_user_presence_permitted(hr, -1);
1552 if (r < 0)
1553 return log_error_errno(r, "Failed to reset FIDO2 user presence permission flag: %m");
1554
17e7561a
LP
1555 r = user_record_set_fido2_user_verification_permitted(hr, -1);
1556 if (r < 0)
1557 return log_error_errno(r, "Failed to reset FIDO2 user verification permission flag: %m");
1558
c98811d8
LP
1559 return 0;
1560}
1561
4aa0a8ac
LP
1562static int update_home(int argc, char *argv[], void *userdata) {
1563 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
6b356f44 1564 _cleanup_(user_record_unrefp) UserRecord *hr = NULL, *secret = NULL;
4aa0a8ac
LP
1565 _cleanup_free_ char *buffer = NULL;
1566 const char *username;
1567 int r;
1568
1569 if (argc >= 2)
1570 username = argv[1];
1571 else if (!arg_identity) {
1572 buffer = getusername_malloc();
1573 if (!buffer)
1574 return log_oom();
1575
1576 username = buffer;
1577 } else
1578 username = NULL;
1579
1580 r = acquire_bus(&bus);
1581 if (r < 0)
1582 return r;
1583
1584 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1585
1586 r = acquire_updated_home_record(bus, username, &hr);
1587 if (r < 0)
1588 return r;
1589
6b356f44
LP
1590 /* Add in all secrets we can acquire cheaply */
1591 r = acquire_passed_secrets(username, &secret);
1592 if (r < 0)
1593 return r;
1594
1595 r = user_record_merge_secret(hr, secret);
1596 if (r < 0)
1597 return r;
1598
c98811d8
LP
1599 /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
1600 * authentication might be confusing. */
1601
1602 if (arg_and_resize || arg_and_change_password)
1603 log_info("Updating home directory.");
1604
4aa0a8ac
LP
1605 for (;;) {
1606 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1607 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1608 _cleanup_free_ char *formatted = NULL;
1609
cc9886bc 1610 r = bus_message_new_method_call(bus, &m, bus_mgr, "UpdateHome");
4aa0a8ac
LP
1611 if (r < 0)
1612 return bus_log_create_error(r);
1613
1614 r = json_variant_format(hr->json, 0, &formatted);
1615 if (r < 0)
7b8d55b7 1616 return log_error_errno(r, "Failed to format user record: %m");
4aa0a8ac 1617
2ffee2c9
LP
1618 (void) sd_bus_message_sensitive(m);
1619
4aa0a8ac
LP
1620 r = sd_bus_message_append(m, "s", formatted);
1621 if (r < 0)
1622 return bus_log_create_error(r);
1623
1624 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1625 if (r < 0) {
1626 if (arg_and_change_password &&
1627 sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
1628 /* In the generic handler we'd ask for a password in this case, but when
1629 * changing passwords that's not sufficient, as we need to acquire all keys
1630 * first. */
1631 return log_error_errno(r, "Security token not inserted, refusing.");
1632
1633 r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
1634 if (r < 0)
1635 return r;
1636 } else
1637 break;
1638 }
1639
c98811d8
LP
1640 if (arg_and_resize)
1641 log_info("Resizing home.");
1642
1643 (void) home_record_reset_human_interaction_permission(hr);
1644
4aa0a8ac
LP
1645 /* Also sync down disk size to underlying LUKS/fscrypt/quota */
1646 while (arg_and_resize) {
1647 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1648 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1649
cc9886bc 1650 r = bus_message_new_method_call(bus, &m, bus_mgr, "ResizeHome");
4aa0a8ac
LP
1651 if (r < 0)
1652 return bus_log_create_error(r);
1653
1654 /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
1655 r = sd_bus_message_append(m, "st", hr->user_name, UINT64_MAX);
1656 if (r < 0)
1657 return bus_log_create_error(r);
1658
1659 r = bus_message_append_secret(m, hr);
1660 if (r < 0)
1661 return bus_log_create_error(r);
1662
1663 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1664 if (r < 0) {
1665 if (arg_and_change_password &&
1666 sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
1667 return log_error_errno(r, "Security token not inserted, refusing.");
1668
1669 r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
1670 if (r < 0)
1671 return r;
1672 } else
1673 break;
1674 }
1675
c98811d8
LP
1676 if (arg_and_change_password)
1677 log_info("Synchronizing passwords and encryption keys.");
1678
1679 (void) home_record_reset_human_interaction_permission(hr);
1680
4aa0a8ac
LP
1681 /* Also sync down passwords to underlying LUKS/fscrypt */
1682 while (arg_and_change_password) {
1683 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1684 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1685
cc9886bc 1686 r = bus_message_new_method_call(bus, &m, bus_mgr, "ChangePasswordHome");
4aa0a8ac
LP
1687 if (r < 0)
1688 return bus_log_create_error(r);
1689
1690 /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
1691 r = sd_bus_message_append(m, "ss", hr->user_name, "{}");
1692 if (r < 0)
1693 return bus_log_create_error(r);
1694
1695 r = bus_message_append_secret(m, hr);
1696 if (r < 0)
1697 return bus_log_create_error(r);
1698
1699 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1700 if (r < 0) {
1701 if (sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
1702 return log_error_errno(r, "Security token not inserted, refusing.");
1703
1704 r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
1705 if (r < 0)
1706 return r;
1707 } else
1708 break;
1709 }
1710
1711 return 0;
1712}
1713
1714static int passwd_home(int argc, char *argv[], void *userdata) {
1715 _cleanup_(user_record_unrefp) UserRecord *old_secret = NULL, *new_secret = NULL;
1716 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1717 _cleanup_free_ char *buffer = NULL;
1718 const char *username;
1719 int r;
1720
1721 if (arg_pkcs11_token_uri)
28e5e1e9
DT
1722 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1723 "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=%s'.",
1724 special_glyph(SPECIAL_GLYPH_ELLIPSIS));
1c0c4a43 1725 if (arg_fido2_device)
28e5e1e9
DT
1726 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1727 "To change the FIDO2 security token use 'homectl update --fido2-device=%s'.",
1728 special_glyph(SPECIAL_GLYPH_ELLIPSIS));
4aa0a8ac
LP
1729 if (identity_properties_specified())
1730 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The 'passwd' verb does not permit changing other record properties at the same time.");
1731
1732 if (argc >= 2)
1733 username = argv[1];
1734 else {
1735 buffer = getusername_malloc();
1736 if (!buffer)
1737 return log_oom();
1738
1739 username = buffer;
1740 }
1741
1742 r = acquire_bus(&bus);
1743 if (r < 0)
1744 return r;
1745
1746 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1747
6b356f44
LP
1748 r = acquire_passed_secrets(username, &old_secret);
1749 if (r < 0)
1750 return r;
4aa0a8ac
LP
1751
1752 new_secret = user_record_new();
1753 if (!new_secret)
1754 return log_oom();
1755
80c41552 1756 r = acquire_new_password(username, new_secret, /* suggest = */ true, NULL);
4aa0a8ac
LP
1757 if (r < 0)
1758 return r;
1759
1760 for (;;) {
1761 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1762 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1763
cc9886bc 1764 r = bus_message_new_method_call(bus, &m, bus_mgr, "ChangePasswordHome");
4aa0a8ac
LP
1765 if (r < 0)
1766 return bus_log_create_error(r);
1767
1768 r = sd_bus_message_append(m, "s", username);
1769 if (r < 0)
1770 return bus_log_create_error(r);
1771
1772 r = bus_message_append_secret(m, new_secret);
1773 if (r < 0)
1774 return bus_log_create_error(r);
1775
1776 r = bus_message_append_secret(m, old_secret);
1777 if (r < 0)
1778 return bus_log_create_error(r);
1779
1780 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1781 if (r < 0) {
1782 if (sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY)) {
1783
1784 log_error_errno(r, "%s", bus_error_message(&error, r));
1785
80c41552 1786 r = acquire_new_password(username, new_secret, /* suggest = */ false, NULL);
4aa0a8ac
LP
1787
1788 } else if (sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
1789
1790 /* In the generic handler we'd ask for a password in this case, but when
1791 * changing passwords that's not sufficeint, as we need to acquire all keys
1792 * first. */
1793 return log_error_errno(r, "Security token not inserted, refusing.");
1794 else
1795 r = handle_generic_user_record_error(username, old_secret, &error, r, true);
1796 if (r < 0)
1797 return r;
1798 } else
1799 break;
1800 }
1801
1802 return 0;
1803}
1804
9f5827e0
LP
1805static int parse_disk_size(const char *t, uint64_t *ret) {
1806 int r;
1807
1808 assert(t);
1809 assert(ret);
1810
1811 if (streq(t, "min"))
1812 *ret = 0;
1813 else if (streq(t, "max"))
1814 *ret = UINT64_MAX-1; /* Largest size that isn't UINT64_MAX special marker */
1815 else {
1816 uint64_t ds;
1817
1818 r = parse_size(t, 1024, &ds);
1819 if (r < 0)
1820 return log_error_errno(r, "Failed to parse disk size parameter: %s", t);
1821
1822 if (ds >= UINT64_MAX) /* UINT64_MAX has special meaning for us ("dont change"), refuse */
1823 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Disk size out of range: %s", t);
1824
1825 *ret = ds;
1826 }
1827
1828 return 0;
1829}
1830
4aa0a8ac
LP
1831static int resize_home(int argc, char *argv[], void *userdata) {
1832 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1833 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
1834 uint64_t ds = UINT64_MAX;
1835 int r;
1836
1837 r = acquire_bus(&bus);
1838 if (r < 0)
1839 return r;
1840
1841 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1842
1843 if (arg_disk_size_relative != UINT64_MAX ||
fe845b5e 1844 (argc > 2 && parse_permyriad(argv[2]) >= 0))
4aa0a8ac
LP
1845 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1846 "Relative disk size specification currently not supported when resizing.");
1847
1848 if (argc > 2) {
9f5827e0 1849 r = parse_disk_size(argv[2], &ds);
4aa0a8ac 1850 if (r < 0)
9f5827e0 1851 return r;
4aa0a8ac
LP
1852 }
1853
1854 if (arg_disk_size != UINT64_MAX) {
1855 if (ds != UINT64_MAX && ds != arg_disk_size)
1856 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Disk size specified twice and doesn't match, refusing.");
1857
1858 ds = arg_disk_size;
1859 }
1860
57bb9bcb
LP
1861 r = acquire_passed_secrets(argv[1], &secret);
1862 if (r < 0)
1863 return r;
4aa0a8ac
LP
1864
1865 for (;;) {
1866 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1867 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1868
cc9886bc 1869 r = bus_message_new_method_call(bus, &m, bus_mgr, "ResizeHome");
4aa0a8ac
LP
1870 if (r < 0)
1871 return bus_log_create_error(r);
1872
1873 r = sd_bus_message_append(m, "st", argv[1], ds);
1874 if (r < 0)
1875 return bus_log_create_error(r);
1876
1877 r = bus_message_append_secret(m, secret);
1878 if (r < 0)
1879 return bus_log_create_error(r);
1880
1881 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1882 if (r < 0) {
1883 r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
1884 if (r < 0)
1885 return r;
1886 } else
1887 break;
1888 }
1889
1890 return 0;
1891}
1892
1893static int lock_home(int argc, char *argv[], void *userdata) {
1894 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1895 int r, ret = 0;
4aa0a8ac
LP
1896
1897 r = acquire_bus(&bus);
1898 if (r < 0)
1899 return r;
1900
1901 STRV_FOREACH(i, strv_skip(argv, 1)) {
1902 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1903 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1904
cc9886bc 1905 r = bus_message_new_method_call(bus, &m, bus_mgr, "LockHome");
4aa0a8ac
LP
1906 if (r < 0)
1907 return bus_log_create_error(r);
1908
1909 r = sd_bus_message_append(m, "s", *i);
1910 if (r < 0)
1911 return bus_log_create_error(r);
1912
1913 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1914 if (r < 0) {
1915 log_error_errno(r, "Failed to lock home: %s", bus_error_message(&error, r));
1916 if (ret == 0)
1917 ret = r;
1918 }
1919 }
1920
1921 return ret;
1922}
1923
1924static int unlock_home(int argc, char *argv[], void *userdata) {
1925 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1926 int r, ret = 0;
4aa0a8ac
LP
1927
1928 r = acquire_bus(&bus);
1929 if (r < 0)
1930 return r;
1931
1932 STRV_FOREACH(i, strv_skip(argv, 1)) {
1933 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
1934
57bb9bcb
LP
1935 r = acquire_passed_secrets(*i, &secret);
1936 if (r < 0)
1937 return r;
4aa0a8ac
LP
1938
1939 for (;;) {
1940 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1941 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1942
cc9886bc 1943 r = bus_message_new_method_call(bus, &m, bus_mgr, "UnlockHome");
4aa0a8ac
LP
1944 if (r < 0)
1945 return bus_log_create_error(r);
1946
1947 r = sd_bus_message_append(m, "s", *i);
1948 if (r < 0)
1949 return bus_log_create_error(r);
1950
1951 r = bus_message_append_secret(m, secret);
1952 if (r < 0)
1953 return bus_log_create_error(r);
1954
1955 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1956 if (r < 0) {
1957 r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
1958 if (r < 0) {
1959 if (ret == 0)
1960 ret = r;
1961
1962 break;
1963 }
1964 } else
1965 break;
1966 }
1967 }
1968
1969 return ret;
1970}
1971
1972static int with_home(int argc, char *argv[], void *userdata) {
1973 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1974 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
1975 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1976 _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
254d1313 1977 _cleanup_close_ int acquired_fd = -EBADF;
4aa0a8ac
LP
1978 _cleanup_strv_free_ char **cmdline = NULL;
1979 const char *home;
1980 int r, ret;
1981 pid_t pid;
1982
1983 r = acquire_bus(&bus);
1984 if (r < 0)
1985 return r;
1986
1987 if (argc < 3) {
1988 _cleanup_free_ char *shell = NULL;
1989
1990 /* If no command is specified, spawn a shell */
1991 r = get_shell(&shell);
1992 if (r < 0)
1993 return log_error_errno(r, "Failed to acquire shell: %m");
1994
1995 cmdline = strv_new(shell);
1996 } else
1997 cmdline = strv_copy(argv + 2);
1998 if (!cmdline)
1999 return log_oom();
2000
57bb9bcb
LP
2001 r = acquire_passed_secrets(argv[1], &secret);
2002 if (r < 0)
2003 return r;
4aa0a8ac
LP
2004
2005 for (;;) {
cc9886bc 2006 r = bus_message_new_method_call(bus, &m, bus_mgr, "AcquireHome");
4aa0a8ac
LP
2007 if (r < 0)
2008 return bus_log_create_error(r);
2009
2010 r = sd_bus_message_append(m, "s", argv[1]);
2011 if (r < 0)
2012 return bus_log_create_error(r);
2013
2014 r = bus_message_append_secret(m, secret);
2015 if (r < 0)
2016 return bus_log_create_error(r);
2017
2018 r = sd_bus_message_append(m, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
2019 if (r < 0)
2020 return bus_log_create_error(r);
2021
2022 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
2023 m = sd_bus_message_unref(m);
2024 if (r < 0) {
2025 r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
2026 if (r < 0)
2027 return r;
2028
2029 sd_bus_error_free(&error);
2030 } else {
2031 int fd;
2032
2033 r = sd_bus_message_read(reply, "h", &fd);
2034 if (r < 0)
2035 return bus_log_parse_error(r);
2036
2037 acquired_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
2038 if (acquired_fd < 0)
2039 return log_error_errno(errno, "Failed to duplicate acquired fd: %m");
2040
2041 reply = sd_bus_message_unref(reply);
2042 break;
2043 }
2044 }
2045
cc9886bc 2046 r = bus_call_method(bus, bus_mgr, "GetHomeByName", &error, &reply, "s", argv[1]);
4aa0a8ac
LP
2047 if (r < 0)
2048 return log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
2049
2050 r = sd_bus_message_read(reply, "usussso", NULL, NULL, NULL, NULL, &home, NULL, NULL);
2051 if (r < 0)
2052 return bus_log_parse_error(r);
2053
e9ccae31 2054 r = safe_fork("(with)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REOPEN_LOG, &pid);
4aa0a8ac
LP
2055 if (r < 0)
2056 return r;
2057 if (r == 0) {
2058 if (chdir(home) < 0) {
2059 log_error_errno(errno, "Failed to change to directory %s: %m", home);
2060 _exit(255);
2061 }
2062
2063 execvp(cmdline[0], cmdline);
2064 log_error_errno(errno, "Failed to execute %s: %m", cmdline[0]);
2065 _exit(255);
2066 }
2067
2068 ret = wait_for_terminate_and_check(cmdline[0], pid, WAIT_LOG_ABNORMAL);
2069
2070 /* Close the fd that pings the home now. */
2071 acquired_fd = safe_close(acquired_fd);
2072
cc9886bc 2073 r = bus_message_new_method_call(bus, &m, bus_mgr, "ReleaseHome");
4aa0a8ac
LP
2074 if (r < 0)
2075 return bus_log_create_error(r);
2076
2077 r = sd_bus_message_append(m, "s", argv[1]);
2078 if (r < 0)
2079 return bus_log_create_error(r);
2080
2081 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2082 if (r < 0) {
2083 if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_BUSY))
2084 log_notice("Not deactivating home directory of %s, as it is still used.", argv[1]);
2085 else
2086 return log_error_errno(r, "Failed to release user home: %s", bus_error_message(&error, r));
2087 }
2088
2089 return ret;
2090}
2091
2092static int lock_all_homes(int argc, char *argv[], void *userdata) {
2093 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2094 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2095 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2096 int r;
2097
2098 r = acquire_bus(&bus);
2099 if (r < 0)
2100 return r;
2101
cc9886bc 2102 r = bus_message_new_method_call(bus, &m, bus_mgr, "LockAllHomes");
4aa0a8ac
LP
2103 if (r < 0)
2104 return bus_log_create_error(r);
2105
2106 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2107 if (r < 0)
d1f6e01e
LP
2108 return log_error_errno(r, "Failed to lock all homes: %s", bus_error_message(&error, r));
2109
2110 return 0;
2111}
2112
2113static int deactivate_all_homes(int argc, char *argv[], void *userdata) {
2114 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2115 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2116 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2117 int r;
2118
2119 r = acquire_bus(&bus);
2120 if (r < 0)
2121 return r;
2122
2123 r = bus_message_new_method_call(bus, &m, bus_mgr, "DeactivateAllHomes");
2124 if (r < 0)
2125 return bus_log_create_error(r);
2126
2127 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2128 if (r < 0)
2129 return log_error_errno(r, "Failed to deactivate all homes: %s", bus_error_message(&error, r));
4aa0a8ac
LP
2130
2131 return 0;
2132}
2133
6d6d4459
LP
2134static int rebalance(int argc, char *argv[], void *userdata) {
2135 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2136 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2137 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2138 int r;
2139
2140 r = acquire_bus(&bus);
2141 if (r < 0)
2142 return r;
2143
2144 r = bus_message_new_method_call(bus, &m, bus_mgr, "Rebalance");
2145 if (r < 0)
2146 return bus_log_create_error(r);
2147
2148 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
2149 if (r < 0) {
2150 if (sd_bus_error_has_name(&error, BUS_ERROR_REBALANCE_NOT_NEEDED))
2151 log_info("No homes needed rebalancing.");
2152 else
2153 return log_error_errno(r, "Failed to rebalance: %s", bus_error_message(&error, r));
2154 } else
2155 log_info("Completed rebalancing.");
2156
2157 return 0;
2158}
2159
3ccadbce
LP
2160static int create_from_credentials(void) {
2161 _cleanup_close_ int fd = -EBADF;
2162 int ret = 0, n_created = 0, r;
2163
2164 fd = open_credentials_dir();
2165 if (IN_SET(fd, -ENXIO, -ENOENT)) /* Credential env var not set, or dir doesn't exist. */
2166 return 0;
2167 if (fd < 0)
2168 return log_error_errno(fd, "Failed to open credentials directory: %m");
2169
2170 _cleanup_free_ DirectoryEntries *des = NULL;
2171 r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
2172 if (r < 0)
2173 return log_error_errno(r, "Failed to enumerate credentials: %m");
2174
2175 FOREACH_ARRAY(i, des->entries, des->n_entries) {
2176 _cleanup_(json_variant_unrefp) JsonVariant *identity = NULL;
2177 struct dirent *de = *i;
2178 const char *e;
2179
2180 if (de->d_type != DT_REG)
2181 continue;
2182
2183 e = startswith(de->d_name, "home.create.");
2184 if (!e)
2185 continue;
2186
2187 if (!valid_user_group_name(e, 0)) {
2188 log_notice("Skipping over credential with name that is not a suitable user name: %s", de->d_name);
2189 continue;
2190 }
2191
2192 r = json_parse_file_at(
2193 /* f= */ NULL,
2194 fd,
2195 de->d_name,
2196 /* flags= */ 0,
2197 &identity,
2198 /* ret_line= */ NULL,
2199 /* ret_column= */ NULL);
2200 if (r < 0) {
2201 log_warning_errno(r, "Failed to parse user record in credential '%s', ignoring: %m", de->d_name);
2202 continue;
2203 }
2204
2205 JsonVariant *un;
2206 un = json_variant_by_key(identity, "userName");
2207 if (un) {
2208 if (!json_variant_is_string(un)) {
2209 log_warning("User record from credential '%s' contains 'userName' field of invalid type, ignoring.", de->d_name);
2210 continue;
2211 }
2212
2213 if (!streq(json_variant_string(un), e)) {
2214 log_warning("User record from credential '%s' contains 'userName' field (%s) that doesn't match credential name (%s), ignoring.", de->d_name, json_variant_string(un), e);
2215 continue;
2216 }
2217 } else {
2218 r = json_variant_set_field_string(&identity, "userName", e);
2219 if (r < 0)
2220 return log_warning_errno(r, "Failed to set userName field: %m");
2221 }
2222
2223 log_notice("Processing user '%s' from credentials.", e);
2224
2225 r = create_home_common(identity);
2226 if (r >= 0)
2227 n_created++;
2228
2229 RET_GATHER(ret, r);
2230 }
2231
2232 return ret < 0 ? ret : n_created;
2233}
2234
2235static int has_regular_user(void) {
2236 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
2237 int r;
2238
2239 r = userdb_all(USERDB_SUPPRESS_SHADOW, &iterator);
2240 if (r < 0)
2241 return log_error_errno(r, "Failed to create user enumerator: %m");
2242
2243 for (;;) {
2244 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
2245
2246 r = userdb_iterator_get(iterator, &ur);
2247 if (r == -ESRCH)
2248 break;
2249 if (r < 0)
2250 return log_error_errno(r, "Failed to enumerate users: %m");
2251
2252 if (user_record_disposition(ur) == USER_REGULAR)
2253 return true;
2254 }
2255
2256 return false;
2257}
2258
2259static int create_interactively(void) {
2260 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2261 _cleanup_free_ char *username = NULL;
2262 int r;
2263
2264 if (!arg_prompt_new_user) {
2265 log_debug("Prompting for user creation was not requested.");
2266 return 0;
2267 }
2268
2269 r = acquire_bus(&bus);
2270 if (r < 0)
2271 return r;
2272
2273 (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
2274
2275 (void) reset_terminal_fd(STDIN_FILENO, /* switch_to_text= */ false);
2276
2277 for (;;) {
2278 username = mfree(username);
2279
2280 r = ask_string(&username,
2281 "%s Please enter user name to create (empty to skip): ",
2282 special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
2283 if (r < 0)
2284 return log_error_errno(r, "Failed to query user for username: %m");
2285
2286 if (isempty(username)) {
2287 log_info("No data entered, skipping.");
2288 return 0;
2289 }
2290
2291 if (!valid_user_group_name(username, /* flags= */ 0)) {
2292 log_notice("Specified user name is not a valid UNIX user name, try again: %s", username);
2293 continue;
2294 }
2295
2296 r = userdb_by_name(username, USERDB_SUPPRESS_SHADOW, /* ret= */ NULL);
2297 if (r == -ESRCH)
2298 break;
2299 if (r < 0)
2300 return log_error_errno(r, "Failed to check if specified user '%s' already exists: %m", username);
2301
2302 log_notice("Specified user '%s' exists already, try again.", username);
2303 }
2304
2305 r = json_variant_set_field_string(&arg_identity_extra, "userName", username);
2306 if (r < 0)
2307 return log_error_errno(r, "Failed to set userName field: %m");
2308
2309 return create_home_common(/* input= */ NULL);
2310}
2311
2312static int verb_firstboot(int argc, char *argv[], void *userdata) {
2313 int r;
2314
2315 /* Let's honour the systemd.firstboot kernel command line option, just like the systemd-firstboot
2316 * tool. */
2317
2318 bool enabled;
2319 r = proc_cmdline_get_bool("systemd.firstboot", /* flags = */ 0, &enabled);
2320 if (r < 0)
2321 return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
2322 if (r > 0 && !enabled) {
2323 log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
2324 arg_prompt_new_user = false;
2325 }
2326
2327 r = create_from_credentials();
2328 if (r < 0)
2329 return r;
2330 if (r > 0) /* Already created users from credentials */
2331 return 0;
2332
2333 r = has_regular_user();
2334 if (r < 0)
2335 return r;
2336 if (r > 0) {
2337 log_info("Regular user already present in user database, skipping user creation.");
2338 return 0;
2339 }
2340
2341 return create_interactively();
2342}
2343
4aa0a8ac
LP
2344static int drop_from_identity(const char *field) {
2345 int r;
2346
2347 assert(field);
2348
2349 /* If we are called to update an identity record and drop some field, let's keep track of what to
2350 * remove from the old record */
2351 r = strv_extend(&arg_identity_filter, field);
2352 if (r < 0)
2353 return log_oom();
2354
2355 /* Let's also drop the field if it was previously set to a new value on the same command line */
2356 r = json_variant_filter(&arg_identity_extra, STRV_MAKE(field));
2357 if (r < 0)
2358 return log_error_errno(r, "Failed to filter JSON identity data: %m");
2359
2360 r = json_variant_filter(&arg_identity_extra_this_machine, STRV_MAKE(field));
2361 if (r < 0)
2362 return log_error_errno(r, "Failed to filter JSON identity data: %m");
2363
2364 r = json_variant_filter(&arg_identity_extra_privileged, STRV_MAKE(field));
2365 if (r < 0)
2366 return log_error_errno(r, "Failed to filter JSON identity data: %m");
2367
2368 return 0;
2369}
2370
2371static int help(int argc, char *argv[], void *userdata) {
2372 _cleanup_free_ char *link = NULL;
2373 int r;
2374
384c2c32 2375 pager_open(arg_pager_flags);
4aa0a8ac
LP
2376
2377 r = terminal_urlify_man("homectl", "1", &link);
2378 if (r < 0)
2379 return log_oom();
2380
2381 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
2382 "%2$sCreate, manipulate or inspect home directories.%3$s\n"
2383 "\n%4$sCommands:%5$s\n"
4bbafcc3
ZJS
2384 " list List home areas\n"
2385 " activate USER… Activate a home area\n"
2386 " deactivate USER… Deactivate a home area\n"
2387 " inspect USER… Inspect a home area\n"
2388 " authenticate USER… Authenticate a home area\n"
2389 " create USER Create a home area\n"
2390 " remove USER… Remove a home area\n"
2391 " update USER Update a home area\n"
2392 " passwd USER Change password of a home area\n"
2393 " resize USER SIZE Resize a home area\n"
2394 " lock USER… Temporarily lock an active home area\n"
2395 " unlock USER… Unlock a temporarily locked home area\n"
2396 " lock-all Lock all suitable home areas\n"
2397 " deactivate-all Deactivate all active home areas\n"
6d6d4459 2398 " rebalance Rebalance free space between home areas\n"
4bbafcc3 2399 " with USER [COMMAND…] Run shell or command with access to a home area\n"
3ccadbce 2400 " firstboot Run first-boot home area creation wizard\n"
4aa0a8ac 2401 "\n%4$sOptions:%5$s\n"
4bbafcc3
ZJS
2402 " -h --help Show this help\n"
2403 " --version Show package version\n"
2404 " --no-pager Do not pipe output into a pager\n"
2405 " --no-legend Do not show the headers and footers\n"
2406 " --no-ask-password Do not ask for system passwords\n"
2407 " -H --host=[USER@]HOST Operate on remote host\n"
2408 " -M --machine=CONTAINER Operate on local container\n"
2409 " --identity=PATH Read JSON identity from file\n"
2410 " --json=FORMAT Output inspection data in JSON (takes one of\n"
2411 " pretty, short, off)\n"
2412 " -j Equivalent to --json=pretty (on TTY) or\n"
2413 " --json=short (otherwise)\n"
2414 " --export-format= Strip JSON inspection data (full, stripped,\n"
2415 " minimal)\n"
2416 " -E When specified once equals -j --export-format=\n"
2417 " stripped, when specified twice equals\n"
2418 " -j --export-format=minimal\n"
3ccadbce
LP
2419 " --prompt-new-user firstboot: Query user interactively for user\n"
2420 " to create\n"
4aa0a8ac 2421 "\n%4$sGeneral User Record Properties:%5$s\n"
4bbafcc3
ZJS
2422 " -c --real-name=REALNAME Real name for user\n"
2423 " --realm=REALM Realm to create user in\n"
2424 " --email-address=EMAIL Email address for user\n"
2425 " --location=LOCATION Set location of user on earth\n"
2426 " --icon-name=NAME Icon name for user\n"
2427 " -d --home-dir=PATH Home directory\n"
2428 " -u --uid=UID Numeric UID for user\n"
2429 " -G --member-of=GROUP Add user to group\n"
fada2c75
LP
2430 " --capability-bounding-set=CAPS\n"
2431 " Bounding POSIX capability set\n"
2432 " --capability-ambient-set=CAPS\n"
2433 " Ambient POSIX capability set\n"
4bbafcc3
ZJS
2434 " --skel=PATH Skeleton directory to use\n"
2435 " --shell=PATH Shell for account\n"
2436 " --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n"
2437 " --timezone=TIMEZONE Set a time-zone\n"
49e55abb 2438 " --language=LOCALE Set preferred languages\n"
4aa0a8ac 2439 " --ssh-authorized-keys=KEYS\n"
4bbafcc3
ZJS
2440 " Specify SSH public keys\n"
2441 " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
2442 " private key and matching X.509 certificate\n"
2443 " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
2444 " extension\n"
17e7561a 2445 " --fido2-with-client-pin=BOOL\n"
4bbafcc3
ZJS
2446 " Whether to require entering a PIN to unlock the\n"
2447 " account\n"
17e7561a 2448 " --fido2-with-user-presence=BOOL\n"
4bbafcc3
ZJS
2449 " Whether to require user presence to unlock the\n"
2450 " account\n"
17e7561a 2451 " --fido2-with-user-verification=BOOL\n"
4bbafcc3
ZJS
2452 " Whether to require user verification to unlock\n"
2453 " the account\n"
2454 " --recovery-key=BOOL Add a recovery key\n"
2455 "\n%4$sAccount Management User Record Properties:%5$s\n"
2456 " --locked=BOOL Set locked account state\n"
2457 " --not-before=TIMESTAMP Do not allow logins before\n"
2458 " --not-after=TIMESTAMP Do not allow logins after\n"
4aa0a8ac 2459 " --rate-limit-interval=SECS\n"
4bbafcc3 2460 " Login rate-limit interval in seconds\n"
4aa0a8ac 2461 " --rate-limit-burst=NUMBER\n"
4bbafcc3 2462 " Login rate-limit attempts per interval\n"
4aa0a8ac 2463 "\n%4$sPassword Policy User Record Properties:%5$s\n"
4bbafcc3 2464 " --password-hint=HINT Set Password hint\n"
4aa0a8ac 2465 " --enforce-password-policy=BOOL\n"
4bbafcc3
ZJS
2466 " Control whether to enforce system's password\n"
2467 " policy for this user\n"
2468 " -P Same as --enforce-password-password=no\n"
4aa0a8ac 2469 " --password-change-now=BOOL\n"
4bbafcc3 2470 " Require the password to be changed on next login\n"
4aa0a8ac 2471 " --password-change-min=TIME\n"
4bbafcc3 2472 " Require minimum time between password changes\n"
4aa0a8ac 2473 " --password-change-max=TIME\n"
4bbafcc3 2474 " Require maximum time between password changes\n"
4aa0a8ac 2475 " --password-change-warn=TIME\n"
4bbafcc3 2476 " How much time to warn before password expiry\n"
4aa0a8ac 2477 " --password-change-inactive=TIME\n"
4bbafcc3 2478 " How much time to block password after expiry\n"
4aa0a8ac 2479 "\n%4$sResource Management User Record Properties:%5$s\n"
4bbafcc3
ZJS
2480 " --disk-size=BYTES Size to assign the user on disk\n"
2481 " --access-mode=MODE User home directory access mode\n"
2482 " --umask=MODE Umask for user when logging in\n"
2483 " --nice=NICE Nice level for user\n"
4aa0a8ac 2484 " --rlimit=LIMIT=VALUE[:VALUE]\n"
4bbafcc3
ZJS
2485 " Set resource limits\n"
2486 " --tasks-max=MAX Set maximum number of per-user tasks\n"
2487 " --memory-high=BYTES Set high memory threshold in bytes\n"
2488 " --memory-max=BYTES Set maximum memory limit\n"
2489 " --cpu-weight=WEIGHT Set CPU weight\n"
2490 " --io-weight=WEIGHT Set IO weight\n"
4aa0a8ac 2491 "\n%4$sStorage User Record Properties:%5$s\n"
4bbafcc3
ZJS
2492 " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
2493 " subvolume, cifs)\n"
2494 " --image-path=PATH Path to image file/directory\n"
86019efa 2495 " --drop-caches=BOOL Whether to automatically drop caches on logout\n"
4aa0a8ac 2496 "\n%4$sLUKS Storage User Record Properties:%5$s\n"
4bbafcc3
ZJS
2497 " --fs-type=TYPE File system type to use in case of luks\n"
2498 " storage (btrfs, ext4, xfs)\n"
2499 " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
2500 " when activated (mounted)\n"
cba11699 2501 " --luks-offline-discard=BOOL\n"
4bbafcc3
ZJS
2502 " Whether to trim file on logout\n"
2503 " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
2504 " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
4aa0a8ac 2505 " --luks-volume-key-size=BITS\n"
4bbafcc3
ZJS
2506 " Volume key size to use for LUKS encryption\n"
2507 " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
4aa0a8ac 2508 " --luks-pbkdf-hash-algorithm=ALGORITHM\n"
4bbafcc3 2509 " PBKDF hash algorithm to use\n"
4aa0a8ac 2510 " --luks-pbkdf-time-cost=SECS\n"
4bbafcc3 2511 " Time cost for PBKDF in seconds\n"
4aa0a8ac 2512 " --luks-pbkdf-memory-cost=BYTES\n"
4bbafcc3 2513 " Memory cost for PBKDF in bytes\n"
4aa0a8ac 2514 " --luks-pbkdf-parallel-threads=NUMBER\n"
4bbafcc3 2515 " Number of parallel threads for PKBDF\n"
fd83c98e
AD
2516 " --luks-sector-size=BYTES\n"
2517 " Sector size for LUKS encryption in bytes\n"
edf0c907
LP
2518 " --luks-extra-mount-options=OPTIONS\n"
2519 " LUKS extra mount options\n"
ab3b6fcb 2520 " --auto-resize-mode=MODE Automatically grow/shrink home on login/logout\n"
21505c93 2521 " --rebalance-weight=WEIGHT Weight while rebalancing\n"
4aa0a8ac 2522 "\n%4$sMounting User Record Properties:%5$s\n"
4bbafcc3
ZJS
2523 " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
2524 " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
2525 " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
4aa0a8ac 2526 "\n%4$sCIFS User Record Properties:%5$s\n"
4bbafcc3
ZJS
2527 " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
2528 " --cifs-user-name=USER CIFS (Windows) user name\n"
2529 " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
4c2ee5c7
LP
2530 " --cifs-extra-mount-options=OPTIONS\n"
2531 " CIFS (Windows) extra mount options\n"
4aa0a8ac 2532 "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
4bbafcc3
ZJS
2533 " --stop-delay=SECS How long to leave user services running after\n"
2534 " logout\n"
2535 " --kill-processes=BOOL Whether to kill user processes when sessions\n"
2536 " terminate\n"
2537 " --auto-login=BOOL Try to log this user in automatically\n"
bc556335
DDM
2538 "\nSee the %6$s for details.\n",
2539 program_invocation_short_name,
2540 ansi_highlight(),
2541 ansi_normal(),
2542 ansi_underline(),
2543 ansi_normal(),
2544 link);
4aa0a8ac
LP
2545
2546 return 0;
2547}
2548
2549static int parse_argv(int argc, char *argv[]) {
49e55abb 2550 _cleanup_strv_free_ char **arg_languages = NULL;
4aa0a8ac
LP
2551
2552 enum {
2553 ARG_VERSION = 0x100,
2554 ARG_NO_PAGER,
2555 ARG_NO_LEGEND,
2556 ARG_NO_ASK_PASSWORD,
2557 ARG_REALM,
2558 ARG_EMAIL_ADDRESS,
2559 ARG_DISK_SIZE,
2560 ARG_ACCESS_MODE,
2561 ARG_STORAGE,
2562 ARG_FS_TYPE,
2563 ARG_IMAGE_PATH,
2564 ARG_UMASK,
2565 ARG_LUKS_DISCARD,
cba11699 2566 ARG_LUKS_OFFLINE_DISCARD,
4aa0a8ac
LP
2567 ARG_JSON,
2568 ARG_SETENV,
2569 ARG_TIMEZONE,
2570 ARG_LANGUAGE,
2571 ARG_LOCKED,
2572 ARG_SSH_AUTHORIZED_KEYS,
2573 ARG_LOCATION,
2574 ARG_ICON_NAME,
2575 ARG_PASSWORD_HINT,
2576 ARG_NICE,
2577 ARG_RLIMIT,
2578 ARG_NOT_BEFORE,
2579 ARG_NOT_AFTER,
2580 ARG_LUKS_CIPHER,
2581 ARG_LUKS_CIPHER_MODE,
2582 ARG_LUKS_VOLUME_KEY_SIZE,
2583 ARG_NOSUID,
2584 ARG_NODEV,
2585 ARG_NOEXEC,
2586 ARG_CIFS_DOMAIN,
2587 ARG_CIFS_USER_NAME,
2588 ARG_CIFS_SERVICE,
4c2ee5c7 2589 ARG_CIFS_EXTRA_MOUNT_OPTIONS,
4aa0a8ac
LP
2590 ARG_TASKS_MAX,
2591 ARG_MEMORY_HIGH,
2592 ARG_MEMORY_MAX,
2593 ARG_CPU_WEIGHT,
2594 ARG_IO_WEIGHT,
2595 ARG_LUKS_PBKDF_TYPE,
2596 ARG_LUKS_PBKDF_HASH_ALGORITHM,
b04ff66b 2597 ARG_LUKS_PBKDF_FORCE_ITERATIONS,
4aa0a8ac
LP
2598 ARG_LUKS_PBKDF_TIME_COST,
2599 ARG_LUKS_PBKDF_MEMORY_COST,
2600 ARG_LUKS_PBKDF_PARALLEL_THREADS,
fd83c98e 2601 ARG_LUKS_SECTOR_SIZE,
4aa0a8ac
LP
2602 ARG_RATE_LIMIT_INTERVAL,
2603 ARG_RATE_LIMIT_BURST,
2604 ARG_STOP_DELAY,
2605 ARG_KILL_PROCESSES,
2606 ARG_ENFORCE_PASSWORD_POLICY,
2607 ARG_PASSWORD_CHANGE_NOW,
2608 ARG_PASSWORD_CHANGE_MIN,
2609 ARG_PASSWORD_CHANGE_MAX,
2610 ARG_PASSWORD_CHANGE_WARN,
2611 ARG_PASSWORD_CHANGE_INACTIVE,
2612 ARG_EXPORT_FORMAT,
2613 ARG_AUTO_LOGIN,
2614 ARG_PKCS11_TOKEN_URI,
1c0c4a43 2615 ARG_FIDO2_DEVICE,
17e7561a
LP
2616 ARG_FIDO2_WITH_PIN,
2617 ARG_FIDO2_WITH_UP,
2618 ARG_FIDO2_WITH_UV,
80c41552 2619 ARG_RECOVERY_KEY,
4aa0a8ac
LP
2620 ARG_AND_RESIZE,
2621 ARG_AND_CHANGE_PASSWORD,
86019efa 2622 ARG_DROP_CACHES,
edf0c907 2623 ARG_LUKS_EXTRA_MOUNT_OPTIONS,
ab3b6fcb 2624 ARG_AUTO_RESIZE_MODE,
21505c93 2625 ARG_REBALANCE_WEIGHT,
70e723c0 2626 ARG_FIDO2_CRED_ALG,
fada2c75
LP
2627 ARG_CAPABILITY_BOUNDING_SET,
2628 ARG_CAPABILITY_AMBIENT_SET,
3ccadbce 2629 ARG_PROMPT_NEW_USER,
4aa0a8ac
LP
2630 };
2631
2632 static const struct option options[] = {
2633 { "help", no_argument, NULL, 'h' },
2634 { "version", no_argument, NULL, ARG_VERSION },
2635 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2636 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2637 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2638 { "host", required_argument, NULL, 'H' },
2639 { "machine", required_argument, NULL, 'M' },
2640 { "identity", required_argument, NULL, 'I' },
2641 { "real-name", required_argument, NULL, 'c' },
2642 { "comment", required_argument, NULL, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
2643 { "realm", required_argument, NULL, ARG_REALM },
2644 { "email-address", required_argument, NULL, ARG_EMAIL_ADDRESS },
2645 { "location", required_argument, NULL, ARG_LOCATION },
2646 { "password-hint", required_argument, NULL, ARG_PASSWORD_HINT },
2647 { "icon-name", required_argument, NULL, ARG_ICON_NAME },
2648 { "home-dir", required_argument, NULL, 'd' }, /* Compatible with useradd(8) */
2649 { "uid", required_argument, NULL, 'u' }, /* Compatible with useradd(8) */
2650 { "member-of", required_argument, NULL, 'G' },
2651 { "groups", required_argument, NULL, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
2652 { "skel", required_argument, NULL, 'k' }, /* Compatible with useradd(8) */
2653 { "shell", required_argument, NULL, 's' }, /* Compatible with useradd(8) */
2654 { "setenv", required_argument, NULL, ARG_SETENV },
2655 { "timezone", required_argument, NULL, ARG_TIMEZONE },
2656 { "language", required_argument, NULL, ARG_LANGUAGE },
2657 { "locked", required_argument, NULL, ARG_LOCKED },
2658 { "not-before", required_argument, NULL, ARG_NOT_BEFORE },
2659 { "not-after", required_argument, NULL, ARG_NOT_AFTER },
2660 { "expiredate", required_argument, NULL, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
2661 { "ssh-authorized-keys", required_argument, NULL, ARG_SSH_AUTHORIZED_KEYS },
2662 { "disk-size", required_argument, NULL, ARG_DISK_SIZE },
2663 { "access-mode", required_argument, NULL, ARG_ACCESS_MODE },
2664 { "umask", required_argument, NULL, ARG_UMASK },
2665 { "nice", required_argument, NULL, ARG_NICE },
2666 { "rlimit", required_argument, NULL, ARG_RLIMIT },
2667 { "tasks-max", required_argument, NULL, ARG_TASKS_MAX },
2668 { "memory-high", required_argument, NULL, ARG_MEMORY_HIGH },
2669 { "memory-max", required_argument, NULL, ARG_MEMORY_MAX },
2670 { "cpu-weight", required_argument, NULL, ARG_CPU_WEIGHT },
2671 { "io-weight", required_argument, NULL, ARG_IO_WEIGHT },
2672 { "storage", required_argument, NULL, ARG_STORAGE },
2673 { "image-path", required_argument, NULL, ARG_IMAGE_PATH },
2674 { "fs-type", required_argument, NULL, ARG_FS_TYPE },
2675 { "luks-discard", required_argument, NULL, ARG_LUKS_DISCARD },
cba11699 2676 { "luks-offline-discard", required_argument, NULL, ARG_LUKS_OFFLINE_DISCARD },
4aa0a8ac
LP
2677 { "luks-cipher", required_argument, NULL, ARG_LUKS_CIPHER },
2678 { "luks-cipher-mode", required_argument, NULL, ARG_LUKS_CIPHER_MODE },
2679 { "luks-volume-key-size", required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE },
2680 { "luks-pbkdf-type", required_argument, NULL, ARG_LUKS_PBKDF_TYPE },
2681 { "luks-pbkdf-hash-algorithm", required_argument, NULL, ARG_LUKS_PBKDF_HASH_ALGORITHM },
b04ff66b 2682 { "luks-pbkdf-force-iterations", required_argument, NULL, ARG_LUKS_PBKDF_FORCE_ITERATIONS },
4aa0a8ac
LP
2683 { "luks-pbkdf-time-cost", required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST },
2684 { "luks-pbkdf-memory-cost", required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST },
2685 { "luks-pbkdf-parallel-threads", required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS },
fd83c98e 2686 { "luks-sector-size", required_argument, NULL, ARG_LUKS_SECTOR_SIZE },
4aa0a8ac
LP
2687 { "nosuid", required_argument, NULL, ARG_NOSUID },
2688 { "nodev", required_argument, NULL, ARG_NODEV },
2689 { "noexec", required_argument, NULL, ARG_NOEXEC },
2690 { "cifs-user-name", required_argument, NULL, ARG_CIFS_USER_NAME },
2691 { "cifs-domain", required_argument, NULL, ARG_CIFS_DOMAIN },
2692 { "cifs-service", required_argument, NULL, ARG_CIFS_SERVICE },
4c2ee5c7 2693 { "cifs-extra-mount-options", required_argument, NULL, ARG_CIFS_EXTRA_MOUNT_OPTIONS },
4aa0a8ac
LP
2694 { "rate-limit-interval", required_argument, NULL, ARG_RATE_LIMIT_INTERVAL },
2695 { "rate-limit-burst", required_argument, NULL, ARG_RATE_LIMIT_BURST },
2696 { "stop-delay", required_argument, NULL, ARG_STOP_DELAY },
2697 { "kill-processes", required_argument, NULL, ARG_KILL_PROCESSES },
2698 { "enforce-password-policy", required_argument, NULL, ARG_ENFORCE_PASSWORD_POLICY },
2699 { "password-change-now", required_argument, NULL, ARG_PASSWORD_CHANGE_NOW },
2700 { "password-change-min", required_argument, NULL, ARG_PASSWORD_CHANGE_MIN },
2701 { "password-change-max", required_argument, NULL, ARG_PASSWORD_CHANGE_MAX },
2702 { "password-change-warn", required_argument, NULL, ARG_PASSWORD_CHANGE_WARN },
2703 { "password-change-inactive", required_argument, NULL, ARG_PASSWORD_CHANGE_INACTIVE },
2704 { "auto-login", required_argument, NULL, ARG_AUTO_LOGIN },
2705 { "json", required_argument, NULL, ARG_JSON },
2706 { "export-format", required_argument, NULL, ARG_EXPORT_FORMAT },
2707 { "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
70e723c0 2708 { "fido2-credential-algorithm", required_argument, NULL, ARG_FIDO2_CRED_ALG },
1c0c4a43 2709 { "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE },
17e7561a
LP
2710 { "fido2-with-client-pin", required_argument, NULL, ARG_FIDO2_WITH_PIN },
2711 { "fido2-with-user-presence", required_argument, NULL, ARG_FIDO2_WITH_UP },
2712 { "fido2-with-user-verification",required_argument, NULL, ARG_FIDO2_WITH_UV },
80c41552 2713 { "recovery-key", required_argument, NULL, ARG_RECOVERY_KEY },
4aa0a8ac
LP
2714 { "and-resize", required_argument, NULL, ARG_AND_RESIZE },
2715 { "and-change-password", required_argument, NULL, ARG_AND_CHANGE_PASSWORD },
86019efa 2716 { "drop-caches", required_argument, NULL, ARG_DROP_CACHES },
edf0c907 2717 { "luks-extra-mount-options", required_argument, NULL, ARG_LUKS_EXTRA_MOUNT_OPTIONS },
ab3b6fcb 2718 { "auto-resize-mode", required_argument, NULL, ARG_AUTO_RESIZE_MODE },
21505c93 2719 { "rebalance-weight", required_argument, NULL, ARG_REBALANCE_WEIGHT },
fada2c75
LP
2720 { "capability-bounding-set", required_argument, NULL, ARG_CAPABILITY_BOUNDING_SET },
2721 { "capability-ambient-set", required_argument, NULL, ARG_CAPABILITY_AMBIENT_SET },
3ccadbce 2722 { "prompt-new-user", no_argument, NULL, ARG_PROMPT_NEW_USER },
4aa0a8ac
LP
2723 {}
2724 };
2725
2726 int r;
2727
2728 assert(argc >= 0);
2729 assert(argv);
2730
2731 for (;;) {
2732 int c;
2733
2734 c = getopt_long(argc, argv, "hH:M:I:c:d:u:k:s:e:G:jPE", options, NULL);
2735 if (c < 0)
2736 break;
2737
2738 switch (c) {
2739
2740 case 'h':
2741 return help(0, NULL, NULL);
2742
2743 case ARG_VERSION:
2744 return version();
2745
2746 case ARG_NO_PAGER:
2747 arg_pager_flags |= PAGER_DISABLE;
2748 break;
2749
2750 case ARG_NO_LEGEND:
2751 arg_legend = false;
2752 break;
2753
2754 case ARG_NO_ASK_PASSWORD:
2755 arg_ask_password = false;
2756 break;
2757
2758 case 'H':
2759 arg_transport = BUS_TRANSPORT_REMOTE;
2760 arg_host = optarg;
2761 break;
2762
2763 case 'M':
2764 arg_transport = BUS_TRANSPORT_MACHINE;
2765 arg_host = optarg;
2766 break;
2767
2768 case 'I':
2769 arg_identity = optarg;
2770 break;
2771
2772 case 'c':
2773 if (isempty(optarg)) {
2774 r = drop_from_identity("realName");
2775 if (r < 0)
2776 return r;
2777
2778 break;
2779 }
2780
2781 if (!valid_gecos(optarg))
2782 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Real name '%s' not a valid GECOS field.", optarg);
2783
2784 r = json_variant_set_field_string(&arg_identity_extra, "realName", optarg);
2785 if (r < 0)
2786 return log_error_errno(r, "Failed to set realName field: %m");
2787
2788 break;
2789
2790 case 'd': {
2791 _cleanup_free_ char *hd = NULL;
2792
2793 if (isempty(optarg)) {
2794 r = drop_from_identity("homeDirectory");
2795 if (r < 0)
2796 return r;
2797
2798 break;
2799 }
2800
614b022c 2801 r = parse_path_argument(optarg, false, &hd);
4aa0a8ac
LP
2802 if (r < 0)
2803 return r;
2804
2805 if (!valid_home(hd))
2806 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Home directory '%s' not valid.", hd);
2807
2808 r = json_variant_set_field_string(&arg_identity_extra, "homeDirectory", hd);
2809 if (r < 0)
2810 return log_error_errno(r, "Failed to set homeDirectory field: %m");
2811
2812 break;
2813 }
2814
2815 case ARG_REALM:
2816 if (isempty(optarg)) {
2817 r = drop_from_identity("realm");
2818 if (r < 0)
2819 return r;
2820
2821 break;
2822 }
2823
2824 r = dns_name_is_valid(optarg);
2825 if (r < 0)
2826 return log_error_errno(r, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg);
2827 if (r == 0)
2828 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Realm '%s' is not a valid DNS domain: %m", optarg);
2829
2830 r = json_variant_set_field_string(&arg_identity_extra, "realm", optarg);
2831 if (r < 0)
2832 return log_error_errno(r, "Failed to set realm field: %m");
2833 break;
2834
2835 case ARG_EMAIL_ADDRESS:
2836 case ARG_LOCATION:
2837 case ARG_ICON_NAME:
2838 case ARG_CIFS_USER_NAME:
4c2ee5c7 2839 case ARG_CIFS_DOMAIN:
edf0c907
LP
2840 case ARG_CIFS_EXTRA_MOUNT_OPTIONS:
2841 case ARG_LUKS_EXTRA_MOUNT_OPTIONS: {
4aa0a8ac
LP
2842
2843 const char *field =
4c2ee5c7
LP
2844 c == ARG_EMAIL_ADDRESS ? "emailAddress" :
2845 c == ARG_LOCATION ? "location" :
2846 c == ARG_ICON_NAME ? "iconName" :
2847 c == ARG_CIFS_USER_NAME ? "cifsUserName" :
2848 c == ARG_CIFS_DOMAIN ? "cifsDomain" :
2849 c == ARG_CIFS_EXTRA_MOUNT_OPTIONS ? "cifsExtraMountOptions" :
edf0c907 2850 c == ARG_LUKS_EXTRA_MOUNT_OPTIONS ? "luksExtraMountOptions" :
4c2ee5c7 2851 NULL;
4aa0a8ac
LP
2852
2853 assert(field);
2854
2855 if (isempty(optarg)) {
2856 r = drop_from_identity(field);
2857 if (r < 0)
2858 return r;
2859
2860 break;
2861 }
2862
2863 r = json_variant_set_field_string(&arg_identity_extra, field, optarg);
2864 if (r < 0)
2865 return log_error_errno(r, "Failed to set %s field: %m", field);
2866
2867 break;
2868 }
2869
16b81da6
LP
2870 case ARG_CIFS_SERVICE:
2871 if (isempty(optarg)) {
2872 r = drop_from_identity("cifsService");
2873 if (r < 0)
2874 return r;
2875
2876 break;
2877 }
2878
2879 r = parse_cifs_service(optarg, NULL, NULL, NULL);
2880 if (r < 0)
2881 return log_error_errno(r, "Failed to validate CIFS service name: %s", optarg);
2882
2883 r = json_variant_set_field_string(&arg_identity_extra, "cifsService", optarg);
2884 if (r < 0)
2885 return log_error_errno(r, "Failed to set cifsService field: %m");
2886
2887 break;
2888
4aa0a8ac
LP
2889 case ARG_PASSWORD_HINT:
2890 if (isempty(optarg)) {
2891 r = drop_from_identity("passwordHint");
2892 if (r < 0)
2893 return r;
2894
2895 break;
2896 }
2897
2898 r = json_variant_set_field_string(&arg_identity_extra_privileged, "passwordHint", optarg);
2899 if (r < 0)
2900 return log_error_errno(r, "Failed to set passwordHint field: %m");
2901
2902 string_erase(optarg);
2903 break;
2904
2905 case ARG_NICE: {
2906 int nc;
2907
2908 if (isempty(optarg)) {
2909 r = drop_from_identity("niceLevel");
2910 if (r < 0)
2911 return r;
2912 break;
2913 }
2914
2915 r = parse_nice(optarg, &nc);
2916 if (r < 0)
2917 return log_error_errno(r, "Failed to parse nice level: %s", optarg);
2918
2919 r = json_variant_set_field_integer(&arg_identity_extra, "niceLevel", nc);
2920 if (r < 0)
2921 return log_error_errno(r, "Failed to set niceLevel field: %m");
2922
2923 break;
2924 }
2925
2926 case ARG_RLIMIT: {
f5fc7732 2927 _cleanup_(json_variant_unrefp) JsonVariant *jcur = NULL, *jmax = NULL;
4aa0a8ac
LP
2928 _cleanup_free_ char *field = NULL, *t = NULL;
2929 const char *eq;
2930 struct rlimit rl;
2931 int l;
2932
2933 if (isempty(optarg)) {
2934 /* Remove all resource limits */
2935
2936 r = drop_from_identity("resourceLimits");
2937 if (r < 0)
2938 return r;
2939
2940 arg_identity_filter_rlimits = strv_free(arg_identity_filter_rlimits);
2941 arg_identity_extra_rlimits = json_variant_unref(arg_identity_extra_rlimits);
2942 break;
2943 }
2944
2945 eq = strchr(optarg, '=');
2946 if (!eq)
2947 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't parse resource limit assignment: %s", optarg);
2948
2949 field = strndup(optarg, eq - optarg);
2950 if (!field)
2951 return log_oom();
2952
2953 l = rlimit_from_string_harder(field);
2954 if (l < 0)
7211c853 2955 return log_error_errno(l, "Unknown resource limit type: %s", field);
4aa0a8ac
LP
2956
2957 if (isempty(eq + 1)) {
2958 /* Remove only the specific rlimit */
2959
2960 r = strv_extend(&arg_identity_filter_rlimits, rlimit_to_string(l));
2961 if (r < 0)
2962 return r;
2963
2964 r = json_variant_filter(&arg_identity_extra_rlimits, STRV_MAKE(field));
2965 if (r < 0)
2966 return log_error_errno(r, "Failed to filter JSON identity data: %m");
2967
2968 break;
2969 }
2970
2971 r = rlimit_parse(l, eq + 1, &rl);
2972 if (r < 0)
2973 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse resource limit value: %s", eq + 1);
2974
2975 r = rl.rlim_cur == RLIM_INFINITY ? json_variant_new_null(&jcur) : json_variant_new_unsigned(&jcur, rl.rlim_cur);
2976 if (r < 0)
2977 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to allocate current integer: %m");
2978
2979 r = rl.rlim_max == RLIM_INFINITY ? json_variant_new_null(&jmax) : json_variant_new_unsigned(&jmax, rl.rlim_max);
2980 if (r < 0)
2981 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to allocate maximum integer: %m");
2982
4aa0a8ac
LP
2983 t = strjoin("RLIMIT_", rlimit_to_string(l));
2984 if (!t)
2985 return log_oom();
2986
f5fc7732
LP
2987 r = json_variant_set_fieldb(
2988 &arg_identity_extra_rlimits, t,
2989 JSON_BUILD_OBJECT(
2990 JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur)),
2991 JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax))));
4aa0a8ac
LP
2992 if (r < 0)
2993 return log_error_errno(r, "Failed to set %s field: %m", rlimit_to_string(l));
2994
2995 break;
2996 }
2997
2998 case 'u': {
2999 uid_t uid;
3000
3001 if (isempty(optarg)) {
3002 r = drop_from_identity("uid");
3003 if (r < 0)
3004 return r;
3005
3006 break;
3007 }
3008
3009 r = parse_uid(optarg, &uid);
3010 if (r < 0)
3011 return log_error_errno(r, "Failed to parse UID '%s'.", optarg);
3012
3013 if (uid_is_system(uid))
3014 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in system range, refusing.", uid);
3015 if (uid_is_dynamic(uid))
3016 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in dynamic range, refusing.", uid);
3017 if (uid == UID_NOBODY)
3018 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is nobody UID, refusing.", uid);
3019
3020 r = json_variant_set_field_unsigned(&arg_identity_extra, "uid", uid);
3021 if (r < 0)
3022 return log_error_errno(r, "Failed to set realm field: %m");
3023
3024 break;
3025 }
3026
3027 case 'k':
3028 case ARG_IMAGE_PATH: {
3029 const char *field = c == 'k' ? "skeletonDirectory" : "imagePath";
3030 _cleanup_free_ char *v = NULL;
3031
3032 if (isempty(optarg)) {
3033 r = drop_from_identity(field);
3034 if (r < 0)
3035 return r;
3036
3037 break;
3038 }
3039
614b022c 3040 r = parse_path_argument(optarg, false, &v);
4aa0a8ac
LP
3041 if (r < 0)
3042 return r;
3043
3044 r = json_variant_set_field_string(&arg_identity_extra_this_machine, field, v);
3045 if (r < 0)
3046 return log_error_errno(r, "Failed to set %s field: %m", v);
3047
3048 break;
3049 }
3050
3051 case 's':
3052 if (isempty(optarg)) {
3053 r = drop_from_identity("shell");
3054 if (r < 0)
3055 return r;
3056
3057 break;
3058 }
3059
3060 if (!valid_shell(optarg))
3061 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Shell '%s' not valid.", optarg);
3062
3063 r = json_variant_set_field_string(&arg_identity_extra, "shell", optarg);
3064 if (r < 0)
3065 return log_error_errno(r, "Failed to set shell field: %m");
3066
3067 break;
3068
3069 case ARG_SETENV: {
aaf057c4 3070 _cleanup_free_ char **l = NULL;
4aa0a8ac
LP
3071 _cleanup_(json_variant_unrefp) JsonVariant *ne = NULL;
3072 JsonVariant *e;
3073
3074 if (isempty(optarg)) {
3075 r = drop_from_identity("environment");
3076 if (r < 0)
3077 return r;
3078
3079 break;
3080 }
3081
4aa0a8ac
LP
3082 e = json_variant_by_key(arg_identity_extra, "environment");
3083 if (e) {
3084 r = json_variant_strv(e, &l);
3085 if (r < 0)
3086 return log_error_errno(r, "Failed to parse JSON environment field: %m");
3087 }
3088
4bbafcc3 3089 r = strv_env_replace_strdup_passthrough(&l, optarg);
aaf057c4 3090 if (r < 0)
4bbafcc3 3091 return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
4aa0a8ac 3092
aaf057c4 3093 strv_sort(l);
4aa0a8ac 3094
aaf057c4 3095 r = json_variant_new_array_strv(&ne, l);
4aa0a8ac
LP
3096 if (r < 0)
3097 return log_error_errno(r, "Failed to allocate environment list JSON: %m");
3098
3099 r = json_variant_set_field(&arg_identity_extra, "environment", ne);
3100 if (r < 0)
162392b7 3101 return log_error_errno(r, "Failed to set environment list: %m");
4aa0a8ac
LP
3102
3103 break;
3104 }
3105
3106 case ARG_TIMEZONE:
3107
3108 if (isempty(optarg)) {
3109 r = drop_from_identity("timeZone");
3110 if (r < 0)
3111 return r;
3112
3113 break;
3114 }
3115
3116 if (!timezone_is_valid(optarg, LOG_DEBUG))
3117 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Timezone '%s' is not valid.", optarg);
3118
3119 r = json_variant_set_field_string(&arg_identity_extra, "timeZone", optarg);
3120 if (r < 0)
3121 return log_error_errno(r, "Failed to set timezone field: %m");
3122
3123 break;
3124
49e55abb
AV
3125 case ARG_LANGUAGE: {
3126 const char *p = optarg;
3127
3128 if (isempty(p)) {
3129 r = drop_from_identity("preferredLanguage");
4aa0a8ac
LP
3130 if (r < 0)
3131 return r;
3132
49e55abb
AV
3133 r = drop_from_identity("additionalLanguages");
3134 if (r < 0)
3135 return r;
3136
3137 arg_languages = strv_free(arg_languages);
4aa0a8ac
LP
3138 break;
3139 }
3140
49e55abb
AV
3141 for (;;) {
3142 _cleanup_free_ char *word = NULL;
4aa0a8ac 3143
49e55abb
AV
3144 r = extract_first_word(&p, &word, ",:", 0);
3145 if (r < 0)
3146 return log_error_errno(r, "Failed to parse locale list: %m");
3147 if (r == 0)
3148 break;
a00a78b8 3149
49e55abb
AV
3150 if (!locale_is_valid(word))
3151 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale '%s' is not valid.", word);
3152
3153 if (locale_is_installed(word) <= 0)
3154 log_warning("Locale '%s' is not installed, accepting anyway.", word);
3155
3156 r = strv_consume(&arg_languages, TAKE_PTR(word));
3157 if (r < 0)
3158 return log_oom();
3159
3160 strv_uniq(arg_languages);
3161 }
4aa0a8ac
LP
3162
3163 break;
49e55abb 3164 }
4aa0a8ac
LP
3165
3166 case ARG_NOSUID:
3167 case ARG_NODEV:
3168 case ARG_NOEXEC:
3169 case ARG_LOCKED:
3170 case ARG_KILL_PROCESSES:
3171 case ARG_ENFORCE_PASSWORD_POLICY:
3172 case ARG_AUTO_LOGIN:
3173 case ARG_PASSWORD_CHANGE_NOW: {
3174 const char *field =
3175 c == ARG_LOCKED ? "locked" :
3176 c == ARG_NOSUID ? "mountNoSuid" :
3177 c == ARG_NODEV ? "mountNoDevices" :
3178 c == ARG_NOEXEC ? "mountNoExecute" :
3179 c == ARG_KILL_PROCESSES ? "killProcesses" :
3180 c == ARG_ENFORCE_PASSWORD_POLICY ? "enforcePasswordPolicy" :
3181 c == ARG_AUTO_LOGIN ? "autoLogin" :
3182 c == ARG_PASSWORD_CHANGE_NOW ? "passwordChangeNow" :
3183 NULL;
3184
3185 assert(field);
3186
3187 if (isempty(optarg)) {
3188 r = drop_from_identity(field);
3189 if (r < 0)
3190 return r;
3191
3192 break;
3193 }
3194
3195 r = parse_boolean(optarg);
3196 if (r < 0)
3197 return log_error_errno(r, "Failed to parse %s boolean: %m", field);
3198
3199 r = json_variant_set_field_boolean(&arg_identity_extra, field, r > 0);
3200 if (r < 0)
3201 return log_error_errno(r, "Failed to set %s field: %m", field);
3202
3203 break;
3204 }
3205
3206 case 'P':
3207 r = json_variant_set_field_boolean(&arg_identity_extra, "enforcePasswordPolicy", false);
3208 if (r < 0)
3209 return log_error_errno(r, "Failed to set enforcePasswordPolicy field: %m");
3210
3211 break;
3212
3213 case ARG_DISK_SIZE:
3214 if (isempty(optarg)) {
21505c93
LP
3215 FOREACH_STRING(prop, "diskSize", "diskSizeRelative", "rebalanceWeight") {
3216 r = drop_from_identity(prop);
3217 if (r < 0)
3218 return r;
3219 }
4aa0a8ac
LP
3220
3221 arg_disk_size = arg_disk_size_relative = UINT64_MAX;
3222 break;
3223 }
3224
fe845b5e 3225 r = parse_permyriad(optarg);
4aa0a8ac 3226 if (r < 0) {
9f5827e0 3227 r = parse_disk_size(optarg, &arg_disk_size);
4aa0a8ac 3228 if (r < 0)
9f5827e0 3229 return r;
4aa0a8ac
LP
3230
3231 r = drop_from_identity("diskSizeRelative");
3232 if (r < 0)
3233 return r;
3234
3235 r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "diskSize", arg_disk_size);
3236 if (r < 0)
3237 return log_error_errno(r, "Failed to set diskSize field: %m");
3238
3239 arg_disk_size_relative = UINT64_MAX;
3240 } else {
3241 /* Normalize to UINT32_MAX == 100% */
9cba32bc 3242 arg_disk_size_relative = UINT32_SCALE_FROM_PERMYRIAD(r);
4aa0a8ac
LP
3243
3244 r = drop_from_identity("diskSize");
3245 if (r < 0)
3246 return r;
3247
3248 r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "diskSizeRelative", arg_disk_size_relative);
3249 if (r < 0)
3250 return log_error_errno(r, "Failed to set diskSizeRelative field: %m");
3251
3252 arg_disk_size = UINT64_MAX;
3253 }
3254
21505c93
LP
3255 /* Automatically turn off the rebalance logic if user configured a size explicitly */
3256 r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "rebalanceWeight", REBALANCE_WEIGHT_OFF);
3257 if (r < 0)
3258 return log_error_errno(r, "Failed to set rebalanceWeight field: %m");
3259
4aa0a8ac
LP
3260 break;
3261
3262 case ARG_ACCESS_MODE: {
3263 mode_t mode;
3264
3265 if (isempty(optarg)) {
3266 r = drop_from_identity("accessMode");
3267 if (r < 0)
3268 return r;
3269
3270 break;
3271 }
3272
3273 r = parse_mode(optarg, &mode);
3274 if (r < 0)
3275 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Access mode '%s' not valid.", optarg);
3276
3277 r = json_variant_set_field_unsigned(&arg_identity_extra, "accessMode", mode);
3278 if (r < 0)
3279 return log_error_errno(r, "Failed to set access mode field: %m");
3280
3281 break;
3282 }
3283
3284 case ARG_LUKS_DISCARD:
3285 if (isempty(optarg)) {
3286 r = drop_from_identity("luksDiscard");
3287 if (r < 0)
3288 return r;
3289
3290 break;
3291 }
3292
3293 r = parse_boolean(optarg);
3294 if (r < 0)
3295 return log_error_errno(r, "Failed to parse --luks-discard= parameter: %s", optarg);
3296
3297 r = json_variant_set_field_boolean(&arg_identity_extra, "luksDiscard", r);
3298 if (r < 0)
3299 return log_error_errno(r, "Failed to set discard field: %m");
3300
3301 break;
3302
cba11699
LP
3303 case ARG_LUKS_OFFLINE_DISCARD:
3304 if (isempty(optarg)) {
3305 r = drop_from_identity("luksOfflineDiscard");
3306 if (r < 0)
3307 return r;
3308
3309 break;
3310 }
3311
3312 r = parse_boolean(optarg);
3313 if (r < 0)
3314 return log_error_errno(r, "Failed to parse --luks-offline-discard= parameter: %s", optarg);
3315
3316 r = json_variant_set_field_boolean(&arg_identity_extra, "luksOfflineDiscard", r);
3317 if (r < 0)
3318 return log_error_errno(r, "Failed to set offline discard field: %m");
3319
3320 break;
3321
4aa0a8ac 3322 case ARG_LUKS_VOLUME_KEY_SIZE:
b04ff66b 3323 case ARG_LUKS_PBKDF_FORCE_ITERATIONS:
4aa0a8ac
LP
3324 case ARG_LUKS_PBKDF_PARALLEL_THREADS:
3325 case ARG_RATE_LIMIT_BURST: {
3326 const char *field =
3327 c == ARG_LUKS_VOLUME_KEY_SIZE ? "luksVolumeKeySize" :
b04ff66b 3328 c == ARG_LUKS_PBKDF_FORCE_ITERATIONS ? "luksPbkdfForceIterations" :
4aa0a8ac
LP
3329 c == ARG_LUKS_PBKDF_PARALLEL_THREADS ? "luksPbkdfParallelThreads" :
3330 c == ARG_RATE_LIMIT_BURST ? "rateLimitBurst" : NULL;
3331 unsigned n;
3332
3333 assert(field);
3334
3335 if (isempty(optarg)) {
3336 r = drop_from_identity(field);
3337 if (r < 0)
3338 return r;
3339 }
3340
3341 r = safe_atou(optarg, &n);
3342 if (r < 0)
3343 return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
3344
3345 r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
3346 if (r < 0)
3347 return log_error_errno(r, "Failed to set %s field: %m", field);
3348
3349 break;
3350 }
3351
fd83c98e
AD
3352 case ARG_LUKS_SECTOR_SIZE: {
3353 uint64_t ss;
3354
3355 if (isempty(optarg)) {
3356 r = drop_from_identity("luksSectorSize");
3357 if (r < 0)
3358 return r;
3359
3360 break;
3361 }
3362
3363 r = parse_sector_size(optarg, &ss);
3364 if (r < 0)
3365 return r;
3366
3367 r = json_variant_set_field_unsigned(&arg_identity_extra, "luksSectorSize", ss);
3368 if (r < 0)
3369 return log_error_errno(r, "Failed to set sector size field: %m");
3370
3371 break;
3372 }
3373
4aa0a8ac
LP
3374 case ARG_UMASK: {
3375 mode_t m;
3376
3377 if (isempty(optarg)) {
3378 r = drop_from_identity("umask");
3379 if (r < 0)
3380 return r;
3381
3382 break;
3383 }
3384
3385 r = parse_mode(optarg, &m);
3386 if (r < 0)
3387 return log_error_errno(r, "Failed to parse umask: %m");
3388
3389 r = json_variant_set_field_integer(&arg_identity_extra, "umask", m);
3390 if (r < 0)
3391 return log_error_errno(r, "Failed to set umask field: %m");
3392
3393 break;
3394 }
3395
3396 case ARG_SSH_AUTHORIZED_KEYS: {
3397 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
5d2a48da 3398 _cleanup_strv_free_ char **l = NULL, **add = NULL;
4aa0a8ac
LP
3399
3400 if (isempty(optarg)) {
3401 r = drop_from_identity("sshAuthorizedKeys");
3402 if (r < 0)
3403 return r;
3404
3405 break;
3406 }
3407
3408 if (optarg[0] == '@') {
3409 _cleanup_fclose_ FILE *f = NULL;
3410
3411 /* If prefixed with '@' read from a file */
3412
3413 f = fopen(optarg+1, "re");
3414 if (!f)
3415 return log_error_errno(errno, "Failed to open '%s': %m", optarg+1);
3416
3417 for (;;) {
3418 _cleanup_free_ char *line = NULL;
3419
3420 r = read_line(f, LONG_LINE_MAX, &line);
3421 if (r < 0)
80ace4f2 3422 return log_error_errno(r, "Failed to read from '%s': %m", optarg+1);
4aa0a8ac
LP
3423 if (r == 0)
3424 break;
3425
3426 if (isempty(line))
3427 continue;
3428
3429 if (line[0] == '#')
3430 continue;
3431
3432 r = strv_consume(&add, TAKE_PTR(line));
3433 if (r < 0)
3434 return log_oom();
3435 }
3436 } else {
3437 /* Otherwise, assume it's a literal key. Let's do some superficial checks
3438 * before accept it though. */
3439
3440 if (string_has_cc(optarg, NULL))
3441 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Authorized key contains control characters, refusing.");
3442 if (optarg[0] == '#')
3443 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified key is a comment?");
3444
3445 add = strv_new(optarg);
3446 if (!add)
3447 return log_oom();
3448 }
3449
3450 v = json_variant_ref(json_variant_by_key(arg_identity_extra_privileged, "sshAuthorizedKeys"));
3451 if (v) {
3452 r = json_variant_strv(v, &l);
3453 if (r < 0)
3454 return log_error_errno(r, "Failed to parse SSH authorized keys list: %m");
3455 }
3456
3457 r = strv_extend_strv(&l, add, true);
3458 if (r < 0)
3459 return log_oom();
3460
3461 v = json_variant_unref(v);
3462
3463 r = json_variant_new_array_strv(&v, l);
3464 if (r < 0)
3465 return log_oom();
3466
3467 r = json_variant_set_field(&arg_identity_extra_privileged, "sshAuthorizedKeys", v);
3468 if (r < 0)
3469 return log_error_errno(r, "Failed to set authorized keys: %m");
3470
3471 break;
3472 }
3473
3474 case ARG_NOT_BEFORE:
3475 case ARG_NOT_AFTER:
3476 case 'e': {
3477 const char *field;
3478 usec_t n;
3479
3480 field = c == ARG_NOT_BEFORE ? "notBeforeUSec" :
3481 IN_SET(c, ARG_NOT_AFTER, 'e') ? "notAfterUSec" : NULL;
3482
3483 assert(field);
3484
3485 if (isempty(optarg)) {
3486 r = drop_from_identity(field);
3487 if (r < 0)
3488 return r;
3489
3490 break;
3491 }
3492
3493 /* Note the minor discrepancy regarding -e parsing here: we support that for compat
3494 * reasons, and in the original useradd(8) implementation it accepts dates in the
3495 * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
3496 * with greater precision. */
3497 r = parse_timestamp(optarg, &n);
3498 if (r < 0)
3499 return log_error_errno(r, "Failed to parse %s parameter: %m", field);
3500
3501 r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
3502 if (r < 0)
3503 return log_error_errno(r, "Failed to set %s field: %m", field);
3504 break;
3505 }
3506
3507 case ARG_PASSWORD_CHANGE_MIN:
3508 case ARG_PASSWORD_CHANGE_MAX:
3509 case ARG_PASSWORD_CHANGE_WARN:
3510 case ARG_PASSWORD_CHANGE_INACTIVE: {
3511 const char *field;
3512 usec_t n;
3513
3514 field = c == ARG_PASSWORD_CHANGE_MIN ? "passwordChangeMinUSec" :
3515 c == ARG_PASSWORD_CHANGE_MAX ? "passwordChangeMaxUSec" :
3516 c == ARG_PASSWORD_CHANGE_WARN ? "passwordChangeWarnUSec" :
3517 c == ARG_PASSWORD_CHANGE_INACTIVE ? "passwordChangeInactiveUSec" :
3518 NULL;
3519
3520 assert(field);
3521
3522 if (isempty(optarg)) {
3523 r = drop_from_identity(field);
3524 if (r < 0)
3525 return r;
3526
3527 break;
3528 }
3529
3530 r = parse_sec(optarg, &n);
3531 if (r < 0)
3532 return log_error_errno(r, "Failed to parse %s parameter: %m", field);
3533
3534 r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
3535 if (r < 0)
3536 return log_error_errno(r, "Failed to set %s field: %m", field);
3537 break;
3538 }
3539
3540 case ARG_STORAGE:
3541 case ARG_FS_TYPE:
3542 case ARG_LUKS_CIPHER:
3543 case ARG_LUKS_CIPHER_MODE:
3544 case ARG_LUKS_PBKDF_TYPE:
3545 case ARG_LUKS_PBKDF_HASH_ALGORITHM: {
3546
3547 const char *field =
3548 c == ARG_STORAGE ? "storage" :
d900701e 3549 c == ARG_FS_TYPE ? "fileSystemType" :
4aa0a8ac
LP
3550 c == ARG_LUKS_CIPHER ? "luksCipher" :
3551 c == ARG_LUKS_CIPHER_MODE ? "luksCipherMode" :
3552 c == ARG_LUKS_PBKDF_TYPE ? "luksPbkdfType" :
3553 c == ARG_LUKS_PBKDF_HASH_ALGORITHM ? "luksPbkdfHashAlgorithm" : NULL;
3554
3555 assert(field);
3556
3557 if (isempty(optarg)) {
3558 r = drop_from_identity(field);
3559 if (r < 0)
3560 return r;
3561
3562 break;
3563 }
3564
3565 if (!string_is_safe(optarg))
3566 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parameter for %s field not valid: %s", field, optarg);
3567
3568 r = json_variant_set_field_string(
3569 IN_SET(c, ARG_STORAGE, ARG_FS_TYPE) ?
3570 &arg_identity_extra_this_machine :
3571 &arg_identity_extra, field, optarg);
3572 if (r < 0)
3573 return log_error_errno(r, "Failed to set %s field: %m", field);
3574
3575 break;
3576 }
3577
3578 case ARG_LUKS_PBKDF_TIME_COST:
3579 case ARG_RATE_LIMIT_INTERVAL:
3580 case ARG_STOP_DELAY: {
3581 const char *field =
3582 c == ARG_LUKS_PBKDF_TIME_COST ? "luksPbkdfTimeCostUSec" :
3583 c == ARG_RATE_LIMIT_INTERVAL ? "rateLimitIntervalUSec" :
3584 c == ARG_STOP_DELAY ? "stopDelayUSec" :
3585 NULL;
3586 usec_t t;
3587
3588 assert(field);
3589
3590 if (isempty(optarg)) {
3591 r = drop_from_identity(field);
3592 if (r < 0)
3593 return r;
3594
3595 break;
3596 }
3597
3598 r = parse_sec(optarg, &t);
3599 if (r < 0)
3600 return log_error_errno(r, "Failed to parse %s field: %s", field, optarg);
3601
3602 r = json_variant_set_field_unsigned(&arg_identity_extra, field, t);
3603 if (r < 0)
3604 return log_error_errno(r, "Failed to set %s field: %m", field);
3605
3606 break;
3607 }
3608
3609 case 'G': {
3610 const char *p = optarg;
3611
3612 if (isempty(p)) {
3613 r = drop_from_identity("memberOf");
3614 if (r < 0)
3615 return r;
3616
3617 break;
3618 }
3619
3620 for (;;) {
3621 _cleanup_(json_variant_unrefp) JsonVariant *mo = NULL;
3622 _cleanup_strv_free_ char **list = NULL;
3623 _cleanup_free_ char *word = NULL;
3624
3625 r = extract_first_word(&p, &word, ",", 0);
3626 if (r < 0)
3627 return log_error_errno(r, "Failed to parse group list: %m");
3628 if (r == 0)
3629 break;
3630
7a8867ab 3631 if (!valid_user_group_name(word, 0))
4aa0a8ac
LP
3632 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid group name %s.", word);
3633
3634 mo = json_variant_ref(json_variant_by_key(arg_identity_extra, "memberOf"));
3635
3636 r = json_variant_strv(mo, &list);
3637 if (r < 0)
3638 return log_error_errno(r, "Failed to parse group list: %m");
3639
3640 r = strv_extend(&list, word);
3641 if (r < 0)
3642 return log_oom();
3643
3644 strv_sort(list);
3645 strv_uniq(list);
3646
3647 mo = json_variant_unref(mo);
3648 r = json_variant_new_array_strv(&mo, list);
3649 if (r < 0)
3650 return log_error_errno(r, "Failed to create group list JSON: %m");
3651
3652 r = json_variant_set_field(&arg_identity_extra, "memberOf", mo);
3653 if (r < 0)
3654 return log_error_errno(r, "Failed to update group list: %m");
3655 }
3656
3657 break;
3658 }
3659
3660 case ARG_TASKS_MAX: {
3661 uint64_t u;
3662
3663 if (isempty(optarg)) {
3664 r = drop_from_identity("tasksMax");
3665 if (r < 0)
3666 return r;
3667 break;
3668 }
3669
3670 r = safe_atou64(optarg, &u);
3671 if (r < 0)
3672 return log_error_errno(r, "Failed to parse --tasks-max= parameter: %s", optarg);
3673
3674 r = json_variant_set_field_unsigned(&arg_identity_extra, "tasksMax", u);
3675 if (r < 0)
3676 return log_error_errno(r, "Failed to set tasksMax field: %m");
3677
3678 break;
3679 }
3680
3681 case ARG_MEMORY_MAX:
3682 case ARG_MEMORY_HIGH:
3683 case ARG_LUKS_PBKDF_MEMORY_COST: {
3684 const char *field =
3685 c == ARG_MEMORY_MAX ? "memoryMax" :
3686 c == ARG_MEMORY_HIGH ? "memoryHigh" :
3687 c == ARG_LUKS_PBKDF_MEMORY_COST ? "luksPbkdfMemoryCost" : NULL;
3688
3689 uint64_t u;
3690
3691 assert(field);
3692
3693 if (isempty(optarg)) {
3694 r = drop_from_identity(field);
3695 if (r < 0)
3696 return r;
3697 break;
3698 }
3699
3700 r = parse_size(optarg, 1024, &u);
3701 if (r < 0)
3702 return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
3703
3704 r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, field, u);
3705 if (r < 0)
3706 return log_error_errno(r, "Failed to set %s field: %m", field);
3707
3708 break;
3709 }
3710
3711 case ARG_CPU_WEIGHT:
3712 case ARG_IO_WEIGHT: {
3713 const char *field = c == ARG_CPU_WEIGHT ? "cpuWeight" :
3714 c == ARG_IO_WEIGHT ? "ioWeight" : NULL;
3715 uint64_t u;
3716
3717 assert(field);
3718
3719 if (isempty(optarg)) {
3720 r = drop_from_identity(field);
3721 if (r < 0)
3722 return r;
3723 break;
3724 }
3725
3726 r = safe_atou64(optarg, &u);
3727 if (r < 0)
3728 return log_error_errno(r, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg);
3729
3730 if (!CGROUP_WEIGHT_IS_OK(u))
3731 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Weight %" PRIu64 " is out of valid weight range.", u);
3732
3733 r = json_variant_set_field_unsigned(&arg_identity_extra, field, u);
3734 if (r < 0)
3735 return log_error_errno(r, "Failed to set %s field: %m", field);
3736
3737 break;
3738 }
3739
5980d463 3740 case ARG_PKCS11_TOKEN_URI:
0eb3be46 3741 if (streq(optarg, "list"))
f240cbb6 3742 return pkcs11_list_tokens();
0eb3be46 3743
4aa0a8ac
LP
3744 /* If --pkcs11-token-uri= is specified we always drop everything old */
3745 FOREACH_STRING(p, "pkcs11TokenUri", "pkcs11EncryptedKey") {
3746 r = drop_from_identity(p);
3747 if (r < 0)
3748 return r;
3749 }
3750
3751 if (isempty(optarg)) {
3752 arg_pkcs11_token_uri = strv_free(arg_pkcs11_token_uri);
3753 break;
3754 }
3755
0eb3be46
LP
3756 if (streq(optarg, "auto")) {
3757 _cleanup_free_ char *found = NULL;
4aa0a8ac 3758
f240cbb6 3759 r = pkcs11_find_token_auto(&found);
0eb3be46
LP
3760 if (r < 0)
3761 return r;
3762 r = strv_consume(&arg_pkcs11_token_uri, TAKE_PTR(found));
3763 } else {
3764 if (!pkcs11_uri_valid(optarg))
3765 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg);
3766
3767 r = strv_extend(&arg_pkcs11_token_uri, optarg);
3768 }
4aa0a8ac
LP
3769 if (r < 0)
3770 return r;
3771
3772 strv_uniq(arg_pkcs11_token_uri);
3773 break;
1c0c4a43 3774
70e723c0
M
3775 case ARG_FIDO2_CRED_ALG:
3776 r = parse_fido2_algorithm(optarg, &arg_fido2_cred_alg);
3777 if (r < 0)
3778 return log_error_errno(r, "Failed to parse COSE algorithm: %s", optarg);
3779 break;
3780
5980d463 3781 case ARG_FIDO2_DEVICE:
1c0c4a43 3782 if (streq(optarg, "list"))
fb2d839c 3783 return fido2_list_devices();
1c0c4a43
LP
3784
3785 FOREACH_STRING(p, "fido2HmacCredential", "fido2HmacSalt") {
3786 r = drop_from_identity(p);
3787 if (r < 0)
3788 return r;
3789 }
3790
3791 if (isempty(optarg)) {
3792 arg_fido2_device = strv_free(arg_fido2_device);
3793 break;
3794 }
3795
3796 if (streq(optarg, "auto")) {
3797 _cleanup_free_ char *found = NULL;
3798
fb2d839c 3799 r = fido2_find_device_auto(&found);
1c0c4a43
LP
3800 if (r < 0)
3801 return r;
3802
3803 r = strv_consume(&arg_fido2_device, TAKE_PTR(found));
3804 } else
3805 r = strv_extend(&arg_fido2_device, optarg);
1c0c4a43
LP
3806 if (r < 0)
3807 return r;
3808
3809 strv_uniq(arg_fido2_device);
3810 break;
1c0c4a43 3811
51214cf4
ZJS
3812 case ARG_FIDO2_WITH_PIN:
3813 r = parse_boolean_argument("--fido2-with-client-pin=", optarg, NULL);
17e7561a
LP
3814 if (r < 0)
3815 return r;
3816
51214cf4 3817 SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_PIN, r);
17e7561a 3818 break;
17e7561a 3819
51214cf4
ZJS
3820 case ARG_FIDO2_WITH_UP:
3821 r = parse_boolean_argument("--fido2-with-user-presence=", optarg, NULL);
17e7561a
LP
3822 if (r < 0)
3823 return r;
3824
51214cf4 3825 SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UP, r);
17e7561a 3826 break;
17e7561a 3827
51214cf4
ZJS
3828 case ARG_FIDO2_WITH_UV:
3829 r = parse_boolean_argument("--fido2-with-user-verification=", optarg, NULL);
17e7561a
LP
3830 if (r < 0)
3831 return r;
3832
51214cf4 3833 SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UV, r);
17e7561a 3834 break;
17e7561a 3835
5980d463 3836 case ARG_RECOVERY_KEY:
80c41552
LP
3837 r = parse_boolean(optarg);
3838 if (r < 0)
3839 return log_error_errno(r, "Failed to parse --recovery-key= argument: %s", optarg);
3840
3841 arg_recovery_key = r;
3842
3843 FOREACH_STRING(p, "recoveryKey", "recoveryKeyType") {
3844 r = drop_from_identity(p);
3845 if (r < 0)
3846 return r;
3847 }
3848
3849 break;
80c41552 3850
ab3b6fcb
LP
3851 case ARG_AUTO_RESIZE_MODE:
3852 if (isempty(optarg)) {
3853 r = drop_from_identity("autoResizeMode");
3854 if (r < 0)
3855 return r;
3856
3857 break;
3858 }
3859
3860 r = auto_resize_mode_from_string(optarg);
3861 if (r < 0)
3862 return log_error_errno(r, "Failed to parse --auto-resize-mode= argument: %s", optarg);
3863
3864 r = json_variant_set_field_string(&arg_identity_extra, "autoResizeMode", auto_resize_mode_to_string(r));
3865 if (r < 0)
3866 return log_error_errno(r, "Failed to set autoResizeMode field: %m");
3867
3868 break;
3869
21505c93
LP
3870 case ARG_REBALANCE_WEIGHT: {
3871 uint64_t u;
3872
3873 if (isempty(optarg)) {
3874 r = drop_from_identity("rebalanceWeight");
3875 if (r < 0)
3876 return r;
464ec1de 3877 break;
21505c93
LP
3878 }
3879
3880 if (streq(optarg, "off"))
3881 u = REBALANCE_WEIGHT_OFF;
3882 else {
3883 r = safe_atou64(optarg, &u);
3884 if (r < 0)
3885 return log_error_errno(r, "Failed to parse --rebalance-weight= argument: %s", optarg);
3886
3887 if (u < REBALANCE_WEIGHT_MIN || u > REBALANCE_WEIGHT_MAX)
28e5e1e9
DT
3888 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Rebalancing weight out of valid range %" PRIu64 "%s%" PRIu64 ": %s",
3889 REBALANCE_WEIGHT_MIN, special_glyph(SPECIAL_GLYPH_ELLIPSIS), REBALANCE_WEIGHT_MAX, optarg);
21505c93
LP
3890 }
3891
3892 /* Drop from per machine stuff and everywhere */
3893 r = drop_from_identity("rebalanceWeight");
3894 if (r < 0)
3895 return r;
3896
3897 /* Add to main identity */
3898 r = json_variant_set_field_unsigned(&arg_identity_extra, "rebalanceWeight", u);
3899 if (r < 0)
3900 return log_error_errno(r, "Failed to set rebalanceWeight field: %m");
3901
3902 break;
3903 }
3904
4aa0a8ac 3905 case 'j':
4aa0a8ac
LP
3906 arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
3907 break;
3908
3909 case ARG_JSON:
b1e8f46c 3910 r = parse_json_argument(optarg, &arg_json_format_flags);
6a01ea4a
LP
3911 if (r <= 0)
3912 return r;
4aa0a8ac
LP
3913
3914 break;
3915
3916 case 'E':
3917 if (arg_export_format == EXPORT_FORMAT_FULL)
3918 arg_export_format = EXPORT_FORMAT_STRIPPED;
3919 else if (arg_export_format == EXPORT_FORMAT_STRIPPED)
3920 arg_export_format = EXPORT_FORMAT_MINIMAL;
3921 else
3922 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specifying -E more than twice is not supported.");
3923
6a01ea4a 3924 arg_json_format_flags &= ~JSON_FORMAT_OFF;
4aa0a8ac
LP
3925 if (arg_json_format_flags == 0)
3926 arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
3927 break;
3928
3929 case ARG_EXPORT_FORMAT:
3930 if (streq(optarg, "full"))
3931 arg_export_format = EXPORT_FORMAT_FULL;
3932 else if (streq(optarg, "stripped"))
3933 arg_export_format = EXPORT_FORMAT_STRIPPED;
3934 else if (streq(optarg, "minimal"))
3935 arg_export_format = EXPORT_FORMAT_MINIMAL;
3936 else if (streq(optarg, "help")) {
3937 puts("full\n"
3938 "stripped\n"
3939 "minimal");
3940 return 0;
3941 }
3942
3943 break;
3944
3945 case ARG_AND_RESIZE:
3946 arg_and_resize = true;
3947 break;
3948
3949 case ARG_AND_CHANGE_PASSWORD:
3950 arg_and_change_password = true;
3951 break;
3952
86019efa 3953 case ARG_DROP_CACHES: {
86019efa
LP
3954 if (isempty(optarg)) {
3955 r = drop_from_identity("dropCaches");
3956 if (r < 0)
3957 return r;
464ec1de 3958 break;
86019efa
LP
3959 }
3960
51214cf4 3961 r = parse_boolean_argument("--drop-caches=", optarg, NULL);
86019efa
LP
3962 if (r < 0)
3963 return r;
3964
3965 r = json_variant_set_field_boolean(&arg_identity_extra, "dropCaches", r);
3966 if (r < 0)
3967 return log_error_errno(r, "Failed to set drop caches field: %m");
3968
3969 break;
3970 }
3971
fada2c75
LP
3972 case ARG_CAPABILITY_AMBIENT_SET:
3973 case ARG_CAPABILITY_BOUNDING_SET: {
3974 _cleanup_strv_free_ char **l = NULL;
3975 bool subtract = false;
3976 uint64_t parsed, *which, updated;
3977 const char *p, *field;
3978
3979 if (c == ARG_CAPABILITY_AMBIENT_SET) {
3980 which = &arg_capability_ambient_set;
3981 field = "capabilityAmbientSet";
3982 } else {
3983 assert(c == ARG_CAPABILITY_BOUNDING_SET);
3984 which = &arg_capability_bounding_set;
3985 field = "capabilityBoundingSet";
3986 }
3987
3988 if (isempty(optarg)) {
3989 r = drop_from_identity(field);
3990 if (r < 0)
3991 return r;
3992
3993 *which = UINT64_MAX;
3994 break;
3995 }
3996
3997 p = optarg;
3998 if (*p == '~') {
3999 subtract = true;
4000 p++;
4001 }
4002
4003 r = capability_set_from_string(p, &parsed);
4004 if (r == 0)
4005 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid capabilities in capability string '%s'.", p);
4006 if (r < 0)
4007 return log_error_errno(r, "Failed to parse capability string '%s': %m", p);
4008
4009 if (*which == UINT64_MAX)
4010 updated = subtract ? all_capabilities() & ~parsed : parsed;
4011 else if (subtract)
4012 updated = *which & ~parsed;
4013 else
4014 updated = *which | parsed;
4015
4016 if (capability_set_to_strv(updated, &l) < 0)
4017 return log_oom();
4018
4019 r = json_variant_set_field_strv(&arg_identity_extra, field, l);
4020 if (r < 0)
4021 return log_error_errno(r, "Failed to set %s field: %m", field);
4022
4023 *which = updated;
4024 break;
4025 }
4026
3ccadbce
LP
4027 case ARG_PROMPT_NEW_USER:
4028 arg_prompt_new_user = true;
4029 break;
4030
4aa0a8ac
LP
4031 case '?':
4032 return -EINVAL;
4033
4034 default:
04499a70 4035 assert_not_reached();
4aa0a8ac
LP
4036 }
4037 }
4038
1c0c4a43 4039 if (!strv_isempty(arg_pkcs11_token_uri) || !strv_isempty(arg_fido2_device))
4aa0a8ac
LP
4040 arg_and_change_password = true;
4041
4042 if (arg_disk_size != UINT64_MAX || arg_disk_size_relative != UINT64_MAX)
4043 arg_and_resize = true;
4044
49e55abb
AV
4045 if (!strv_isempty(arg_languages)) {
4046 char **additional;
4047
4048 r = json_variant_set_field_string(&arg_identity_extra, "preferredLanguage", arg_languages[0]);
4049 if (r < 0)
4050 return log_error_errno(r, "Failed to update preferred language: %m");
4051
4052 additional = strv_skip(arg_languages, 1);
4053 if (!strv_isempty(additional)) {
4054 r = json_variant_set_field_strv(&arg_identity_extra, "additionalLanguages", additional);
4055 if (r < 0)
4056 return log_error_errno(r, "Failed to update additional language list: %m");
4057 } else {
4058 r = drop_from_identity("additionalLanguages");
4059 if (r < 0)
4060 return r;
4061 }
4062 }
4063
4aa0a8ac
LP
4064 return 1;
4065}
4066
cc9886bc
LP
4067static int redirect_bus_mgr(void) {
4068 const char *suffix;
4069
4070 /* Talk to a different service if that's requested. (The same env var is also understood by homed, so
4071 * that it is relatively easily possible to invoke a second instance of homed for debug purposes and
4072 * have homectl talk to it, without colliding with the host version. This is handy when operating
4073 * from a homed-managed account.) */
4074
4075 suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
4076 if (suffix) {
4077 static BusLocator locator = {
4078 .path = "/org/freedesktop/home1",
4079 .interface = "org.freedesktop.home1.Manager",
4080 };
4081
4082 /* Yes, we leak this memory, but there's little point to collect this, given that we only do
4083 * this in a debug environment, do it only once, and the string shall live for out entire
4084 * process runtime. */
4085
4086 locator.destination = strjoin("org.freedesktop.home1.", suffix);
4087 if (!locator.destination)
4088 return log_oom();
4089
4090 bus_mgr = &locator;
4091 } else
4092 bus_mgr = bus_home_mgr;
4093
4094 return 0;
4095}
4096
4aa0a8ac
LP
4097static int run(int argc, char *argv[]) {
4098 static const Verb verbs[] = {
d1f6e01e
LP
4099 { "help", VERB_ANY, VERB_ANY, 0, help },
4100 { "list", VERB_ANY, 1, VERB_DEFAULT, list_homes },
4101 { "activate", 2, VERB_ANY, 0, activate_home },
4102 { "deactivate", 2, VERB_ANY, 0, deactivate_home },
4103 { "inspect", VERB_ANY, VERB_ANY, 0, inspect_home },
4104 { "authenticate", VERB_ANY, VERB_ANY, 0, authenticate_home },
4105 { "create", VERB_ANY, 2, 0, create_home },
4106 { "remove", 2, VERB_ANY, 0, remove_home },
4107 { "update", VERB_ANY, 2, 0, update_home },
4108 { "passwd", VERB_ANY, 2, 0, passwd_home },
4109 { "resize", 2, 3, 0, resize_home },
4110 { "lock", 2, VERB_ANY, 0, lock_home },
4111 { "unlock", 2, VERB_ANY, 0, unlock_home },
4112 { "with", 2, VERB_ANY, 0, with_home },
4113 { "lock-all", VERB_ANY, 1, 0, lock_all_homes },
4114 { "deactivate-all", VERB_ANY, 1, 0, deactivate_all_homes },
6d6d4459 4115 { "rebalance", VERB_ANY, 1, 0, rebalance },
3ccadbce 4116 { "firstboot", VERB_ANY, 1, 0, verb_firstboot },
4aa0a8ac
LP
4117 {}
4118 };
4119
4120 int r;
4121
d2acb93d 4122 log_setup();
4aa0a8ac 4123
cc9886bc
LP
4124 r = redirect_bus_mgr();
4125 if (r < 0)
4126 return r;
4127
4aa0a8ac
LP
4128 r = parse_argv(argc, argv);
4129 if (r <= 0)
4130 return r;
4131
4132 return dispatch_verb(argc, argv, verbs, NULL);
4133}
4134
b9bfa250 4135DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);