#include "main-func.h"
#include "memory-util.h"
#include "pager.h"
+#include "parse-argument.h"
#include "parse-util.h"
#include "path-util.h"
+#include "percent-util.h"
#include "pkcs11-util.h"
#include "pretty-print.h"
#include "process-util.h"
static uint64_t arg_disk_size_relative = UINT64_MAX;
static char **arg_pkcs11_token_uri = NULL;
static char **arg_fido2_device = NULL;
+static Fido2EnrollFlags arg_fido2_lock_with = FIDO2ENROLL_PIN | FIDO2ENROLL_UP;
static bool arg_recovery_key = false;
-static bool arg_json = false;
-static JsonFormatFlags arg_json_format_flags = 0;
+static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static bool arg_and_resize = false;
static bool arg_and_change_password = false;
static enum {
_cleanup_(table_unrefp) Table *table = NULL;
int r;
- (void) pager_open(arg_pager_flags);
-
r = acquire_bus(&bus);
if (r < 0)
return r;
if (r < 0)
return bus_log_parse_error(r);
- if (table_get_rows(table) > 1 || arg_json) {
- r = table_set_sort(table, (size_t) 0, (size_t) -1);
+ if (table_get_rows(table) > 1 || !FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
+ r = table_set_sort(table, (size_t) 0);
if (r < 0)
return table_log_sort_error(r);
- table_set_header(table, arg_legend);
-
- if (arg_json)
- r = table_print_json(table, stdout, arg_json_format_flags);
- else
- r = table_print(table, NULL);
+ r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
if (r < 0)
- return table_log_print_error(r);
+ return r;
}
- if (arg_legend && !arg_json) {
+ if (arg_legend && (arg_json_format_flags & JSON_FORMAT_OFF)) {
if (table_get_rows(table) > 1)
printf("\n%zu home areas listed.\n", table_get_rows(table) - 1);
else
return 0;
}
-static int acquire_existing_password(const char *user_name, UserRecord *hr, bool emphasize_current) {
+static int acquire_existing_password(
+ const char *user_name,
+ UserRecord *hr,
+ bool emphasize_current,
+ AskPasswordFlags flags) {
+
_cleanup_(strv_free_erasep) char **password = NULL;
_cleanup_free_ char *question = NULL;
char *e;
if (r < 0)
return log_error_errno(r, "Failed to store password: %m");
- string_erase(e);
- assert_se(unsetenv("PASSWORD") == 0);
-
- return 0;
+ assert_se(unsetenv_erase("PASSWORD") >= 0);
+ return 1;
}
+ /* If this is not our own user, then don't use the password cache */
+ if (is_this_me(user_name) <= 0)
+ SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, false);
+
if (asprintf(&question, emphasize_current ?
"Please enter current password for user %s:" :
"Please enter password for user %s:",
user_name) < 0)
return log_oom();
- r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, &password);
+ r = ask_password_auto(question,
+ /* icon= */ "user-home",
+ NULL,
+ /* key_name= */ "home-password",
+ /* credential_name= */ "home.password",
+ USEC_INFINITY,
+ flags,
+ &password);
+ if (r == -EUNATCH) { /* EUNATCH is returned if no password was found and asking interactively was
+ * disabled via the flags. Not an error for us. */
+ log_debug_errno(r, "No passwords acquired.");
+ return 0;
+ }
if (r < 0)
return log_error_errno(r, "Failed to acquire password: %m");
if (r < 0)
return log_error_errno(r, "Failed to store password: %m");
- return 0;
+ return 1;
}
-static int acquire_token_pin(const char *user_name, UserRecord *hr) {
+static int acquire_token_pin(
+ const char *user_name,
+ UserRecord *hr,
+ AskPasswordFlags flags) {
+
_cleanup_(strv_free_erasep) char **pin = NULL;
_cleanup_free_ char *question = NULL;
char *e;
if (r < 0)
return log_error_errno(r, "Failed to store token PIN: %m");
- string_erase(e);
- assert_se(unsetenv("PIN") == 0);
-
- return 0;
+ assert_se(unsetenv_erase("PIN") >= 0);
+ return 1;
}
+ /* If this is not our own user, then don't use the password cache */
+ if (is_this_me(user_name) <= 0)
+ SET_FLAG(flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, false);
+
if (asprintf(&question, "Please enter security token PIN for user %s:", user_name) < 0)
return log_oom();
- /* We never cache or use cached PINs, since usually there are only very few attempts allowed before the PIN is blocked */
- r = ask_password_auto(question, "user-home", NULL, "token-pin", USEC_INFINITY, 0, &pin);
+ r = ask_password_auto(
+ question,
+ /* icon= */ "user-home",
+ NULL,
+ /* key_name= */ "token-pin",
+ /* credential_name= */ "home.token-pin",
+ USEC_INFINITY,
+ flags,
+ &pin);
+ if (r == -EUNATCH) { /* EUNATCH is returned if no PIN was found and asking interactively was disabled
+ * via the flags. Not an error for us. */
+ log_debug_errno(r, "No security token PINs acquired.");
+ return 0;
+ }
if (r < 0)
return log_error_errno(r, "Failed to acquire security token PIN: %m");
if (r < 0)
return log_error_errno(r, "Failed to store security token PIN: %m");
- return 0;
+ return 1;
}
static int handle_generic_user_record_error(
if (!strv_isempty(hr->password))
log_notice("Password incorrect or not sufficient, please try again.");
- r = acquire_existing_password(user_name, hr, emphasize_current_password);
+ /* Don't consume cache entries or credentials here, we already tried that unsuccessfully. But
+ * let's push what we acquire here into the cache */
+ r = acquire_existing_password(
+ user_name,
+ hr,
+ emphasize_current_password,
+ ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
if (r < 0)
return r;
else
log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
- r = acquire_existing_password(user_name, hr, emphasize_current_password);
+ r = acquire_existing_password(
+ user_name,
+ hr,
+ emphasize_current_password,
+ ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
if (r < 0)
return r;
} else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
- r = acquire_token_pin(user_name, hr);
+ /* First time the PIN is requested, let's accept cached data, and allow using credential store */
+ r = acquire_token_pin(
+ user_name,
+ hr,
+ ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_PUSH_CACHE);
if (r < 0)
return r;
} else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
- log_notice("%s%sAuthentication requires presence verification on security token.",
+ log_notice("%s%sPlease confirm presence on security token.",
emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
emoji_enabled() ? " " : "");
if (r < 0)
return log_error_errno(r, "Failed to set FIDO2 user presence permitted flag: %m");
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED)) {
+
+ log_notice("%s%sPlease verify user on security token.",
+ emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+ emoji_enabled() ? " " : "");
+
+ r = user_record_set_fido2_user_verification_permitted(hr, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set FIDO2 user verification permitted flag: %m");
+
} else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED))
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
log_notice("Security token PIN incorrect, please try again.");
- r = acquire_token_pin(user_name, hr);
+ /* If the previous PIN was wrong don't accept cached info anymore, but add to cache. Also, don't use the credential data */
+ r = acquire_token_pin(
+ user_name,
+ hr,
+ ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
if (r < 0)
return r;
log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
- r = acquire_token_pin(user_name, hr);
+ r = acquire_token_pin(
+ user_name,
+ hr,
+ ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
if (r < 0)
return r;
log_notice("Security token PIN incorrect, please try again (only one try left!).");
- r = acquire_token_pin(user_name, hr);
+ r = acquire_token_pin(
+ user_name,
+ hr,
+ ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_NO_CREDENTIAL);
if (r < 0)
return r;
} else
return 0;
}
+static int acquire_passed_secrets(const char *user_name, UserRecord **ret) {
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+ int r;
+
+ assert(ret);
+
+ /* Generates an initial secret objects that contains passwords supplied via $PASSWORD, the password
+ * cache or the credentials subsystem, but excluding any interactive stuff. If nothing is passed,
+ * returns an empty secret object. */
+
+ secret = user_record_new();
+ if (!secret)
+ return log_oom();
+
+ r = acquire_existing_password(
+ user_name,
+ secret,
+ /* emphasize_current_password = */ false,
+ ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_NO_TTY | ASK_PASSWORD_NO_AGENT);
+ if (r < 0)
+ return r;
+
+ r = acquire_token_pin(
+ user_name,
+ secret,
+ ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_NO_TTY | ASK_PASSWORD_NO_AGENT);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(secret);
+ return 0;
+}
+
static int activate_home(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r, ret = 0;
STRV_FOREACH(i, strv_skip(argv, 1)) {
_cleanup_(user_record_unrefp) UserRecord *secret = NULL;
- secret = user_record_new();
- if (!secret)
- return log_oom();
+ r = acquire_passed_secrets(*i, &secret);
+ if (r < 0)
+ return r;
for (;;) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
if (r < 0) {
- r = handle_generic_user_record_error(*i, secret, &error, r, false);
+ r = handle_generic_user_record_error(*i, secret, &error, r, /* emphasize_current_password= */ false);
if (r < 0) {
if (ret == 0)
ret = r;
log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr->user_name);
}
- if (arg_json) {
+ if (arg_json_format_flags & JSON_FORMAT_OFF)
+ user_record_show(hr, true);
+ else {
_cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
if (arg_export_format == EXPORT_FORMAT_STRIPPED)
- r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED, &stripped);
+ r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED|USER_RECORD_PERMISSIVE, &stripped);
else if (arg_export_format == EXPORT_FORMAT_MINIMAL)
- r = user_record_clone(hr, USER_RECORD_EXTRACT_SIGNABLE, &stripped);
+ r = user_record_clone(hr, USER_RECORD_EXTRACT_SIGNABLE|USER_RECORD_PERMISSIVE, &stripped);
else
r = 0;
if (r < 0)
hr = stripped;
json_variant_dump(hr->json, arg_json_format_flags, stdout, NULL);
- } else
- user_record_show(hr, true);
+ }
}
static char **mangle_user_list(char **list, char ***ret_allocated) {
if (!hr)
return log_oom();
- r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG);
+ r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG|USER_RECORD_PERMISSIVE);
if (r < 0) {
if (ret == 0)
ret = r;
STRV_FOREACH(i, items) {
_cleanup_(user_record_unrefp) UserRecord *secret = NULL;
- secret = user_record_new();
- if (!secret)
- return log_oom();
+ r = acquire_passed_secrets(*i, &secret);
+ if (r < 0)
+ return r;
for (;;) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (arg_identity_extra_this_machine || !strv_isempty(arg_identity_filter)) {
_cleanup_(json_variant_unrefp) JsonVariant *per_machine = NULL, *mmid = NULL;
- char mids[SD_ID128_STRING_MAX];
sd_id128_t mid;
r = sd_id128_get_machine(&mid);
if (r < 0)
return log_error_errno(r, "Failed to acquire machine ID: %m");
- r = json_variant_new_string(&mmid, sd_id128_to_string(mid, mids));
+ r = json_variant_new_string(&mmid, SD_ID128_TO_STRING(mid));
if (r < 0)
return log_error_errno(r, "Failed to allocate matchMachineId object: %m");
}
STRV_FOREACH(i, arg_fido2_device) {
- r = identity_add_fido2_parameters(&v, *i);
+ r = identity_add_fido2_parameters(&v, *i, arg_fido2_lock_with);
if (r < 0)
return r;
}
if (!hr)
return log_oom();
- r = user_record_load(hr, v, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG);
+ r = user_record_load(hr, v, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG|USER_RECORD_PERMISSIVE);
if (r < 0)
return r;
if (r < 0)
return log_error_errno(r, "Failed to store password: %m");
- string_erase(e);
- assert_se(unsetenv("NEWPASSWORD") == 0);
+ assert_se(unsetenv_erase("NEWPASSWORD") >= 0);
if (ret)
*ret = TAKE_PTR(copy);
if (asprintf(&question, "Please enter new password for user %s:", user_name) < 0)
return log_oom();
- r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, 0, &first);
+ r = ask_password_auto(
+ question,
+ /* icon= */ "user-home",
+ NULL,
+ /* key_name= */ "home-password",
+ /* credential_name= */ "home.new-password",
+ USEC_INFINITY,
+ 0, /* no caching, we want to collect a new password here after all */
+ &first);
if (r < 0)
return log_error_errno(r, "Failed to acquire password: %m");
if (asprintf(&question, "Please enter new password for user %s (repeat):", user_name) < 0)
return log_oom();
- r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, 0, &second);
+ r = ask_password_auto(
+ question,
+ /* icon= */ "user-home",
+ NULL,
+ /* key_name= */ "home-password",
+ /* credential_name= */ "home.new-password",
+ USEC_INFINITY,
+ 0, /* no caching */
+ &second);
if (r < 0)
return log_error_errno(r, "Failed to acquire password: %m");
return log_error_errno(r, "Failed to hash password: %m");
} else {
/* There's a hash password set in the record, acquire the unhashed version of it. */
- r = acquire_existing_password(hr->user_name, hr, /* emphasize_current= */ false);
+ r = acquire_existing_password(
+ hr->user_name,
+ hr,
+ /* emphasize_current= */ false,
+ ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_PUSH_CACHE);
if (r < 0)
return r;
}
}
STRV_FOREACH(i, arg_fido2_device) {
- r = identity_add_fido2_parameters(&json, *i);
+ r = identity_add_fido2_parameters(&json, *i, arg_fido2_lock_with);
if (r < 0)
return r;
}
if (!hr)
return log_oom();
- 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);
+ 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);
if (r < 0)
return r;
if (r < 0)
return log_error_errno(r, "Failed to reset FIDO2 user presence permission flag: %m");
+ r = user_record_set_fido2_user_verification_permitted(hr, -1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to reset FIDO2 user verification permission flag: %m");
+
return 0;
}
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
if (arg_disk_size_relative != UINT64_MAX ||
- (argc > 2 && parse_percent(argv[2]) >= 0))
+ (argc > 2 && parse_permyriad(argv[2]) >= 0))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Relative disk size specification currently not supported when resizing.");
ds = arg_disk_size;
}
- secret = user_record_new();
- if (!secret)
- return log_oom();
+ r = acquire_passed_secrets(argv[1], &secret);
+ if (r < 0)
+ return r;
for (;;) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
STRV_FOREACH(i, strv_skip(argv, 1)) {
_cleanup_(user_record_unrefp) UserRecord *secret = NULL;
- secret = user_record_new();
- if (!secret)
- return log_oom();
+ r = acquire_passed_secrets(*i, &secret);
+ if (r < 0)
+ return r;
for (;;) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (!cmdline)
return log_oom();
- secret = user_record_new();
- if (!secret)
- return log_oom();
+ r = acquire_passed_secrets(argv[1], &secret);
+ if (r < 0)
+ return r;
for (;;) {
r = bus_message_new_method_call(bus, &m, bus_mgr, "AcquireHome");
printf("%1$s [OPTIONS...] COMMAND ...\n\n"
"%2$sCreate, manipulate or inspect home directories.%3$s\n"
"\n%4$sCommands:%5$s\n"
- " list List home areas\n"
- " activate USER… Activate a home area\n"
- " deactivate USER… Deactivate a home area\n"
- " inspect USER… Inspect a home area\n"
- " authenticate USER… Authenticate a home area\n"
- " create USER Create a home area\n"
- " remove USER… Remove a home area\n"
- " update USER Update a home area\n"
- " passwd USER Change password of a home area\n"
- " resize USER SIZE Resize a home area\n"
- " lock USER… Temporarily lock an active home area\n"
- " unlock USER… Unlock a temporarily locked home area\n"
- " lock-all Lock all suitable home areas\n"
- " deactivate-all Deactivate all active home areas\n"
- " with USER [COMMAND…] Run shell or command with access to a home area\n"
+ " list List home areas\n"
+ " activate USER… Activate a home area\n"
+ " deactivate USER… Deactivate a home area\n"
+ " inspect USER… Inspect a home area\n"
+ " authenticate USER… Authenticate a home area\n"
+ " create USER Create a home area\n"
+ " remove USER… Remove a home area\n"
+ " update USER Update a home area\n"
+ " passwd USER Change password of a home area\n"
+ " resize USER SIZE Resize a home area\n"
+ " lock USER… Temporarily lock an active home area\n"
+ " unlock USER… Unlock a temporarily locked home area\n"
+ " lock-all Lock all suitable home areas\n"
+ " deactivate-all Deactivate all active home areas\n"
+ " with USER [COMMAND…] Run shell or command with access to a home area\n"
"\n%4$sOptions:%5$s\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --no-pager Do not pipe output into a pager\n"
- " --no-legend Do not show the headers and footers\n"
- " --no-ask-password Do not ask for system passwords\n"
- " -H --host=[USER@]HOST Operate on remote host\n"
- " -M --machine=CONTAINER Operate on local container\n"
- " --identity=PATH Read JSON identity from file\n"
- " --json=FORMAT Output inspection data in JSON (takes one of\n"
- " pretty, short, off)\n"
- " -j Equivalent to --json=pretty (on TTY) or\n"
- " --json=short (otherwise)\n"
- " --export-format= Strip JSON inspection data (full, stripped,\n"
- " minimal)\n"
- " -E When specified once equals -j --export-format=\n"
- " stripped, when specified twice equals\n"
- " -j --export-format=minimal\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-legend Do not show the headers and footers\n"
+ " --no-ask-password Do not ask for system passwords\n"
+ " -H --host=[USER@]HOST Operate on remote host\n"
+ " -M --machine=CONTAINER Operate on local container\n"
+ " --identity=PATH Read JSON identity from file\n"
+ " --json=FORMAT Output inspection data in JSON (takes one of\n"
+ " pretty, short, off)\n"
+ " -j Equivalent to --json=pretty (on TTY) or\n"
+ " --json=short (otherwise)\n"
+ " --export-format= Strip JSON inspection data (full, stripped,\n"
+ " minimal)\n"
+ " -E When specified once equals -j --export-format=\n"
+ " stripped, when specified twice equals\n"
+ " -j --export-format=minimal\n"
"\n%4$sGeneral User Record Properties:%5$s\n"
- " -c --real-name=REALNAME Real name for user\n"
- " --realm=REALM Realm to create user in\n"
- " --email-address=EMAIL Email address for user\n"
- " --location=LOCATION Set location of user on earth\n"
- " --icon-name=NAME Icon name for user\n"
- " -d --home-dir=PATH Home directory\n"
- " -u --uid=UID Numeric UID for user\n"
- " -G --member-of=GROUP Add user to group\n"
- " --skel=PATH Skeleton directory to use\n"
- " --shell=PATH Shell for account\n"
- " --setenv=VARIABLE=VALUE Set an environment variable at log-in\n"
- " --timezone=TIMEZONE Set a time-zone\n"
- " --language=LOCALE Set preferred language\n"
+ " -c --real-name=REALNAME Real name for user\n"
+ " --realm=REALM Realm to create user in\n"
+ " --email-address=EMAIL Email address for user\n"
+ " --location=LOCATION Set location of user on earth\n"
+ " --icon-name=NAME Icon name for user\n"
+ " -d --home-dir=PATH Home directory\n"
+ " -u --uid=UID Numeric UID for user\n"
+ " -G --member-of=GROUP Add user to group\n"
+ " --skel=PATH Skeleton directory to use\n"
+ " --shell=PATH Shell for account\n"
+ " --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n"
+ " --timezone=TIMEZONE Set a time-zone\n"
+ " --language=LOCALE Set preferred language\n"
" --ssh-authorized-keys=KEYS\n"
- " Specify SSH public keys\n"
- " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
- " private key and matching X.509 certificate\n"
- " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
- " extension\n"
- " --recovery-key=BOOL Add a recovery key\n"
- "\n%4$sAccount Management User Record Properties:%5$s\n"
- " --locked=BOOL Set locked account state\n"
- " --not-before=TIMESTAMP Do not allow logins before\n"
- " --not-after=TIMESTAMP Do not allow logins after\n"
+ " Specify SSH public keys\n"
+ " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
+ " private key and matching X.509 certificate\n"
+ " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
+ " extension\n"
+ " --fido2-with-client-pin=BOOL\n"
+ " Whether to require entering a PIN to unlock the\n"
+ " account\n"
+ " --fido2-with-user-presence=BOOL\n"
+ " Whether to require user presence to unlock the\n"
+ " account\n"
+ " --fido2-with-user-verification=BOOL\n"
+ " Whether to require user verification to unlock\n"
+ " the account\n"
+ " --recovery-key=BOOL Add a recovery key\n"
+ "\n%4$sAccount Management User Record Properties:%5$s\n"
+ " --locked=BOOL Set locked account state\n"
+ " --not-before=TIMESTAMP Do not allow logins before\n"
+ " --not-after=TIMESTAMP Do not allow logins after\n"
" --rate-limit-interval=SECS\n"
- " Login rate-limit interval in seconds\n"
+ " Login rate-limit interval in seconds\n"
" --rate-limit-burst=NUMBER\n"
- " Login rate-limit attempts per interval\n"
+ " Login rate-limit attempts per interval\n"
"\n%4$sPassword Policy User Record Properties:%5$s\n"
- " --password-hint=HINT Set Password hint\n"
+ " --password-hint=HINT Set Password hint\n"
" --enforce-password-policy=BOOL\n"
- " Control whether to enforce system's password\n"
- " policy for this user\n"
- " -P Equivalent to --enforce-password-password=no\n"
+ " Control whether to enforce system's password\n"
+ " policy for this user\n"
+ " -P Same as --enforce-password-password=no\n"
" --password-change-now=BOOL\n"
- " Require the password to be changed on next login\n"
+ " Require the password to be changed on next login\n"
" --password-change-min=TIME\n"
- " Require minimum time between password changes\n"
+ " Require minimum time between password changes\n"
" --password-change-max=TIME\n"
- " Require maximum time between password changes\n"
+ " Require maximum time between password changes\n"
" --password-change-warn=TIME\n"
- " How much time to warn before password expiry\n"
+ " How much time to warn before password expiry\n"
" --password-change-inactive=TIME\n"
- " How much time to block password after expiry\n"
+ " How much time to block password after expiry\n"
"\n%4$sResource Management User Record Properties:%5$s\n"
- " --disk-size=BYTES Size to assign the user on disk\n"
- " --access-mode=MODE User home directory access mode\n"
- " --umask=MODE Umask for user when logging in\n"
- " --nice=NICE Nice level for user\n"
+ " --disk-size=BYTES Size to assign the user on disk\n"
+ " --access-mode=MODE User home directory access mode\n"
+ " --umask=MODE Umask for user when logging in\n"
+ " --nice=NICE Nice level for user\n"
" --rlimit=LIMIT=VALUE[:VALUE]\n"
- " Set resource limits\n"
- " --tasks-max=MAX Set maximum number of per-user tasks\n"
- " --memory-high=BYTES Set high memory threshold in bytes\n"
- " --memory-max=BYTES Set maximum memory limit\n"
- " --cpu-weight=WEIGHT Set CPU weight\n"
- " --io-weight=WEIGHT Set IO weight\n"
+ " Set resource limits\n"
+ " --tasks-max=MAX Set maximum number of per-user tasks\n"
+ " --memory-high=BYTES Set high memory threshold in bytes\n"
+ " --memory-max=BYTES Set maximum memory limit\n"
+ " --cpu-weight=WEIGHT Set CPU weight\n"
+ " --io-weight=WEIGHT Set IO weight\n"
"\n%4$sStorage User Record Properties:%5$s\n"
- " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
- " subvolume, cifs)\n"
- " --image-path=PATH Path to image file/directory\n"
+ " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
+ " subvolume, cifs)\n"
+ " --image-path=PATH Path to image file/directory\n"
"\n%4$sLUKS Storage User Record Properties:%5$s\n"
- " --fs-type=TYPE File system type to use in case of luks\n"
- " storage (btrfs, ext4, xfs)\n"
- " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
- " when activated (mounted)\n"
+ " --fs-type=TYPE File system type to use in case of luks\n"
+ " storage (btrfs, ext4, xfs)\n"
+ " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
+ " when activated (mounted)\n"
" --luks-offline-discard=BOOL\n"
- " Whether to trim file on logout\n"
- " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
- " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
+ " Whether to trim file on logout\n"
+ " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
+ " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
" --luks-volume-key-size=BITS\n"
- " Volume key size to use for LUKS encryption\n"
- " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
+ " Volume key size to use for LUKS encryption\n"
+ " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
" --luks-pbkdf-hash-algorithm=ALGORITHM\n"
- " PBKDF hash algorithm to use\n"
+ " PBKDF hash algorithm to use\n"
" --luks-pbkdf-time-cost=SECS\n"
- " Time cost for PBKDF in seconds\n"
+ " Time cost for PBKDF in seconds\n"
" --luks-pbkdf-memory-cost=BYTES\n"
- " Memory cost for PBKDF in bytes\n"
+ " Memory cost for PBKDF in bytes\n"
" --luks-pbkdf-parallel-threads=NUMBER\n"
- " Number of parallel threads for PKBDF\n"
+ " Number of parallel threads for PKBDF\n"
"\n%4$sMounting User Record Properties:%5$s\n"
- " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
- " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
- " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
+ " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
+ " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
+ " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
"\n%4$sCIFS User Record Properties:%5$s\n"
- " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
- " --cifs-user-name=USER CIFS (Windows) user name\n"
- " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
+ " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
+ " --cifs-user-name=USER CIFS (Windows) user name\n"
+ " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
"\n%4$sLogin Behaviour User Record Properties:%5$s\n"
- " --stop-delay=SECS How long to leave user services running after\n"
- " logout\n"
- " --kill-processes=BOOL Whether to kill user processes when sessions\n"
- " terminate\n"
- " --auto-login=BOOL Try to log this user in automatically\n"
- "\nSee the %6$s for details.\n"
- , program_invocation_short_name
- , ansi_highlight(), ansi_normal()
- , ansi_underline(), ansi_normal()
- , link
- );
+ " --stop-delay=SECS How long to leave user services running after\n"
+ " logout\n"
+ " --kill-processes=BOOL Whether to kill user processes when sessions\n"
+ " terminate\n"
+ " --auto-login=BOOL Try to log this user in automatically\n"
+ "\nSee the %6$s for details.\n",
+ program_invocation_short_name,
+ ansi_highlight(),
+ ansi_normal(),
+ ansi_underline(),
+ ansi_normal(),
+ link);
return 0;
}
ARG_AUTO_LOGIN,
ARG_PKCS11_TOKEN_URI,
ARG_FIDO2_DEVICE,
+ ARG_FIDO2_WITH_PIN,
+ ARG_FIDO2_WITH_UP,
+ ARG_FIDO2_WITH_UV,
ARG_RECOVERY_KEY,
ARG_AND_RESIZE,
ARG_AND_CHANGE_PASSWORD,
{ "export-format", required_argument, NULL, ARG_EXPORT_FORMAT },
{ "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
{ "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE },
+ { "fido2-with-client-pin", required_argument, NULL, ARG_FIDO2_WITH_PIN },
+ { "fido2-with-user-presence", required_argument, NULL, ARG_FIDO2_WITH_UP },
+ { "fido2-with-user-verification",required_argument, NULL, ARG_FIDO2_WITH_UV },
{ "recovery-key", required_argument, NULL, ARG_RECOVERY_KEY },
{ "and-resize", required_argument, NULL, ARG_AND_RESIZE },
{ "and-change-password", required_argument, NULL, ARG_AND_CHANGE_PASSWORD },
break;
}
- r = parse_path_argument_and_warn(optarg, false, &hd);
+ r = parse_path_argument(optarg, false, &hd);
if (r < 0)
return r;
l = rlimit_from_string_harder(field);
if (l < 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown resource limit type: %s", field);
+ return log_error_errno(l, "Unknown resource limit type: %s", field);
if (isempty(eq + 1)) {
/* Remove only the specific rlimit */
break;
}
- r = parse_path_argument_and_warn(optarg, false, &v);
+ r = parse_path_argument(optarg, false, &v);
if (r < 0)
return r;
break;
case ARG_SETENV: {
- _cleanup_free_ char **l = NULL, **k = NULL;
+ _cleanup_free_ char **l = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *ne = NULL;
JsonVariant *e;
break;
}
- if (!env_assignment_is_valid(optarg))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Environment assignment '%s' not valid.", optarg);
-
e = json_variant_by_key(arg_identity_extra, "environment");
if (e) {
r = json_variant_strv(e, &l);
return log_error_errno(r, "Failed to parse JSON environment field: %m");
}
- k = strv_env_set(l, optarg);
- if (!k)
- return log_oom();
+ r = strv_env_replace_strdup_passthrough(&l, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
- strv_sort(k);
+ strv_sort(l);
- r = json_variant_new_array_strv(&ne, k);
+ r = json_variant_new_array_strv(&ne, l);
if (r < 0)
return log_error_errno(r, "Failed to allocate environment list JSON: %m");
break;
}
- r = parse_permille(optarg);
+ r = parse_permyriad(optarg);
if (r < 0) {
r = parse_size(optarg, 1024, &arg_disk_size);
if (r < 0)
arg_disk_size_relative = UINT64_MAX;
} else {
/* Normalize to UINT32_MAX == 100% */
- arg_disk_size_relative = (uint64_t) r * UINT32_MAX / 1000U;
+ arg_disk_size_relative = UINT32_SCALE_FROM_PERMYRIAD(r);
r = drop_from_identity("diskSize");
if (r < 0)
r = strv_consume(&arg_fido2_device, TAKE_PTR(found));
} else
r = strv_extend(&arg_fido2_device, optarg);
-
if (r < 0)
return r;
break;
}
+ case ARG_FIDO2_WITH_PIN: {
+ bool lock_with_pin;
+
+ r = parse_boolean_argument("--fido2-with-client-pin=", optarg, &lock_with_pin);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_PIN, lock_with_pin);
+ break;
+ }
+
+ case ARG_FIDO2_WITH_UP: {
+ bool lock_with_up;
+
+ r = parse_boolean_argument("--fido2-with-user-presence=", optarg, &lock_with_up);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UP, lock_with_up);
+ break;
+ }
+
+ case ARG_FIDO2_WITH_UV: {
+ bool lock_with_uv;
+
+ r = parse_boolean_argument("--fido2-with-user-verification=", optarg, &lock_with_uv);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UV, lock_with_uv);
+ break;
+ }
+
case ARG_RECOVERY_KEY: {
const char *p;
}
case 'j':
- arg_json = true;
arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
break;
case ARG_JSON:
- if (streq(optarg, "pretty")) {
- arg_json = true;
- arg_json_format_flags = JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO;
- } else if (streq(optarg, "short")) {
- arg_json = true;
- arg_json_format_flags = JSON_FORMAT_NEWLINE;
- } else if (streq(optarg, "off")) {
- arg_json = false;
- arg_json_format_flags = 0;
- } else if (streq(optarg, "help")) {
- puts("pretty\n"
- "short\n"
- "off");
- return 0;
- } else
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown argument to --json=: %s", optarg);
+ r = parse_json_argument(optarg, &arg_json_format_flags);
+ if (r <= 0)
+ return r;
break;
else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specifying -E more than twice is not supported.");
- arg_json = true;
+ arg_json_format_flags &= ~JSON_FORMAT_OFF;
if (arg_json_format_flags == 0)
arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
break;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
}
int r;
- log_setup_cli();
+ log_setup();
r = redirect_bus_mgr();
if (r < 0)
return dispatch_verb(argc, argv, verbs, NULL);
}
-DEFINE_MAIN_FUNCTION(run);
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);