Merge pull request #15637 from poettering/cryptsetup-literal

a number of cryptsetup fixes and additions
This commit is contained in:
Lennart Poettering
2020-05-19 19:28:42 +02:00
committed by GitHub
13 changed files with 436 additions and 137 deletions
+28 -14
View File
@@ -41,7 +41,7 @@
character are ignored. Each of the remaining lines describes one
encrypted block device. Fields are delimited by white space.</para>
<para>Each line is in the form<programlisting><replaceable>name</replaceable> <replaceable>encrypted-device</replaceable> <replaceable>password</replaceable> <replaceable>options</replaceable></programlisting>
<para>Each line is in the form<programlisting><replaceable>volume-name</replaceable> <replaceable>encrypted-device</replaceable> <replaceable>key-file</replaceable> <replaceable>options</replaceable></programlisting>
The first two fields are mandatory, the remaining two are
optional.</para>
@@ -53,24 +53,20 @@
it is opened as a LUKS device; otherwise, it is assumed to be in
raw dm-crypt (plain mode) format.</para>
<para>The first field contains the name of the resulting encrypted
block device; the device is set up within
<filename>/dev/mapper/</filename>.</para>
<para>The first field contains the name of the resulting encrypted volume; its block device is set up
below <filename>/dev/mapper/</filename>.</para>
<para>The second field contains a path to the underlying block
device or file, or a specification of a block device via
<literal>UUID=</literal> followed by the UUID.</para>
<para>The third field specifies the encryption password. If the
field is not present or the password is set to
<literal>none</literal> or <literal>-</literal>, the password has
to be manually entered during system boot. Otherwise, the field is
interpreted as an absolute path to a file containing the encryption
password. For swap encryption, <filename>/dev/urandom</filename>
or the hardware device <filename>/dev/hw_random</filename> can be
used as the password file; using <filename>/dev/random</filename>
may prevent boot completion if the system does not have enough
entropy to generate a truly random encryption key.</para>
<para>The third field specifies an absolute path to a file to read the encryption key from. If the field
is not present or set to <literal>none</literal> or <literal>-</literal>, a key file named after the
volume to unlock (i.e. the first column of the line), suffixed with <filename>.key</filename> is
automatically loaded from the <filename>/etc/cryptsetup-keys.d/</filename> and
<filename>/run/cryptsetup-keys.d/</filename> directories, if present. Otherwise, the password has to be
manually entered during system boot. For swap encryption, <filename>/dev/urandom</filename> may be used
as key file.</para>
<para>The fourth field, if present, is a comma-delimited list of
options. The following options are recognized:</para>
@@ -138,6 +134,15 @@
size is then given by the key size.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>keyfile-erase</option></term>
<listitem><para>If enabled, the specified key file is erased after the volume is activated or when
activation fails. This is in particular useful when the key file is only acquired transiently before
activation (e.g. via a file in <filename>/run/</filename>, generated by a service running before
activation), and shall be removed after use. Defaults to off.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>key-slot=</option></term>
@@ -431,6 +436,15 @@
before it is used to unlock the LUKS volume.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>try-empty-password=</option></term>
<listitem><para>Takes a boolean argument. If enabled, right before asking the user for a password it
is first attempted to unlock the volume with an empty password. This is useful for systems that are
initialized with an encrypted volume with only an empty password set, which shall be replaced with a
suitable password during first boot, but after activation.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>x-systemd.device-timeout=</option></term>
+26
View File
@@ -44,6 +44,32 @@
<para>At early boot and when the system manager configuration is reloaded, <filename>/etc/crypttab</filename> is
translated into <filename>systemd-cryptsetup@.service</filename> units by
<citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para>In order to unlock a volume a password or binary key is
required. <filename>systemd-cryptsetup@.service</filename> tries to acquire a suitable password or binary
key via the following mechanisms, tried in order:</para>
<orderedlist>
<listitem><para>If a key file is explicitly configured (via the third column in
<filename>/etc/crypttab</filename>), a key read from it is used. If a PKCS#11 token is configured
(using the <varname>pkcs11-uri=</varname> option) the key is decrypted before use.</para></listitem>
<listitem><para>If no key file is configured explicitly this way, a key file is automatically loaded
from <filename>/etc/cryptsetup-keys.d/<replaceable>volume</replaceable>.key</filename> and
<filename>/run/cryptsetup-keys.d/<replaceable>volume</replaceable>.key</filename>, if present. Here
too, if a PKCS#11 token is configured, any key found this way is decrypted before
use.</para></listitem>
<listitem><para>If the <varname>try-empty-password</varname> option is specified it is then attempted
to unlock the volume with an empty password.</para></listitem>
<listitem><para>The kernel keyring is then checked for a suitable cached password from previous
attempts.</para></listitem>
<listitem><para>Finally, the user is queried for a password, possibly multiple times.</para></listitem>
</orderedlist>
<para>If no suitable key may be acquired via any of the mechanisms describes above, volume activation fails.</para>
</refsect1>
<refsect1>
+8 -5
View File
@@ -23,8 +23,9 @@ dd if=/dev/urandom of=plaintext.bin bs=128 count=1
base64 < plaintext.bin | tr -d '\n\r\t ' > plaintext.base64
# Encrypt this newly generated (binary) LUKS decryption key using the public key whose private key is on the
# Yubikey, store the result in /etc/encrypted-luks-key.bin, where we'll look for it during boot.
sudo openssl rsautl -encrypt -pubin -inkey pubkey.pem -in plaintext.bin -out /etc/encrypted-luks-key.bin
# Yubikey, store the result in /etc/cryptsetup-keys.d/mytest.key, where we'll look for it during boot.
mkdir -p /etc/cryptsetup-keys.d
sudo openssl rsautl -encrypt -pubin -inkey pubkey.pem -in plaintext.bin -out /etc/cryptsetup-keys.d/mytest.key
# Configure the LUKS decryption key on the LUKS device. We use very low pbkdf settings since the key already
# has quite a high quality (it comes directly from /dev/urandom after all), and thus we don't need to do much
@@ -40,8 +41,10 @@ shred -u plaintext.bin plaintext.base64
rm pubkey.pem
# Test: Let's run systemd-cryptsetup to test if this all worked. The option string should contain the full
# PKCS#11 URI we have in the clipboard, it tells the tool how to decipher the encrypted LUKS key.
sudo systemd-cryptsetup attach mytest /dev/sdXn /etc/encrypted-luks-key.bin 'pkcs11-uri=pkcs11:…'
# PKCS#11 URI we have in the clipboard; it tells the tool how to decipher the encrypted LUKS key. Note that
# systemd-cryptsetup automatically searches for the encrypted key in /etc/cryptsetup-keys.d/, hence we do
# not need to specify the key file path explicitly here.
sudo systemd-cryptsetup attach mytest /dev/sdXn - 'pkcs11-uri=pkcs11:…'
# If that worked, let's now add the same line persistently to /etc/crypttab, for the future.
sudo bash -c 'echo "mytest /dev/sdXn /etc/encrypted-luks-key \'pkcs11-uri=pkcs11:…\'" >> /etc/crypttab'
sudo bash -c 'echo "mytest /dev/sdXn - \'pkcs11-uri=pkcs11:…\'" >> /etc/crypttab'
+3 -1
View File
@@ -2227,8 +2227,10 @@ executable(
if conf.get('HAVE_LIBCRYPTSETUP') == 1
systemd_cryptsetup_sources = files('''
src/cryptsetup/cryptsetup.c
src/cryptsetup/cryptsetup-pkcs11.h
src/cryptsetup/cryptsetup-util.c
src/cryptsetup/cryptsetup-util.h
src/cryptsetup/cryptsetup.c
'''.split())
if conf.get('HAVE_P11KIT') == 1
+45 -4
View File
@@ -23,6 +23,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "random-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
@@ -1303,11 +1304,13 @@ void unlink_tempfilep(char (*p)[]) {
(void) unlink_noerrno(*p);
}
int unlinkat_deallocate(int fd, const char *name, int flags) {
int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
_cleanup_close_ int truncate_fd = -1;
struct stat st;
off_t l, bs;
assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0);
/* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other
* link to it. This is useful to ensure that other processes that might have the file open for reading won't be
* able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up
@@ -1324,7 +1327,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
* Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the
* primary job – to delete the file – is accomplished. */
if ((flags & AT_REMOVEDIR) == 0) {
if (!FLAGS_SET(flags, UNLINK_REMOVEDIR)) {
truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
if (truncate_fd < 0) {
@@ -1340,7 +1343,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
}
}
if (unlinkat(fd, name, flags) < 0)
if (unlinkat(fd, name, FLAGS_SET(flags, UNLINK_REMOVEDIR) ? AT_REMOVEDIR : 0) < 0)
return -errno;
if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */
@@ -1351,7 +1354,45 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
return 0;
}
if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0)
if (!S_ISREG(st.st_mode))
return 0;
if (FLAGS_SET(flags, UNLINK_ERASE) && st.st_size > 0 && st.st_nlink == 0) {
uint64_t left = st.st_size;
char buffer[64 * 1024];
/* If erasing is requested, let's overwrite the file with random data once before deleting
* it. This isn't going to give you shred(1) semantics, but hopefully should be good enough
* for stuff backed by tmpfs at least.
*
* Note that we only erase like this if the link count of the file is zero. If it is higer it
* is still linked by someone else and we'll leave it to them to remove it securely
* eventually! */
random_bytes(buffer, sizeof(buffer));
while (left > 0) {
ssize_t n;
n = write(truncate_fd, buffer, MIN(sizeof(buffer), left));
if (n < 0) {
log_debug_errno(errno, "Failed to erase data in file '%s', ignoring.", name);
break;
}
assert(left >= (size_t) n);
left -= n;
}
/* Let's refresh metadata */
if (fstat(truncate_fd, &st) < 0) {
log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name);
return 0;
}
}
/* Don't dallocate if there's nothing to deallocate or if the file is linked elsewhere */
if (st.st_blocks == 0 || st.st_nlink > 0)
return 0;
/* If this is a regular file, it actually took up space on disk and there are no other links it's time to
+7 -1
View File
@@ -113,7 +113,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
int access_fd(int fd, int mode);
void unlink_tempfilep(char (*p)[]);
int unlinkat_deallocate(int fd, const char *name, int flags);
typedef enum UnlinkDeallocateFlags {
UNLINK_REMOVEDIR = 1 << 0,
UNLINK_ERASE = 1 << 1,
} UnlinkDeallocateFlags;
int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags);
int fsync_directory_of_file(int fd);
int fsync_full(int fd);
+8 -2
View File
@@ -188,7 +188,11 @@ static int print_dependencies(FILE *f, const char* device_path) {
/* None, nothing to do */
return 0;
if (PATH_IN_SET(device_path, "/dev/urandom", "/dev/random", "/dev/hw_random")) {
if (PATH_IN_SET(device_path,
"/dev/urandom",
"/dev/random",
"/dev/hw_random",
"/dev/hwrng")) {
/* RNG device, add random dep */
fputs("After=systemd-random-seed.service\n", f);
return 0;
@@ -209,7 +213,9 @@ static int print_dependencies(FILE *f, const char* device_path) {
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
fprintf(f, "After=%1$s\nRequires=%1$s\n", unit);
fprintf(f,
"After=%1$s\n"
"Requires=%1$s\n", unit);
} else {
/* Regular file, add mount dependency */
_cleanup_free_ char *escaped_path = specifier_escape(device_path);
+21 -73
View File
@@ -10,6 +10,7 @@
#include "alloc-util.h"
#include "ask-password-api.h"
#include "cryptsetup-pkcs11.h"
#include "cryptsetup-util.h"
#include "escape.h"
#include "fd-util.h"
#include "format-util.h"
@@ -19,73 +20,6 @@
#include "stat-util.h"
#include "strv.h"
#define KEY_FILE_SIZE_MAX (16U*1024U*1024U) /* 16 MiB */
static int load_key_file(
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
void **ret_encrypted_key,
size_t *ret_encrypted_key_size) {
_cleanup_(erase_and_freep) char *buffer = NULL;
_cleanup_close_ int fd = -1;
ssize_t n;
int r;
assert(key_file);
assert(ret_encrypted_key);
assert(ret_encrypted_key_size);
fd = open(key_file, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return log_error_errno(errno, "Failed to load encrypted PKCS#11 key: %m");
if (key_file_size == 0) {
struct stat st;
if (fstat(fd, &st) < 0)
return log_error_errno(errno, "Failed to stat key file: %m");
r = stat_verify_regular(&st);
if (r < 0)
return log_error_errno(r, "Key file is not a regular file: %m");
if (st.st_size == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file is empty, refusing.");
if ((uint64_t) st.st_size > KEY_FILE_SIZE_MAX) {
char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX];
return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
"Key file larger (%s) than allowed maximum size (%s), refusing.",
format_bytes(buf1, sizeof(buf1), st.st_size),
format_bytes(buf2, sizeof(buf2), KEY_FILE_SIZE_MAX));
}
if (key_file_offset >= (uint64_t) st.st_size)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file offset too large for file, refusing.");
key_file_size = st.st_size - key_file_offset;
}
buffer = malloc(key_file_size);
if (!buffer)
return log_oom();
if (key_file_offset > 0)
n = pread(fd, buffer, key_file_size, key_file_offset);
else
n = read(fd, buffer, key_file_size);
if (n < 0)
return log_error_errno(errno, "Failed to read PKCS#11 key file: %m");
if (n == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty encrypted key found, refusing.");
*ret_encrypted_key = TAKE_PTR(buffer);
*ret_encrypted_key_size = (size_t) n;
return 0;
}
struct pkcs11_callback_data {
const char *friendly_name;
usec_t until;
@@ -93,11 +27,14 @@ struct pkcs11_callback_data {
size_t encrypted_key_size;
void *decrypted_key;
size_t decrypted_key_size;
bool free_encrypted_key;
};
static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
free(data->decrypted_key);
free(data->encrypted_key);
if (data->free_encrypted_key)
free(data->encrypted_key);
}
static int pkcs11_callback(
@@ -160,9 +97,11 @@ static int pkcs11_callback(
int decrypt_pkcs11_key(
const char *friendly_name,
const char *pkcs11_uri,
const char *key_file,
const char *key_file, /* We either expect key_file and associated parameters to be set (for file keys) … */
size_t key_file_size,
uint64_t key_file_offset,
const void *key_data, /* … or key_data and key_data_size (for literal keys) */
size_t key_data_size,
usec_t until,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
@@ -175,15 +114,24 @@ int decrypt_pkcs11_key(
assert(friendly_name);
assert(pkcs11_uri);
assert(key_file);
assert(key_file || key_data);
assert(ret_decrypted_key);
assert(ret_decrypted_key_size);
/* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */
r = load_key_file(key_file, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size);
if (r < 0)
return r;
if (key_data) {
data.encrypted_key = (void*) key_data;
data.encrypted_key_size = key_data_size;
data.free_encrypted_key = false;
} else {
r = load_key_file(key_file, NULL, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size);
if (r < 0)
return r;
data.free_encrypted_key = true;
}
r = pkcs11_find_token(pkcs11_uri, pkcs11_callback, &data);
if (r < 0)
+4
View File
@@ -14,6 +14,8 @@ int decrypt_pkcs11_key(
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
const void *key_data,
size_t key_data_size,
usec_t until,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size);
@@ -26,6 +28,8 @@ static inline int decrypt_pkcs11_key(
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
const void *key_data,
size_t key_data_size,
usec_t until,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
+110
View File
@@ -0,0 +1,110 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <unistd.h>
#include "cryptsetup-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "memory-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "strv.h"
#define KEY_FILE_SIZE_MAX (16U*1024U*1024U) /* 16 MiB */
int load_key_file(
const char *key_file,
char **search_path,
size_t key_file_size,
uint64_t key_file_offset,
void **ret_key,
size_t *ret_key_size) {
_cleanup_(erase_and_freep) char *buffer = NULL;
_cleanup_free_ char *discovered_path = NULL;
_cleanup_close_ int fd = -1;
ssize_t n;
int r;
assert(key_file);
assert(ret_key);
assert(ret_key_size);
if (strv_isempty(search_path) || path_is_absolute(key_file)) {
fd = open(key_file, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return log_error_errno(errno, "Failed to load key file '%s': %m", key_file);
} else {
char **i;
STRV_FOREACH(i, search_path) {
_cleanup_free_ char *joined;
joined = path_join(*i, key_file);
if (!joined)
return log_oom();
fd = open(joined, O_RDONLY|O_CLOEXEC);
if (fd >= 0) {
discovered_path = TAKE_PTR(joined);
break;
}
if (errno != ENOENT)
return log_error_errno(errno, "Failed to load key file '%s': %m", joined);
}
if (!discovered_path) {
/* Search path supplied, but file not found, report by returning NULL, but not failing */
*ret_key = NULL;
*ret_key_size = 0;
return 0;
}
assert(fd >= 0);
key_file = discovered_path;
}
if (key_file_size == 0) {
struct stat st;
if (fstat(fd, &st) < 0)
return log_error_errno(errno, "Failed to stat key file '%s': %m", key_file);
r = stat_verify_regular(&st);
if (r < 0)
return log_error_errno(r, "Key file is not a regular file: %m");
if (st.st_size == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file is empty, refusing.");
if ((uint64_t) st.st_size > KEY_FILE_SIZE_MAX) {
char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX];
return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
"Key file larger (%s) than allowed maximum size (%s), refusing.",
format_bytes(buf1, sizeof(buf1), st.st_size),
format_bytes(buf2, sizeof(buf2), KEY_FILE_SIZE_MAX));
}
if (key_file_offset >= (uint64_t) st.st_size)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file offset too large for file, refusing.");
key_file_size = st.st_size - key_file_offset;
}
buffer = malloc(key_file_size);
if (!buffer)
return log_oom();
if (key_file_offset > 0)
n = pread(fd, buffer, key_file_size, key_file_offset);
else
n = read(fd, buffer, key_file_size);
if (n < 0)
return log_error_errno(errno, "Failed to read key file '%s': %m", key_file);
if (n == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty encrypted key found, refusing.");
*ret_key = TAKE_PTR(buffer);
*ret_key_size = (size_t) n;
return 1;
}
+13
View File
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <inttypes.h>
#include <sys/types.h>
int load_key_file(
const char *key_file,
char **search_path,
size_t key_file_size,
uint64_t key_file_offset,
void **ret_key,
size_t *ret_key_size);
+162 -36
View File
@@ -13,13 +13,16 @@
#include "ask-password-api.h"
#include "crypt-util.h"
#include "cryptsetup-pkcs11.h"
#include "cryptsetup-util.h"
#include "device-util.h"
#include "escape.h"
#include "fileio.h"
#include "fs-util.h"
#include "fstab-util.h"
#include "hexdecoct.h"
#include "log.h"
#include "main-func.h"
#include "memory-util.h"
#include "mount-util.h"
#include "nulstr-util.h"
#include "parse-util.h"
@@ -42,6 +45,8 @@ static unsigned arg_sector_size = CRYPT_SECTOR_SIZE;
static int arg_key_slot = CRYPT_ANY_SLOT;
static unsigned arg_keyfile_size = 0;
static uint64_t arg_keyfile_offset = 0;
static bool arg_keyfile_erase = false;
static bool arg_try_empty_password = false;
static char *arg_hash = NULL;
static char *arg_header = NULL;
static unsigned arg_tries = 3;
@@ -67,12 +72,14 @@ STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_uri, freep);
/* Options Debian's crypttab knows we don't:
precheck=
check=
checkargs=
noearly=
loud=
noearly
loud
quiet
keyscript=
tmp= (the version without argument is supported)
initramfs
*/
static int parse_one_option(const char *option) {
@@ -126,7 +133,8 @@ static int parse_one_option(const char *option) {
return 0;
}
} else if ((val = startswith(option, "key-slot="))) {
} else if ((val = startswith(option, "key-slot=")) ||
(val = startswith(option, "keyslot="))) {
arg_type = ANY_LUKS;
r = safe_atoi(val, &arg_key_slot);
@@ -160,7 +168,20 @@ static int parse_one_option(const char *option) {
return 0;
}
} else if ((val = startswith(option, "hash="))) {
} else if ((val = startswith(option, "keyfile-erase="))) {
r = parse_boolean(val);
if (r < 0) {
log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
arg_keyfile_erase = r;
} else if (streq(option, "keyfile-erase"))
arg_keyfile_erase = true;
else if ((val = startswith(option, "hash="))) {
r = free_and_strdup(&arg_hash, val);
if (r < 0)
return log_oom();
@@ -202,13 +223,13 @@ static int parse_one_option(const char *option) {
arg_type = ANY_LUKS;
else if (streq(option, "tcrypt"))
arg_type = CRYPT_TCRYPT;
else if (streq(option, "tcrypt-hidden")) {
else if (STR_IN_SET(option, "tcrypt-hidden", "tcrypthidden")) {
arg_type = CRYPT_TCRYPT;
arg_tcrypt_hidden = true;
} else if (streq(option, "tcrypt-system")) {
arg_type = CRYPT_TCRYPT;
arg_tcrypt_system = true;
} else if (streq(option, "tcrypt-veracrypt")) {
} else if (STR_IN_SET(option, "tcrypt-veracrypt", "veracrypt")) {
arg_type = CRYPT_TCRYPT;
arg_tcrypt_veracrypt = true;
} else if (STR_IN_SET(option, "plain", "swap", "tmp"))
@@ -242,7 +263,20 @@ static int parse_one_option(const char *option) {
if (r < 0)
return log_oom();
} else if (!streq(option, "x-initrd.attach"))
} else if ((val = startswith(option, "try-empty-password="))) {
r = parse_boolean(val);
if (r < 0) {
log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
arg_try_empty_password = r;
} else if (streq(option, "try-empty-password"))
arg_try_empty_password = true;
else if (!streq(option, "x-initrd.attach"))
log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
return 0;
@@ -440,6 +474,8 @@ static int attach_tcrypt(
struct crypt_device *cd,
const char *name,
const char *key_file,
const void *key_data,
size_t key_data_size,
char **passwords,
uint32_t flags) {
@@ -453,7 +489,7 @@ static int attach_tcrypt(
assert(cd);
assert(name);
assert(key_file || (passwords && passwords[0]));
assert(key_file || key_data || !strv_isempty(passwords));
if (arg_pkcs11_uri)
/* Ask for a regular password */
@@ -469,22 +505,33 @@ static int attach_tcrypt(
if (arg_tcrypt_veracrypt)
params.flags |= CRYPT_TCRYPT_VERA_MODES;
if (key_file) {
r = read_one_line_file(key_file, &passphrase);
if (r < 0) {
log_error_errno(r, "Failed to read password file '%s': %m", key_file);
return -EAGAIN; /* log with the actual error, but return EAGAIN */
}
if (key_data) {
params.passphrase = key_data;
params.passphrase_size = key_data_size;
} else {
if (key_file) {
r = read_one_line_file(key_file, &passphrase);
if (r < 0) {
log_error_errno(r, "Failed to read password file '%s': %m", key_file);
return -EAGAIN; /* log with the actual error, but return EAGAIN */
}
params.passphrase = passphrase;
} else
params.passphrase = passwords[0];
params.passphrase_size = strlen(params.passphrase);
params.passphrase = passphrase;
} else
params.passphrase = passwords[0];
params.passphrase_size = strlen(params.passphrase);
}
r = crypt_load(cd, CRYPT_TCRYPT, &params);
if (r < 0) {
if (key_file && r == -EPERM) {
log_error_errno(r, "Failed to activate using password file '%s'. (Key data not correct?)", key_file);
if (r == -EPERM) {
if (key_data)
log_error_errno(r, "Failed to activate using discovered key. (Key not correct?)");
if (key_file)
log_error_errno(r, "Failed to activate using password file '%s'. (Key data not correct?)", key_file);
return -EAGAIN; /* log the actual error, but return EAGAIN */
}
@@ -502,6 +549,8 @@ static int attach_luks_or_plain(
struct crypt_device *cd,
const char *name,
const char *key_file,
const void *key_data,
size_t key_data_size,
char **passwords,
uint32_t flags,
usec_t until) {
@@ -571,7 +620,7 @@ static int attach_luks_or_plain(
_cleanup_free_ char *friendly = NULL;
size_t decrypted_key_size = 0;
if (!key_file)
if (!key_file && !key_data)
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);
@@ -584,8 +633,8 @@ static int attach_luks_or_plain(
r = decrypt_pkcs11_key(
friendly,
arg_pkcs11_uri,
key_file,
arg_keyfile_size, arg_keyfile_offset,
key_file, arg_keyfile_size, arg_keyfile_offset,
key_data, key_data_size,
until,
&decrypted_key, &decrypted_key_size);
if (r >= 0)
@@ -620,7 +669,7 @@ static int attach_luks_or_plain(
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);
arg_pkcs11_uri, friendly);
/* Let's immediately rescan in case the token appeared in the time we needed
* to create and configure the monitor */
@@ -668,6 +717,18 @@ static int attach_luks_or_plain(
if (r < 0)
return log_error_errno(r, "Failed to activate with PKCS#11 acquired key: %m");
} else if (key_data) {
if (pass_volume_key)
r = crypt_activate_by_volume_key(cd, name, key_data, key_data_size, flags);
else
r = crypt_activate_by_passphrase(cd, name, arg_key_slot, key_data, key_data_size, flags);
if (r == -EPERM) {
log_error_errno(r, "Failed to activate. (Key incorrect?)");
return -EAGAIN; /* Log actual error, but return EAGAIN */
}
if (r < 0)
return log_error_errno(r, "Failed to activate: %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) {
@@ -748,6 +809,17 @@ static uint32_t determine_flags(void) {
return flags;
}
static void remove_and_erasep(const char **p) {
int r;
if (!*p)
return;
r = unlinkat_deallocate(AT_FDCWD, *p, UNLINK_ERASE);
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Unable to erase key file '%s', ignoring: %m", *p);
}
static int run(int argc, char *argv[]) {
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
int r;
@@ -774,13 +846,19 @@ static int run(int argc, char *argv[]) {
unsigned tries;
usec_t until;
crypt_status_info status;
_cleanup_(remove_and_erasep) const char *destroy_key_file = NULL;
const char *key_file = NULL;
_cleanup_(erase_and_freep) void *key_data = NULL;
size_t key_data_size = 0;
/* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
if (argc < 4)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least two arguments.");
if (!filename_is_valid(argv[2]))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", argv[2]);
if (argc >= 5 && !STR_IN_SET(argv[4], "", "-", "none")) {
if (path_is_absolute(argv[4]))
key_file = argv[4];
@@ -797,6 +875,24 @@ static int run(int argc, char *argv[]) {
/* A delicious drop of snake oil */
(void) mlockall(MCL_FUTURE);
if (!key_file) {
const char *fn;
/* If a key file is not explicitly specified, search for a key in a well defined
* search path, and load it. */
fn = strjoina(argv[2], ".key");
r = load_key_file(fn,
STRV_MAKE("/etc/cryptsetup-keys.d", "/run/cryptsetup-keys.d"),
0, 0, /* Note we leave arg_keyfile_offset/arg_keyfile_size as something that only applies to arg_keyfile! */
&key_data, &key_data_size);
if (r < 0)
return r;
if (r > 0)
log_debug("Automatically discovered key for volume '%s'.", argv[2]);
} else if (arg_keyfile_erase)
destroy_key_file = key_file; /* let's get this baby erased when we leave */
if (arg_header) {
log_debug("LUKS header: %s", arg_header);
r = crypt_init(&cd, arg_header);
@@ -843,7 +939,7 @@ static int run(int argc, char *argv[]) {
}
/* Tokens are available in LUKS2 only, but it is ok to call (and fail) with LUKS1. */
if (!key_file) {
if (!key_file && !key_data) {
r = crypt_activate_by_token(cd, argv[2], CRYPT_ANY_TOKEN, NULL, flags);
if (r >= 0) {
log_debug("Volume %s activated with LUKS token id %i.", argv[2], r);
@@ -857,26 +953,53 @@ 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 && !arg_pkcs11_uri) {
r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
if (r == -EAGAIN)
continue;
if (r < 0)
return r;
/* When we were able to acquire multiple keys, let's always process them in this order:
*
* 1. A key acquired via PKCS#11 token
* 2. The discovered key: i.e. key_data + key_data_size
* 3. The configured key: i.e. key_file + arg_keyfile_offset + arg_keyfile_size
* 4. The empty password, in case arg_try_empty_password is set
* 5. We enquire the user for a password
*/
if (!key_file && !key_data && !arg_pkcs11_uri) {
if (arg_try_empty_password) {
/* Hmm, let's try an empty password now, but only once */
arg_try_empty_password = false;
key_data = strdup("");
if (!key_data)
return log_oom();
key_data_size = 0;
} else {
/* Ask the user for a passphrase only as last resort, if we have
* nothing else to check for */
r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
if (r == -EAGAIN)
continue;
if (r < 0)
return r;
}
}
if (streq_ptr(arg_type, CRYPT_TCRYPT))
r = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
r = attach_tcrypt(cd, argv[2], key_file, key_data, key_data_size, passwords, flags);
else
r = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags, until);
r = attach_luks_or_plain(cd, argv[2], key_file, key_data, key_data_size, passwords, flags, until);
if (r >= 0)
break;
if (r != -EAGAIN)
return r;
/* Passphrase not correct? Let's try again! */
/* Key not correct? Let's try again! */
key_file = NULL;
arg_pkcs11_uri = NULL;
key_data = erase_and_free(key_data);
key_data_size = 0;
arg_pkcs11_uri = mfree(arg_pkcs11_uri);
}
if (arg_tries != 0 && tries >= arg_tries)
@@ -884,6 +1007,9 @@ static int run(int argc, char *argv[]) {
} else if (streq(argv[1], "detach")) {
if (!filename_is_valid(argv[2]))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", argv[2]);
r = crypt_init_by_name(&cd, argv[2]);
if (r == -ENODEV) {
log_info("Volume %s already inactive.", argv[2]);
+1 -1
View File
@@ -670,7 +670,7 @@ static void test_unlinkat_deallocate(void) {
assert_se(st.st_blocks > 0);
assert_se(st.st_nlink == 1);
assert_se(unlinkat_deallocate(AT_FDCWD, p, 0) >= 0);
assert_se(unlinkat_deallocate(AT_FDCWD, p, UNLINK_ERASE) >= 0);
assert_se(fstat(fd, &st) >= 0);
assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6