diff options
Diffstat (limited to 'src/cryptsetup/cryptsetup.c')
-rw-r--r-- | src/cryptsetup/cryptsetup.c | 189 |
1 files changed, 165 insertions, 24 deletions
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 19f075dfeb..328873e0e1 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -12,16 +12,19 @@ #include "alloc-util.h" #include "ask-password-api.h" #include "crypt-util.h" +#include "cryptsetup-pkcs11.h" #include "device-util.h" #include "escape.h" #include "fileio.h" #include "fstab-util.h" +#include "hexdecoct.h" #include "log.h" #include "main-func.h" #include "mount-util.h" #include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" +#include "pkcs11-util.h" #include "pretty-print.h" #include "string-util.h" #include "strv.h" @@ -54,11 +57,13 @@ static char **arg_tcrypt_keyfiles = NULL; static uint64_t arg_offset = 0; static uint64_t arg_skip = 0; static usec_t arg_timeout = USEC_INFINITY; +static char *arg_pkcs11_uri = NULL; STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep); STATIC_DESTRUCTOR_REGISTER(arg_hash, freep); STATIC_DESTRUCTOR_REGISTER(arg_header, freep); STATIC_DESTRUCTOR_REGISTER(arg_tcrypt_keyfiles, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_uri, freep); /* Options Debian's crypttab knows we don't: @@ -228,6 +233,15 @@ static int parse_one_option(const char *option) { if (r < 0) return log_error_errno(r, "Failed to parse %s: %m", option); + } else if ((val = startswith(option, "pkcs11-uri="))) { + + if (!pkcs11_uri_valid(val)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "pkcs11-uri= parameter expects a PKCS#11 URI, refusing"); + + r = free_and_strdup(&arg_pkcs11_uri, val); + if (r < 0) + return log_oom(); + } else if (!streq(option, "x-initrd.attach")) log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option); @@ -314,28 +328,19 @@ static char *disk_mount_point(const char *label) { return NULL; } -static int get_password(const char *vol, const char *src, usec_t until, bool accept_cached, char ***ret) { - _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL, *text = NULL, *disk_path = NULL; - _cleanup_strv_free_erase_ char **passwords = NULL; - const char *name = NULL; - char **p, *id; - int r = 0; +static char *friendly_disk_name(const char *src, const char *vol) { + _cleanup_free_ char *description = NULL, *mount_point = NULL; + char *name_buffer = NULL; + int r; - assert(vol); assert(src); - assert(ret); + assert(vol); description = disk_description(src); mount_point = disk_mount_point(vol); - disk_path = cescape(src); - if (!disk_path) - return log_oom(); - + /* If the description string is simply the volume name, then let's not show this twice */ if (description && streq(vol, description)) - /* If the description string is simply the - * volume name, then let's not show this - * twice */ description = mfree(description); if (mount_point && description) @@ -344,13 +349,39 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc r = asprintf(&name_buffer, "%s on %s", vol, mount_point); else if (description) r = asprintf(&name_buffer, "%s (%s)", description, vol); - + else + return strdup(vol); if (r < 0) + return NULL; + + return name_buffer; +} + +static int get_password( + const char *vol, + const char *src, + usec_t until, + bool accept_cached, + char ***ret) { + + _cleanup_free_ char *friendly = NULL, *text = NULL, *disk_path = NULL; + _cleanup_strv_free_erase_ char **passwords = NULL; + char **p, *id; + int r = 0; + + assert(vol); + assert(src); + assert(ret); + + friendly = friendly_disk_name(src, vol); + if (!friendly) return log_oom(); - name = name_buffer ? name_buffer : vol; + if (asprintf(&text, "Please enter passphrase for disk %s:", friendly) < 0) + return log_oom(); - if (asprintf(&text, "Please enter passphrase for disk %s:", name) < 0) + disk_path = cescape(src); + if (!disk_path) return log_oom(); id = strjoina("cryptsetup:", disk_path); @@ -366,7 +397,7 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc assert(strv_length(passwords) == 1); - if (asprintf(&text, "Please enter passphrase for disk %s (verification):", name) < 0) + if (asprintf(&text, "Please enter passphrase for disk %s (verification):", friendly) < 0) return log_oom(); id = strjoina("cryptsetup-verification:", disk_path); @@ -424,6 +455,11 @@ static int attach_tcrypt( assert(name); assert(key_file || (passwords && passwords[0])); + if (arg_pkcs11_uri) { + log_error("Sorry, but tcrypt devices are currently not supported in conjunction with pkcs11 support."); + return -EAGAIN; /* Ask for a regular password */ + } + if (arg_tcrypt_hidden) params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER; @@ -467,14 +503,14 @@ static int attach_luks_or_plain( const char *name, const char *key_file, char **passwords, - uint32_t flags) { + uint32_t flags, + usec_t until) { int r = 0; bool pass_volume_key = false; assert(cd); assert(name); - assert(key_file || passwords); if ((!arg_type && !crypt_get_type(cd)) || streq_ptr(arg_type, CRYPT_PLAIN)) { struct crypt_params_plain params = { @@ -528,7 +564,111 @@ static int attach_luks_or_plain( crypt_get_volume_key_size(cd)*8, crypt_get_device_name(cd)); - if (key_file) { + if (arg_pkcs11_uri) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + _cleanup_free_ void *decrypted_key = NULL; + _cleanup_free_ char *friendly = NULL; + size_t decrypted_key_size = 0; + + if (!key_file) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "PKCS#11 mode selected but no key file specified, refusing."); + + friendly = friendly_disk_name(crypt_get_device_name(cd), name); + if (!friendly) + return log_oom(); + + for (;;) { + bool processed = false; + + r = decrypt_pkcs11_key( + friendly, + arg_pkcs11_uri, + key_file, + arg_keyfile_size, arg_keyfile_offset, + until, + &decrypted_key, &decrypted_key_size); + if (r >= 0) + break; + if (r != -EAGAIN) /* EAGAIN means: token not found */ + return r; + + if (!monitor) { + /* We didn't find the token. In this case, watch for it via udev. Let's + * create an event loop and monitor first. */ + + assert(!event); + + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); + + r = sd_device_monitor_new(&monitor); + if (r < 0) + return log_error_errno(r, "Failed to allocate device monitor: %m"); + + r = sd_device_monitor_filter_add_match_tag(monitor, "security-device"); + if (r < 0) + return log_error_errno(r, "Failed to configure device monitor: %m"); + + r = sd_device_monitor_attach_event(monitor, event); + if (r < 0) + return log_error_errno(r, "Failed to attach device monitor: %m"); + + r = sd_device_monitor_start(monitor, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to start device monitor: %m"); + + log_notice("Security token %s not present for unlocking volume %s, please plug it in.", + arg_pkcs11_uri, friendly); + + /* Let's immediately rescan in case the token appeared in the time we needed + * to create and configure the monitor */ + continue; + } + + for (;;) { + /* Wait for one event, and then eat all subsequent events until there are no + * further ones */ + r = sd_event_run(event, processed ? 0 : UINT64_MAX); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + if (r == 0) + break; + + processed = true; + } + + log_debug("Got one or more potentially relevant udev events, rescanning PKCS#11..."); + } + + if (pass_volume_key) + r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags); + else { + _cleanup_free_ char *base64_encoded = NULL; + + /* Before using this key as passphrase we base64 encode it. Why? For compatibility + * with homed's PKCS#11 hookup: there we want to use the key we acquired through + * PKCS#11 for other authentication/decryption mechanisms too, and some of them do + * not not take arbitrary binary blobs, but require NUL-terminated strings — most + * importantly UNIX password hashes. Hence, for compatibility we want to use a string + * without embedded NUL here too, and that's easiest to generate from a binary blob + * via base64 encoding. */ + + r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded); + if (r < 0) + return log_oom(); + + r = crypt_activate_by_passphrase(cd, name, arg_key_slot, base64_encoded, strlen(base64_encoded), flags); + } + if (r == -EPERM) { + log_error_errno(r, "Failed to activate with PKCS#11 decrypted key. (Key incorrect?)"); + return -EAGAIN; /* log actual error, but return EAGAIN */ + } + if (r < 0) + return log_error_errno(r, "Failed to activate with PKCS#11 acquired key: %m"); + + } else if (key_file) { r = crypt_activate_by_keyfile_device_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags); if (r == -EPERM) { log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file); @@ -717,7 +857,7 @@ static int run(int argc, char *argv[]) { for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { _cleanup_strv_free_erase_ char **passwords = NULL; - if (!key_file) { + if (!key_file && !arg_pkcs11_uri) { r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords); if (r == -EAGAIN) continue; @@ -728,7 +868,7 @@ static int run(int argc, char *argv[]) { if (streq_ptr(arg_type, CRYPT_TCRYPT)) r = attach_tcrypt(cd, argv[2], key_file, passwords, flags); else - r = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags); + r = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags, until); if (r >= 0) break; if (r != -EAGAIN) @@ -736,6 +876,7 @@ static int run(int argc, char *argv[]) { /* Passphrase not correct? Let's try again! */ key_file = NULL; + arg_pkcs11_uri = NULL; } if (arg_tries != 0 && tries >= arg_tries) |