diff --git a/TODO b/TODO index 89f7455e5b..bd5bdda744 100644 --- a/TODO +++ b/TODO @@ -76,9 +76,6 @@ Features: * make use of new glibc 2.32 APIs sigabbrev_np() and strerrorname_np(). -* cryptsetup: if keyfile specified in crypttab is AF_UNIX socket, connect to it - and read from it (like we do elsewhere with READ_FULL_FILE_CONNECT_SOCKET) - * when main nspawn supervisor process gets suspended due to SIGSTOP/SIGTTOU or so, freeze the payload too. diff --git a/man/crypttab.xml b/man/crypttab.xml index 14bf93a0f7..93c0ec01e4 100644 --- a/man/crypttab.xml +++ b/man/crypttab.xml @@ -53,25 +53,36 @@ it is opened as a LUKS device; otherwise, it is assumed to be in raw dm-crypt (plain mode) format. - The first field contains the name of the resulting encrypted volume; its block device is set up - below /dev/mapper/. + The four fields of /etc/crypttab are defined as follows: - The second field contains a path to the underlying block - device or file, or a specification of a block device via - UUID= followed by the UUID. + - The third field specifies an absolute path to a file with the encryption key. Optionally, - the path may be followed by : and an fstab device specification (e.g. starting with - LABEL= or similar); in which case the path is taken relative to the device file system - root. If the field is not present or is none or -, a key file - named after the volume to unlock (i.e. the first column of the line), suffixed with - .key is automatically loaded from the /etc/cryptsetup-keys.d/ - and /run/cryptsetup-keys.d/ directories, if present. Otherwise, the password has to - be manually entered during system boot. For swap encryption, /dev/urandom may be - used as key file. + The first field contains the name of the resulting volume with decrypted data; its + block device is set up below /dev/mapper/. - The fourth field, if present, is a comma-delimited list of - options. The following options are recognized: + The second field contains a path to the underlying block + device or file, or a specification of a block device via + UUID= followed by the UUID. + + The third field specifies an absolute path to a file with the encryption + key. Optionally, the path may be followed by : and an fstab device specification + (e.g. starting with LABEL= or similar); in which case the path is taken relative to + the device file system root. If the field is not present or is none or + -, a key file named after the volume to unlock (i.e. the first column of the line), + suffixed with .key is automatically loaded from the + /etc/cryptsetup-keys.d/ and /run/cryptsetup-keys.d/ + directories, if present. Otherwise, the password has to be manually entered during system boot. For + swap encryption, /dev/urandom may be used as key file, resulting in a randomized + key. + + If the specified key file path refers to an AF_UNIX stream socket in the + file system, the key is acquired by connecting to the socket and reading it from the connection. This + allows the implementation of a service to provide key information dynamically, at the moment when it is + needed. For details see below. + + The fourth field, if present, is a comma-delimited list of options. The suppported + options are listed below. + @@ -499,6 +510,34 @@ systemd-cryptsetup-generator8. + + <constant>AF_UNIX</constant> Key Files + + If the key file path (as specified in the third column of /etc/crypttab + entries, see above) refers to an AF_UNIX stream socket in the file system, the key + is acquired by connecting to the socket and reading the key from the connection. The connection is made + from an AF_UNIX socket name in the abstract namespace, see unix7 for + details. The source socket name is chosen according the following format: + + NUL RANDOM /cryptsetup/ VOLUME + + In other words: a NUL byte (as required for abstract namespace sockets), + followed by a random string (consisting of alphabenumeric characters only), followed by the literal + string /cryptsetup/, followed by the name of the volume to acquire they key + for. Example (for a volume myvol): + + \0d7067f78d9827418/cryptsetup/myvol + + Services listening on the AF_UNIX stream socket may query the source socket + name with getpeername2, + and use it to determine which key to send, allowing a single listening socket to serve keys for a + multitude of volumes. If the PKCS#11 logic is used (see below) the socket source name is picked in + identical fashion, except that the literal string /cryptsetup-pkcs11/ is used. This is + done so that services providing key material know that not a secret key is requested but an encrypted key + that will be decrypted via the PKCS#11 logic to acquire the final secret key. + Examples @@ -529,7 +568,6 @@ external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10s,cipher=xchac A few notes on the above: - We use RSA (and not ECC), since Yubikeys support PKCS#11 Decrypt() only for RSA keys We use RSA2048, which is the longest key size current Yubikeys support LUKS key size must be shorter than 2048bit due to RSA padding, hence we use 128 bytes We use Yubikey key slot 9d, since that's apparently the keyslot to use for decryption purposes, diff --git a/man/systemd-journal-gatewayd.service.xml b/man/systemd-journal-gatewayd.service.xml index 61a4037669..43ceb97032 100644 --- a/man/systemd-journal-gatewayd.service.xml +++ b/man/systemd-journal-gatewayd.service.xml @@ -68,8 +68,8 @@ Specify the path to a file or AF_UNIX stream socket to read the - server key corresponding to the certificate specified with from. The key - must be in PEM format. + secret server key corresponding to the certificate specified with from. The + key must be in PEM format. diff --git a/man/systemd-journal-remote.service.xml b/man/systemd-journal-remote.service.xml index 6e068a617a..bea0936d66 100644 --- a/man/systemd-journal-remote.service.xml +++ b/man/systemd-journal-remote.service.xml @@ -180,7 +180,7 @@ - Takes a path to a SSL key file in PEM format. Defaults to + Takes a path to a SSL secret key file in PEM format. Defaults to &CERTIFICATE_ROOT;/private/journal-remote.pem. This option can be used with . If the path refers to an AF_UNIX stream socket in the file system a connection is made to it and the key read from it. diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 973756c891..f4708bc05f 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -472,12 +472,13 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re int read_full_stream_full( FILE *f, const char *filename, + uint64_t offset, + size_t size, ReadFullFileFlags flags, char **ret_contents, size_t *ret_size) { _cleanup_free_ char *buf = NULL; - struct stat st; size_t n, n_next, l; int fd, r; @@ -485,32 +486,45 @@ int read_full_stream_full( assert(ret_contents); assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)); - n_next = LINE_MAX; /* Start size */ + if (offset != UINT64_MAX && offset > LONG_MAX) + return -ERANGE; + + n_next = size != SIZE_MAX ? size : LINE_MAX; /* Start size */ fd = fileno(f); - if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen()), let's - * optimize our buffering */ + if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see + * fmemopen()), let's optimize our buffering */ + struct stat st; if (fstat(fd, &st) < 0) return -errno; if (S_ISREG(st.st_mode)) { + if (size == SIZE_MAX) { + uint64_t rsize = + LESS_BY((uint64_t) st.st_size, offset == UINT64_MAX ? 0 : offset); - /* Safety check */ - if (st.st_size > READ_FULL_BYTES_MAX) - return -E2BIG; + /* Safety check */ + if (rsize > READ_FULL_BYTES_MAX) + return -E2BIG; - /* Start with the right file size. Note that we increase the size - * to read here by one, so that the first read attempt already - * makes us notice the EOF. */ - if (st.st_size > 0) - n_next = st.st_size + 1; + /* Start with the right file size. Note that we increase the size to read + * here by one, so that the first read attempt already makes us notice the + * EOF. If the reported size of the file is zero, we avoid this logic + * however, since quite likely it might be a virtual file in procfs that all + * report a zero file size. */ + if (st.st_size > 0) + n_next = rsize + 1; + } if (flags & READ_FULL_FILE_WARN_WORLD_READABLE) (void) warn_file_is_world_accessible(filename, &st, NULL, 0); } } + if (offset != UINT64_MAX && fseek(f, offset, SEEK_SET) < 0) + return -errno; + n = l = 0; for (;;) { char *t; @@ -547,6 +561,11 @@ int read_full_stream_full( if (feof(f)) break; + if (size != SIZE_MAX) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */ + assert(l == size); + break; + } + assert(k > 0); /* we can't have read zero bytes because that would have been EOF */ /* Safety check */ @@ -605,15 +624,18 @@ finalize: int read_full_file_full( int dir_fd, const char *filename, + uint64_t offset, + size_t size, ReadFullFileFlags flags, const char *bind_name, - char **contents, size_t *size) { + char **ret_contents, + size_t *ret_size) { _cleanup_fclose_ FILE *f = NULL; int r; assert(filename); - assert(contents); + assert(ret_contents); r = xfopenat(dir_fd, filename, "re", 0, &f); if (r < 0) { @@ -628,6 +650,10 @@ int read_full_file_full( if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET)) return -ENXIO; + /* Seeking is not supported on AF_UNIX sockets */ + if (offset != UINT64_MAX) + return -ESPIPE; + if (dir_fd == AT_FDCWD) r = sockaddr_un_set_path(&sa.un, filename); else { @@ -681,7 +707,7 @@ int read_full_file_full( (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - return read_full_stream_full(f, filename, flags, contents, size); + return read_full_stream_full(f, filename, offset, size, flags, ret_contents, ret_size); } int executable_is_script(const char *path, char **interpreter) { diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 0886354cbc..498e880354 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -60,14 +60,14 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); int read_one_line_file(const char *filename, char **line); -int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, const char *bind_name, char **contents, size_t *size); -static inline int read_full_file(const char *filename, char **contents, size_t *size) { - return read_full_file_full(AT_FDCWD, filename, 0, NULL, contents, size); +int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, const char *bind_name, char **ret_contents, size_t *ret_size); +static inline int read_full_file(const char *filename, char **ret_contents, size_t *ret_size) { + return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size); } int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size); -int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); -static inline int read_full_stream(FILE *f, char **contents, size_t *size) { - return read_full_stream_full(f, NULL, 0, contents, size); +int read_full_stream_full(FILE *f, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, char **ret_contents, size_t *ret_size); +static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_size) { + return read_full_stream_full(f, NULL, UINT64_MAX, SIZE_MAX, 0, ret_contents, ret_size); } int verify_file(const char *fn, const char *blob, bool accept_extra_nl); diff --git a/src/core/execute.c b/src/core/execute.c index f1f3744191..56793c5a43 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2576,7 +2576,7 @@ static int acquire_credentials( if (source) - r = read_full_file_full(AT_FDCWD, source, flags, bindname, &data, &size); + r = read_full_file_full(AT_FDCWD, source, UINT64_MAX, SIZE_MAX, flags, bindname, &data, &size); else r = -ENOENT; if (r == -ENOENT && diff --git a/src/cryptsetup/cryptsetup-keyfile.c b/src/cryptsetup/cryptsetup-keyfile.c index f849123563..a6281fbdee 100644 --- a/src/cryptsetup/cryptsetup-keyfile.c +++ b/src/cryptsetup/cryptsetup-keyfile.c @@ -1,29 +1,18 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include - #include "cryptsetup-keyfile.h" -#include "fd-util.h" -#include "format-util.h" -#include "memory-util.h" +#include "fileio.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( +int find_key_file( const char *key_file, char **search_path, - size_t key_file_size, - uint64_t key_file_offset, + const char *bindname, 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; + char **i; int r; assert(key_file); @@ -31,80 +20,38 @@ int load_key_file( 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); + r = read_full_file_full( + AT_FDCWD, key_file, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + bindname, + (char**) ret_key, ret_key_size); if (r < 0) - return log_error_errno(r, "Key file is not a regular file: %m"); + return log_error_errno(r, "Failed to load key file '%s': %m", key_file); - 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; + return 1; } - buffer = malloc(key_file_size); - if (!buffer) - return log_oom(); + STRV_FOREACH(i, search_path) { + _cleanup_free_ char *joined; - 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."); + joined = path_join(*i, key_file); + if (!joined) + return log_oom(); - *ret_key = TAKE_PTR(buffer); - *ret_key_size = (size_t) n; + r = read_full_file_full( + AT_FDCWD, joined, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + bindname, + (char**) ret_key, ret_key_size); + if (r >= 0) + return 1; + if (r != -ENOENT) + return log_error_errno(r, "Failed to load key file '%s': %m", key_file); + } - return 1; + /* Search path supplied, but file not found, report by returning NULL, but not failing */ + *ret_key = NULL; + *ret_key_size = 0; + return 0; } diff --git a/src/cryptsetup/cryptsetup-keyfile.h b/src/cryptsetup/cryptsetup-keyfile.h index 308f5ebd68..83bd1fbed2 100644 --- a/src/cryptsetup/cryptsetup-keyfile.h +++ b/src/cryptsetup/cryptsetup-keyfile.h @@ -4,10 +4,9 @@ #include #include -int load_key_file( +int find_key_file( const char *key_file, char **search_path, - size_t key_file_size, - uint64_t key_file_offset, + const char *bindname, void **ret_key, size_t *ret_key_size); diff --git a/src/cryptsetup/cryptsetup-pkcs11.c b/src/cryptsetup/cryptsetup-pkcs11.c index 50db46f8d1..b645ff28e0 100644 --- a/src/cryptsetup/cryptsetup-pkcs11.c +++ b/src/cryptsetup/cryptsetup-pkcs11.c @@ -10,13 +10,14 @@ #include "alloc-util.h" #include "ask-password-api.h" #include "cryptsetup-pkcs11.h" -#include "cryptsetup-keyfile.h" #include "escape.h" #include "fd-util.h" +#include "fileio.h" #include "format-util.h" #include "macro.h" #include "memory-util.h" #include "pkcs11-util.h" +#include "random-util.h" #include "stat-util.h" #include "strv.h" @@ -95,6 +96,7 @@ static int pkcs11_callback( } int decrypt_pkcs11_key( + const char *volume_name, const char *friendly_name, const char *pkcs11_uri, const char *key_file, /* We either expect key_file and associated parameters to be set (for file keys) … */ @@ -126,7 +128,19 @@ int decrypt_pkcs11_key( 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); + _cleanup_free_ char *bindname = NULL; + + /* If we read the key via AF_UNIX, make this client recognizable */ + if (asprintf(&bindname, "@%" PRIx64"/cryptsetup-pkcs11/%s", random_u64(), volume_name) < 0) + return log_oom(); + + r = read_full_file_full( + AT_FDCWD, key_file, + key_file_offset == 0 ? UINT64_MAX : key_file_offset, + key_file_size == 0 ? SIZE_MAX : key_file_size, + READ_FULL_FILE_CONNECT_SOCKET, + bindname, + (char**) &data.encrypted_key, &data.encrypted_key_size); if (r < 0) return r; diff --git a/src/cryptsetup/cryptsetup-pkcs11.h b/src/cryptsetup/cryptsetup-pkcs11.h index 266c8e1b3e..522ed28bd3 100644 --- a/src/cryptsetup/cryptsetup-pkcs11.h +++ b/src/cryptsetup/cryptsetup-pkcs11.h @@ -9,6 +9,7 @@ #if HAVE_P11KIT int decrypt_pkcs11_key( + const char *volume_name, const char *friendly_name, const char *pkcs11_uri, const char *key_file, @@ -23,6 +24,7 @@ int decrypt_pkcs11_key( #else static inline int decrypt_pkcs11_key( + const char *volume_name, const char *friendly_name, const char *pkcs11_uri, const char *key_file, diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 5f9d7bb3bb..78d8eec1d7 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -29,6 +29,7 @@ #include "path-util.h" #include "pkcs11-util.h" #include "pretty-print.h" +#include "random-util.h" #include "string-util.h" #include "strv.h" @@ -550,6 +551,15 @@ static int attach_tcrypt( return 0; } +static char *make_bindname(const char *volume) { + char *s; + + if (asprintf(&s, "@%" PRIx64"/cryptsetup/%s", random_u64(), volume) < 0) + return NULL; + + return s; +} + static int attach_luks_or_plain_or_bitlk( struct crypt_device *cd, const char *name, @@ -636,6 +646,7 @@ static int attach_luks_or_plain_or_bitlk( bool processed = false; r = decrypt_pkcs11_key( + name, friendly, arg_pkcs11_uri, key_file, arg_keyfile_size, arg_keyfile_offset, @@ -735,13 +746,30 @@ static int attach_luks_or_plain_or_bitlk( 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) { - log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file); + _cleanup_(erase_and_freep) char *kfdata = NULL; + _cleanup_free_ char *bindname = NULL; + size_t kfsize; + + /* If we read the key via AF_UNIX, make this client recognizable */ + bindname = make_bindname(name); + if (!bindname) + return log_oom(); + + r = read_full_file_full( + AT_FDCWD, key_file, + arg_keyfile_offset == 0 ? UINT64_MAX : arg_keyfile_offset, + arg_keyfile_size == 0 ? SIZE_MAX : arg_keyfile_size, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + bindname, + &kfdata, &kfsize); + if (r == -ENOENT) { + log_error_errno(r, "Failed to activate, key file '%s' missing.", key_file); return -EAGAIN; /* Log actual error, but return EAGAIN */ } - if (r == -EINVAL) { - log_error_errno(r, "Failed to activate with key file '%s'. (Key file missing?)", key_file); + + r = crypt_activate_by_passphrase(cd, name, arg_key_slot, kfdata, kfsize, flags); + if (r == -EPERM) { + log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file); return -EAGAIN; /* Log actual error, but return EAGAIN */ } if (r < 0) @@ -880,16 +908,22 @@ static int run(int argc, char *argv[]) { (void) mlockall(MCL_FUTURE); if (!key_file) { + _cleanup_free_ char *bindname = NULL; const char *fn; + bindname = make_bindname(argv[2]); + if (!bindname) + return log_oom(); + /* 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); + r = find_key_file( + fn, + STRV_MAKE("/etc/cryptsetup-keys.d", "/run/cryptsetup-keys.d"), + bindname, + &key_data, &key_data_size); if (r < 0) return r; if (r > 0) diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 0723f7d8bb..534de51b0f 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -22,6 +22,7 @@ #include "log.h" #include "logs-show.h" #include "main-func.h" +#include "memory-util.h" #include "microhttpd-util.h" #include "os-util.h" #include "parse-util.h" @@ -37,7 +38,7 @@ static char *arg_cert_pem = NULL; static char *arg_trust_pem = NULL; static const char *arg_directory = NULL; -STATIC_DESTRUCTOR_REGISTER(arg_key_pem, freep); +STATIC_DESTRUCTOR_REGISTER(arg_key_pem, erase_and_freep); STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep); STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep); @@ -896,7 +897,11 @@ static int parse_argv(int argc, char *argv[]) { if (arg_key_pem) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file specified twice"); - r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_key_pem, NULL); + r = read_full_file_full( + AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &arg_key_pem, NULL); if (r < 0) return log_error_errno(r, "Failed to read key file: %m"); assert(arg_key_pem); @@ -906,7 +911,11 @@ static int parse_argv(int argc, char *argv[]) { if (arg_cert_pem) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Certificate file specified twice"); - r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_cert_pem, NULL); + r = read_full_file_full( + AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &arg_cert_pem, NULL); if (r < 0) return log_error_errno(r, "Failed to read certificate file: %m"); assert(arg_cert_pem); @@ -917,14 +926,18 @@ static int parse_argv(int argc, char *argv[]) { if (arg_trust_pem) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "CA certificate file specified twice"); - r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_trust_pem, NULL); + r = read_full_file_full( + AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &arg_trust_pem, NULL); if (r < 0) return log_error_errno(r, "Failed to read CA certificate file: %m"); assert(arg_trust_pem); break; #else return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Option --trust is not available."); + "Option --trust= is not available."); #endif case 'D': arg_directory = optarg; diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index d2aa1815c2..af8cde7eef 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -13,6 +13,7 @@ #include "journal-remote-write.h" #include "journal-remote.h" #include "main-func.h" +#include "memory-util.h" #include "pretty-print.h" #include "process-util.h" #include "rlimit-util.h" @@ -1077,12 +1078,20 @@ static int parse_argv(int argc, char *argv[]) { static int load_certificates(char **key, char **cert, char **trust) { int r; - r = read_full_file_full(AT_FDCWD, arg_key ?: PRIV_KEY_FILE, READ_FULL_FILE_CONNECT_SOCKET, NULL, key, NULL); + r = read_full_file_full( + AT_FDCWD, arg_key ?: PRIV_KEY_FILE, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + NULL, + key, NULL); if (r < 0) return log_error_errno(r, "Failed to read key from file '%s': %m", arg_key ?: PRIV_KEY_FILE); - r = read_full_file_full(AT_FDCWD, arg_cert ?: CERT_FILE, READ_FULL_FILE_CONNECT_SOCKET, NULL, cert, NULL); + r = read_full_file_full( + AT_FDCWD, arg_cert ?: CERT_FILE, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_CONNECT_SOCKET, + NULL, + cert, NULL); if (r < 0) return log_error_errno(r, "Failed to read certificate from file '%s': %m", arg_cert ?: CERT_FILE); @@ -1090,7 +1099,11 @@ static int load_certificates(char **key, char **cert, char **trust) { if (arg_trust_all) log_info("Certificate checking disabled."); else { - r = read_full_file_full(AT_FDCWD, arg_trust ?: TRUST_FILE, READ_FULL_FILE_CONNECT_SOCKET, NULL, trust, NULL); + r = read_full_file_full( + AT_FDCWD, arg_trust ?: TRUST_FILE, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_CONNECT_SOCKET, + NULL, + trust, NULL); if (r < 0) return log_error_errno(r, "Failed to read CA certificate file '%s': %m", arg_trust ?: TRUST_FILE); @@ -1106,7 +1119,8 @@ static int load_certificates(char **key, char **cert, char **trust) { static int run(int argc, char **argv) { _cleanup_(journal_remote_server_destroy) RemoteServer s = {}; _cleanup_(notify_on_cleanup) const char *notify_message = NULL; - _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL; + _cleanup_(erase_and_freep) char *key = NULL; + _cleanup_free_ char *cert = NULL, *trust = NULL; int r; log_show_color(true); diff --git a/src/network/netdev/macsec.c b/src/network/netdev/macsec.c index 313277ca16..27fc2fd5e5 100644 --- a/src/network/netdev/macsec.c +++ b/src/network/netdev/macsec.c @@ -986,7 +986,7 @@ static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) { (void) warn_file_is_world_accessible(sa->key_file, NULL, NULL, 0); r = read_full_file_full( - AT_FDCWD, sa->key_file, + AT_FDCWD, sa->key_file, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET, NULL, (char **) &key, &key_len); if (r < 0) diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index 416e9b92d1..76444bdd7c 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -869,7 +869,7 @@ static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_ (void) warn_file_is_world_accessible(filename, NULL, NULL, 0); r = read_full_file_full( - AT_FDCWD, filename, + AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET, NULL, &key, &key_len); if (r < 0) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 0842731c18..ad2f572869 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1589,7 +1589,10 @@ static int parse_argv(int argc, char *argv[]) { return log_oom(); } - r = read_full_file_full(AT_FDCWD, j ?: p, flags, NULL, &data, &size); + r = read_full_file_full(AT_FDCWD, j ?: p, UINT64_MAX, SIZE_MAX, + flags, + NULL, + &data, &size); if (r < 0) return log_error_errno(r, "Failed to read credential '%s': %m", j ?: p); diff --git a/src/partition/repart.c b/src/partition/repart.c index 6db413ed5e..ddb9476cec 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -3621,7 +3621,11 @@ static int parse_argv(int argc, char *argv[]) { _cleanup_(erase_and_freep) char *k = NULL; size_t n = 0; - r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_SECURE|READ_FULL_FILE_CONNECT_SOCKET, NULL, &k, &n); + r = read_full_file_full( + AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &k, &n); if (r < 0) return log_error_errno(r, "Failed to read key file '%s': %m", optarg); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index b32a8bc4e4..c2c46e1816 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -2115,7 +2115,7 @@ int verity_settings_load( if (verity->root_hash && !verity->root_hash_sig) { if (root_hash_sig_path) { - r = read_full_file_full(AT_FDCWD, root_hash_sig_path, 0, NULL, (char**) &root_hash_sig, &root_hash_sig_size); + r = read_full_file(root_hash_sig_path, (char**) &root_hash_sig, &root_hash_sig_size); if (r < 0 && r != -ENOENT) return r; @@ -2131,7 +2131,7 @@ int verity_settings_load( if (!p) return -ENOMEM; - r = read_full_file_full(AT_FDCWD, p, 0, NULL, (char**) &root_hash_sig, &root_hash_sig_size); + r = read_full_file(p, (char**) &root_hash_sig, &root_hash_sig_size); if (r < 0 && r != -ENOENT) return r; if (r >= 0) @@ -2145,7 +2145,7 @@ int verity_settings_load( if (!p) return -ENOMEM; - r = read_full_file_full(AT_FDCWD, p, 0, NULL, (char**) &root_hash_sig, &root_hash_sig_size); + r = read_full_file(p, (char**) &root_hash_sig, &root_hash_sig_size); if (r < 0 && r != -ENOENT) return r; if (r >= 0) diff --git a/src/shared/json.c b/src/shared/json.c index ddf6dcb663..28fe482749 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -3195,7 +3195,7 @@ int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags fla if (f) r = read_full_stream(f, &text, NULL); else if (path) - r = read_full_file_full(dir_fd, path, 0, NULL, &text, NULL); + r = read_full_file_full(dir_fd, path, UINT64_MAX, SIZE_MAX, 0, NULL, &text, NULL); else return -EINVAL; if (r < 0) diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 431aea07ef..a5834eba36 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -911,8 +911,8 @@ static void test_read_full_file_socket(void) { _exit(EXIT_SUCCESS); } - assert_se(read_full_file_full(AT_FDCWD, j, 0, NULL, &data, &size) == -ENXIO); - assert_se(read_full_file_full(AT_FDCWD, j, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0); + assert_se(read_full_file_full(AT_FDCWD, j, UINT64_MAX, SIZE_MAX, 0, NULL, &data, &size) == -ENXIO); + assert_se(read_full_file_full(AT_FDCWD, j, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0); assert_se(size == strlen(TEST_STR)); assert_se(streq(data, TEST_STR)); @@ -920,6 +920,50 @@ static void test_read_full_file_socket(void) { #undef TEST_STR } +static void test_read_full_file_offset_size(void) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_(unlink_and_freep) char *fn = NULL; + _cleanup_free_ char *rbuf = NULL; + size_t rbuf_size; + uint8_t buf[4711]; + + random_bytes(buf, sizeof(buf)); + + assert_se(tempfn_random_child(NULL, NULL, &fn) >= 0); + assert_se(f = fopen(fn, "we")); + assert_se(fwrite(buf, 1, sizeof(buf), f) == sizeof(buf)); + assert_se(fflush_and_check(f) >= 0); + + assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, SIZE_MAX, 0, NULL, &rbuf, &rbuf_size) >= 0); + assert_se(rbuf_size == sizeof(buf)); + assert_se(memcmp(buf, rbuf, rbuf_size) == 0); + rbuf = mfree(rbuf); + + assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, 128, 0, NULL, &rbuf, &rbuf_size) >= 0); + assert_se(rbuf_size == 128); + assert_se(memcmp(buf, rbuf, rbuf_size) == 0); + rbuf = mfree(rbuf); + + assert_se(read_full_file_full(AT_FDCWD, fn, 1234, SIZE_MAX, 0, NULL, &rbuf, &rbuf_size) >= 0); + assert_se(rbuf_size == sizeof(buf) - 1234); + assert_se(memcmp(buf + 1234, rbuf, rbuf_size) == 0); + rbuf = mfree(rbuf); + + assert_se(read_full_file_full(AT_FDCWD, fn, 2345, 777, 0, NULL, &rbuf, &rbuf_size) >= 0); + assert_se(rbuf_size == 777); + assert_se(memcmp(buf + 2345, rbuf, rbuf_size) == 0); + rbuf = mfree(rbuf); + + assert_se(read_full_file_full(AT_FDCWD, fn, 4700, 20, 0, NULL, &rbuf, &rbuf_size) >= 0); + assert_se(rbuf_size == 11); + assert_se(memcmp(buf + 4700, rbuf, rbuf_size) == 0); + rbuf = mfree(rbuf); + + assert_se(read_full_file_full(AT_FDCWD, fn, 10000, 99, 0, NULL, &rbuf, &rbuf_size) >= 0); + assert_se(rbuf_size == 0); + rbuf = mfree(rbuf); +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -946,6 +990,7 @@ int main(int argc, char *argv[]) { test_read_line4(); test_read_nul_string(); test_read_full_file_socket(); + test_read_full_file_offset_size(); return 0; } diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c index 558e9510ff..9b8bca11f2 100644 --- a/src/veritysetup/veritysetup.c +++ b/src/veritysetup/veritysetup.c @@ -100,7 +100,11 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to parse root hash signature '%s': %m", argv[6]); } else { - r = read_full_file_full(AT_FDCWD, argv[6], READ_FULL_FILE_CONNECT_SOCKET, NULL, &hash_sig, &hash_sig_size); + r = read_full_file_full( + AT_FDCWD, argv[6], UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &hash_sig, &hash_sig_size); if (r < 0) return log_error_errno(r, "Failed to read root hash signature: %m"); }