From: Yu Watanabe Date: Sat, 18 Jan 2025 19:58:54 +0000 (+0900) Subject: cryptsetup: use dispatch_verb() X-Git-Tag: v258-rc1~1549^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a0b1ee2fcac05c9f73c3a5c38380674a768f529e;p=thirdparty%2Fsystemd.git cryptsetup: use dispatch_verb() --- diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index e5f21bbce77..04ff160057b 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -46,6 +46,7 @@ #include "strv.h" #include "tpm2-pcr.h" #include "tpm2-util.h" +#include "verbs.h" /* internal helper */ #define ANY_LUKS "LUKS" @@ -2388,294 +2389,296 @@ static int discover_key(const char *key_file, const char *volume, TokenType toke return r; } -static int run(int argc, char *argv[]) { +static int verb_attach(int argc, char *argv[], void *userdata) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; - const char *verb; + _unused_ _cleanup_(remove_and_erasep) const char *destroy_key_file = NULL; + crypt_status_info status; + uint32_t flags = 0; + unsigned tries; + usec_t until; + PassphraseType passphrase_type = PASSPHRASE_NONE; int r; - log_setup(); + /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [KEY-FILE] [CONFIG] */ - umask(0022); + assert(argc >= 3 && argc <= 5); - r = parse_argv(argc, argv); - if (r <= 0) - return r; + const char *volume = ASSERT_PTR(argv[1]), + *source = ASSERT_PTR(argv[2]), + *key_file = argc >= 4 ? mangle_none(argv[3]) : NULL, + *config = argc >= 5 ? mangle_none(argv[4]) : NULL; - cryptsetup_enable_logging(NULL); + if (!filename_is_valid(volume)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume); - if (argc - optind < 2) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "This program requires at least two arguments."); - verb = ASSERT_PTR(argv[optind]); - - if (streq(verb, "attach")) { - _unused_ _cleanup_(remove_and_erasep) const char *destroy_key_file = NULL; - crypt_status_info status; - uint32_t flags = 0; - unsigned tries; - usec_t until; - PassphraseType passphrase_type = PASSPHRASE_NONE; - - /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [KEY-FILE] [CONFIG] */ - - if (argc - optind < 3) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least two arguments."); - if (argc - optind >= 6) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach does not accept more than four arguments."); - - const char *volume = ASSERT_PTR(argv[optind + 1]), - *source = ASSERT_PTR(argv[optind + 2]), - *key_file = argc - optind >= 4 ? mangle_none(argv[optind + 3]) : NULL, - *config = argc - optind >= 5 ? mangle_none(argv[optind + 4]) : NULL; - - if (!filename_is_valid(volume)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume); - - if (key_file && !path_is_absolute(key_file)) { - log_warning("Password file path '%s' is not absolute. Ignoring.", key_file); - key_file = NULL; - } + if (key_file && !path_is_absolute(key_file)) { + log_warning("Password file path '%s' is not absolute. Ignoring.", key_file); + key_file = NULL; + } - if (config) { - r = parse_crypt_config(config); - if (r < 0) - return r; - } + if (config) { + r = parse_crypt_config(config); + if (r < 0) + return r; + } - log_debug("%s %s ← %s type=%s cipher=%s", __func__, - volume, source, strempty(arg_type), strempty(arg_cipher)); + log_debug("%s %s ← %s type=%s cipher=%s", __func__, + volume, source, strempty(arg_type), strempty(arg_cipher)); - /* A delicious drop of snake oil */ - (void) mlockall(MCL_FUTURE); + /* A delicious drop of snake oil */ + (void) mlockall(MCL_FUTURE); - if (key_file && arg_keyfile_erase) - destroy_key_file = key_file; /* let's get this baby erased when we leave */ + if (key_file && arg_keyfile_erase) + destroy_key_file = key_file; /* let's get this baby erased when we leave */ - if (arg_header) { - if (streq_ptr(arg_type, CRYPT_TCRYPT)){ - log_debug("tcrypt header: %s", arg_header); - r = crypt_init_data_device(&cd, arg_header, source); - } else { - log_debug("LUKS header: %s", arg_header); - r = crypt_init(&cd, arg_header); - } - } else - r = crypt_init(&cd, source); - if (r < 0) - return log_error_errno(r, "crypt_init() failed: %m"); + if (arg_header) { + if (streq_ptr(arg_type, CRYPT_TCRYPT)){ + log_debug("tcrypt header: %s", arg_header); + r = crypt_init_data_device(&cd, arg_header, source); + } else { + log_debug("LUKS header: %s", arg_header); + r = crypt_init(&cd, arg_header); + } + } else + r = crypt_init(&cd, source); + if (r < 0) + return log_error_errno(r, "crypt_init() failed: %m"); - cryptsetup_enable_logging(cd); + cryptsetup_enable_logging(cd); - status = crypt_status(cd, volume); - if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) { - log_info("Volume %s already active.", volume); - return 0; - } + status = crypt_status(cd, volume); + if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) { + log_info("Volume %s already active.", volume); + return 0; + } - flags = determine_flags(); + flags = determine_flags(); - until = usec_add(now(CLOCK_MONOTONIC), arg_timeout); - if (until == USEC_INFINITY) - until = 0; + until = usec_add(now(CLOCK_MONOTONIC), arg_timeout); + if (until == USEC_INFINITY) + until = 0; - if (arg_key_size == 0) - arg_key_size = 256U / 8U; + if (arg_key_size == 0) + arg_key_size = 256U / 8U; - if (key_file) { - struct stat st; + if (key_file) { + struct stat st; - /* Ideally we'd do this on the open fd, but since this is just a - * warning it's OK to do this in two steps. */ - if (stat(key_file, &st) >= 0 && S_ISREG(st.st_mode) && (st.st_mode & 0005)) - log_warning("Key file %s is world-readable. This is not a good idea!", key_file); - } + /* Ideally we'd do this on the open fd, but since this is just a warning it's OK to do this + * in two steps. */ + if (stat(key_file, &st) >= 0 && S_ISREG(st.st_mode) && (st.st_mode & 0005)) + log_warning("Key file %s is world-readable. This is not a good idea!", key_file); + } - if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2)) { - r = crypt_load(cd, !arg_type || streq(arg_type, ANY_LUKS) ? CRYPT_LUKS : arg_type, NULL); - if (r < 0) - return log_error_errno(r, "Failed to load LUKS superblock on device %s: %m", crypt_get_device_name(cd)); + if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2)) { + r = crypt_load(cd, !arg_type || streq(arg_type, ANY_LUKS) ? CRYPT_LUKS : arg_type, NULL); + if (r < 0) + return log_error_errno(r, "Failed to load LUKS superblock on device %s: %m", crypt_get_device_name(cd)); /* since cryptsetup 2.7.0 (Jan 2024) */ #if HAVE_CRYPT_SET_KEYRING_TO_LINK - if (arg_link_key_description) { - r = crypt_set_keyring_to_link(cd, arg_link_key_description, NULL, arg_link_key_type, arg_link_keyring); - if (r < 0) - log_warning_errno(r, "Failed to set keyring or key description to link volume key in, ignoring: %m"); - } + if (arg_link_key_description) { + r = crypt_set_keyring_to_link(cd, arg_link_key_description, NULL, arg_link_key_type, arg_link_keyring); + if (r < 0) + log_warning_errno(r, "Failed to set keyring or key description to link volume key in, ignoring: %m"); + } #endif - if (arg_header) { - r = crypt_set_data_device(cd, source); - if (r < 0) - return log_error_errno(r, "Failed to set LUKS data device %s: %m", source); - } - - /* Tokens are available in LUKS2 only, but it is ok to call (and fail) with LUKS1. */ - if (!key_file && use_token_plugins()) { - r = crypt_activate_by_token_pin_ask_password( - cd, - volume, - /* type= */ NULL, - until, - /* userdata= */ NULL, - flags, - "Please enter LUKS2 token PIN:", - "luks2-pin", - "cryptsetup.luks2-pin"); - if (r >= 0) { - log_debug("Volume %s activated with a LUKS token.", volume); - return 0; - } + if (arg_header) { + r = crypt_set_data_device(cd, source); + if (r < 0) + return log_error_errno(r, "Failed to set LUKS data device %s: %m", source); + } - log_debug_errno(r, "Token activation unsuccessful for device %s: %m", crypt_get_device_name(cd)); + /* Tokens are available in LUKS2 only, but it is ok to call (and fail) with LUKS1. */ + if (!key_file && use_token_plugins()) { + r = crypt_activate_by_token_pin_ask_password( + cd, + volume, + /* type= */ NULL, + until, + /* userdata= */ NULL, + flags, + "Please enter LUKS2 token PIN:", + "luks2-pin", + "cryptsetup.luks2-pin"); + if (r >= 0) { + log_debug("Volume %s activated with a LUKS token.", volume); + return 0; } + + log_debug_errno(r, "Token activation unsuccessful for device %s: %m", crypt_get_device_name(cd)); } + } /* since cryptsetup 2.3.0 (Feb 2020) */ #ifdef CRYPT_BITLK - if (streq_ptr(arg_type, CRYPT_BITLK)) { - r = crypt_load(cd, CRYPT_BITLK, NULL); + if (streq_ptr(arg_type, CRYPT_BITLK)) { + r = crypt_load(cd, CRYPT_BITLK, NULL); + if (r < 0) + return log_error_errno(r, "Failed to load Bitlocker superblock on device %s: %m", crypt_get_device_name(cd)); + } +#endif + + bool use_cached_passphrase = true, try_discover_key = !key_file; + const char *discovered_key_fn = strjoina(volume, ".key"); + _cleanup_strv_free_erase_ char **passwords = NULL; + for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { + _cleanup_(iovec_done_erase) struct iovec discovered_key_data = {}; + const struct iovec *key_data = NULL; + TokenType token_type = determine_token_type(); + + log_debug("Beginning attempt %u to unlock.", tries); + + /* When we were able to acquire multiple keys, let's always process them in this order: + * + * 1. A key acquired via PKCS#11 or FIDO2 token, or TPM2 chip + * 2. The configured or discovered key, of which both are exclusive and optional + * 3. The empty password, in case arg_try_empty_password is set + * 4. We enquire the user for a password + */ + + if (try_discover_key) { + r = discover_key(discovered_key_fn, volume, token_type, &discovered_key_data); if (r < 0) - return log_error_errno(r, "Failed to load Bitlocker superblock on device %s: %m", crypt_get_device_name(cd)); + return r; + if (r > 0) + key_data = &discovered_key_data; } -#endif - bool use_cached_passphrase = true, try_discover_key = !key_file; - const char *discovered_key_fn = strjoina(volume, ".key"); - _cleanup_strv_free_erase_ char **passwords = NULL; - for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { - _cleanup_(iovec_done_erase) struct iovec discovered_key_data = {}; - const struct iovec *key_data = NULL; - TokenType token_type = determine_token_type(); - - log_debug("Beginning attempt %u to unlock.", tries); - - /* When we were able to acquire multiple keys, let's always process them in this order: - * - * 1. A key acquired via PKCS#11 or FIDO2 token, or TPM2 chip - * 2. The configured or discovered key, of which both are exclusive and optional - * 3. The empty password, in case arg_try_empty_password is set - * 4. We enquire the user for a password - */ - - if (try_discover_key) { - r = discover_key(discovered_key_fn, volume, token_type, &discovered_key_data); - if (r < 0) - return r; - if (r > 0) - key_data = &discovered_key_data; - } + if (token_type < 0 && !key_file && !key_data && !passwords) { + + /* If we have nothing to try anymore, then acquire a new password */ - if (token_type < 0 && !key_file && !key_data && !passwords) { - - /* If we have nothing to try anymore, then acquire a new password */ - - if (arg_try_empty_password) { - /* Hmm, let's try an empty password now, but only once */ - arg_try_empty_password = false; - key_data = &iovec_empty; - } else { - /* Ask the user for a passphrase or recovery key only as last resort, if we have - * nothing else to check for */ - if (passphrase_type == PASSPHRASE_NONE) { - passphrase_type = check_registered_passwords(cd); - if (passphrase_type == PASSPHRASE_NONE) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No passphrase or recovery key registered."); - } - - r = get_password( - volume, - source, - until, - /* ignore_cached= */ !use_cached_passphrase || arg_verify, - passphrase_type, - &passwords); - use_cached_passphrase = false; - if (r == -EAGAIN) - continue; - if (r < 0) - return r; + if (arg_try_empty_password) { + /* Hmm, let's try an empty password now, but only once */ + arg_try_empty_password = false; + key_data = &iovec_empty; + } else { + /* Ask the user for a passphrase or recovery key only as last resort, if we + * have nothing else to check for */ + if (passphrase_type == PASSPHRASE_NONE) { + passphrase_type = check_registered_passwords(cd); + if (passphrase_type == PASSPHRASE_NONE) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No passphrase or recovery key registered."); } - } - if (streq_ptr(arg_type, CRYPT_TCRYPT)) - r = attach_tcrypt(cd, volume, token_type, key_file, key_data, passwords, flags); - else - r = attach_luks_or_plain_or_bitlk(cd, volume, token_type, key_file, key_data, passwords, flags, until); - if (r >= 0) - break; - if (r != -EAGAIN) - return r; + r = get_password( + volume, + source, + until, + /* ignore_cached= */ !use_cached_passphrase || arg_verify, + passphrase_type, + &passwords); + use_cached_passphrase = false; + if (r == -EAGAIN) + continue; + if (r < 0) + return r; + } + } - /* Key not correct? Let's try again, but let's invalidate one of the passed fields, - * so that we fall back to the next best thing. */ + if (streq_ptr(arg_type, CRYPT_TCRYPT)) + r = attach_tcrypt(cd, volume, token_type, key_file, key_data, passwords, flags); + else + r = attach_luks_or_plain_or_bitlk(cd, volume, token_type, key_file, key_data, passwords, flags, until); + if (r >= 0) + break; + if (r != -EAGAIN) + return r; - if (token_type == TOKEN_TPM2) { - arg_tpm2_device = mfree(arg_tpm2_device); - arg_tpm2_device_auto = false; - continue; - } + /* Key not correct? Let's try again, but let's invalidate one of the passed fields, so that + * we fall back to the next best thing. */ - if (token_type == TOKEN_FIDO2) { - arg_fido2_device = mfree(arg_fido2_device); - arg_fido2_device_auto = false; - continue; - } + if (token_type == TOKEN_TPM2) { + arg_tpm2_device = mfree(arg_tpm2_device); + arg_tpm2_device_auto = false; + continue; + } - if (token_type == TOKEN_PKCS11) { - arg_pkcs11_uri = mfree(arg_pkcs11_uri); - arg_pkcs11_uri_auto = false; - continue; - } + if (token_type == TOKEN_FIDO2) { + arg_fido2_device = mfree(arg_fido2_device); + arg_fido2_device_auto = false; + continue; + } - if (try_discover_key) { - try_discover_key = false; - continue; - } + if (token_type == TOKEN_PKCS11) { + arg_pkcs11_uri = mfree(arg_pkcs11_uri); + arg_pkcs11_uri_auto = false; + continue; + } - if (key_file) { - key_file = NULL; - continue; - } + if (try_discover_key) { + try_discover_key = false; + continue; + } - if (passwords) { - passwords = strv_free_erase(passwords); - continue; - } + if (key_file) { + key_file = NULL; + continue; + } - log_debug("Prepared for next attempt to unlock."); + if (passwords) { + passwords = strv_free_erase(passwords); + continue; } - if (arg_tries != 0 && tries >= arg_tries) - return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Too many attempts to activate; giving up."); + log_debug("Prepared for next attempt to unlock."); + } - } else if (streq(verb, "detach")) { - const char *volume = ASSERT_PTR(argv[optind + 1]); + if (arg_tries != 0 && tries >= arg_tries) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Too many attempts to activate; giving up."); - if (argc - optind >= 3) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "detach does not accept more than one argument."); + return 0; +} - if (!filename_is_valid(volume)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume); +static int verb_detach(int argc, char *argv[], void *userdata) { + _cleanup_(crypt_freep) struct crypt_device *cd = NULL; + const char *volume = ASSERT_PTR(argv[1]); + int r; - r = crypt_init_by_name(&cd, volume); - if (r == -ENODEV) { - log_info("Volume %s already inactive.", volume); - return 0; - } - if (r < 0) - return log_error_errno(r, "crypt_init_by_name() for volume '%s' failed: %m", volume); + assert(argc == 2); - cryptsetup_enable_logging(cd); + if (!filename_is_valid(volume)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume); - r = crypt_deactivate(cd, volume); - if (r < 0) - return log_error_errno(r, "Failed to deactivate '%s': %m", volume); + r = crypt_init_by_name(&cd, volume); + if (r == -ENODEV) { + log_info("Volume %s already inactive.", volume); + return 0; + } + if (r < 0) + return log_error_errno(r, "crypt_init_by_name() for volume '%s' failed: %m", volume); - } else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", verb); + cryptsetup_enable_logging(cd); + + r = crypt_deactivate(cd, volume); + if (r < 0) + return log_error_errno(r, "Failed to deactivate '%s': %m", volume); return 0; } +static int run(int argc, char *argv[]) { + int r; + + log_setup(); + + umask(0022); + + r = parse_argv(argc, argv); + if (r <= 0) + return r; + + cryptsetup_enable_logging(NULL); + + static const Verb verbs[] = { + { "attach", 3, 5, 0, verb_attach }, + { "detach", 2, 2, 0, verb_detach }, + {} + }; + + return dispatch_verb(argc, argv, verbs, NULL); +} + DEFINE_MAIN_FUNCTION(run);