diff --git a/meson.build b/meson.build index 121148f8dd3..f485616d10d 100644 --- a/meson.build +++ b/meson.build @@ -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 diff --git a/src/cryptsetup/cryptsetup-pkcs11.c b/src/cryptsetup/cryptsetup-pkcs11.c index ec9186a6aaa..97a8c68ee8b 100644 --- a/src/cryptsetup/cryptsetup-pkcs11.c +++ b/src/cryptsetup/cryptsetup-pkcs11.c @@ -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; @@ -181,7 +115,7 @@ int decrypt_pkcs11_key( /* 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); + 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; diff --git a/src/cryptsetup/cryptsetup-util.c b/src/cryptsetup/cryptsetup-util.c new file mode 100644 index 00000000000..8ae70a5d10e --- /dev/null +++ b/src/cryptsetup/cryptsetup-util.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#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; +} diff --git a/src/cryptsetup/cryptsetup-util.h b/src/cryptsetup/cryptsetup-util.h new file mode 100644 index 00000000000..7bb78227dcf --- /dev/null +++ b/src/cryptsetup/cryptsetup-util.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +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);