diff options
Diffstat (limited to 'src/cryptsetup/cryptsetup-keyfile.c')
-rw-r--r-- | src/cryptsetup/cryptsetup-keyfile.c | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/src/cryptsetup/cryptsetup-keyfile.c b/src/cryptsetup/cryptsetup-keyfile.c new file mode 100644 index 0000000..f849123 --- /dev/null +++ b/src/cryptsetup/cryptsetup-keyfile.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <unistd.h> + +#include "cryptsetup-keyfile.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; +} |