| /* 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; |
| } |