diff --git a/man/systemd-journal-gatewayd.service.xml b/man/systemd-journal-gatewayd.service.xml index 0f7aaab624..a7c50f382f 100644 --- a/man/systemd-journal-gatewayd.service.xml +++ b/man/systemd-journal-gatewayd.service.xml @@ -58,26 +58,25 @@ - Specify the path to a file containing a server - certificate in PEM format. This option switches - systemd-journal-gatewayd into HTTPS mode - and must be used together with + Specify the path to a file or AF_UNIX stream socket to read the + server certificate from. The certificate must be in PEM format. This option switches + systemd-journal-gatewayd into HTTPS mode and must be used together with . - Specify the path to a file containing a server - key in PEM format corresponding to the certificate specified - with . + 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. - Specify the path to a file containing a - CA certificate in PEM format. + Specify the path to a file or AF_UNIX stream socket to read a CA + certificate from. The certificate must be in PEM format. diff --git a/man/systemd-journal-remote.service.xml b/man/systemd-journal-remote.service.xml index b28092d18c..1db0128f74 100644 --- a/man/systemd-journal-remote.service.xml +++ b/man/systemd-journal-remote.service.xml @@ -180,33 +180,29 @@ - - Takes a path to a SSL key file in PEM format. - Defaults to &CERTIFICATE_ROOT;/private/journal-remote.pem. - This option can be used with . - + Takes a path to a SSL 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. - - Takes a path to a SSL certificate file in PEM format. - Defaults to &CERTIFICATE_ROOT;/certs/journal-remote.pem. - This option can be used with . - + Takes a path to a SSL certificate file in PEM format. Defaults to + &CERTIFICATE_ROOT;/certs/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 certificate read from it. - - Takes a path to a SSL CA certificate file in PEM format, - or . If is set, - then certificate checking will be disabled. - Defaults to &CERTIFICATE_ROOT;/ca/trusted.pem. - This option can be used with . - + Takes a path to a SSL CA certificate file in PEM format, or . If + is set, then certificate checking will be disabled. Defaults to + &CERTIFICATE_ROOT;/ca/trusted.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 certificate read from it. diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 5516f63b65..c2957fd182 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -1028,11 +1028,13 @@ KeyFile= - Takes a absolute path to a file which contains a 128-bit key encoded in a hexadecimal - string, which will be used in the transmission channel. When this option is specified, + Takes a absolute path to a file which contains a 128-bit key encoded in a hexadecimal string, + which will be used in the transmission channel. When this option is specified, Key= is ignored. Note that the file must be readable by the user systemd-network, so it should be, e.g., owned by - root:systemd-network with a 0640 file mode. + root:systemd-network with a 0640 file mode. 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. @@ -1518,11 +1520,12 @@ PrivateKeyFile= - Takes an absolute path to a file which contains the Base64 encoded private key for the interface. - When this option is specified, then PrivateKey= is ignored. - Note that the file must be readable by the user systemd-network, so it - should be, e.g., owned by root:systemd-network with a - 0640 file mode. + Takes an absolute path to a file which contains the Base64 encoded private key for the + interface. When this option is specified, then PrivateKey= is ignored. Note + that the file must be readable by the user systemd-network, so it should be, + e.g., owned by root:systemd-network with a 0640 file mode. 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. @@ -1577,10 +1580,11 @@ PresharedKeyFile= Takes an absolute path to a file which contains the Base64 encoded preshared key for the - peer. When this option is specified, then PresharedKey= is ignored. - Note that the file must be readable by the user systemd-network, so it - should be, e.g., owned by root:systemd-network with a - 0640 file mode. + peer. When this option is specified, then PresharedKey= is ignored. Note that + the file must be readable by the user systemd-network, so it should be, e.g., + owned by root:systemd-network with a 0640 file mode. 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 f2f1e1139f..c3d55d209a 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -22,6 +22,7 @@ #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +#include "socket-util.h" #include "stdio-util.h" #include "string-util.h" #include "tmpfile-util.h" @@ -482,13 +483,12 @@ int read_full_stream_full( assert(f); assert(ret_contents); assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)); - assert(!(flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) || ret_size); n_next = 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 */ if (fstat(fd, &st) < 0) return -errno; @@ -505,7 +505,7 @@ int read_full_stream_full( if (st.st_size > 0) n_next = st.st_size + 1; - if (flags & READ_FULL_FILE_SECURE) + if (flags & READ_FULL_FILE_WARN_WORLD_READABLE) (void) warn_file_is_world_accessible(filename, &st, NULL, 0); } } @@ -535,21 +535,18 @@ int read_full_stream_full( errno = 0; k = fread(buf + l, 1, n - l, f); - if (k > 0) - l += k; + + assert(k <= n - l); + l += k; if (ferror(f)) { r = errno_or_else(EIO); goto finalize; } - if (feof(f)) break; - /* We aren't expecting fread() to return a short read outside - * of (error && eof), assert buffer is full and enlarge buffer. - */ - assert(l == n); + assert(k > 0); /* we can't have read zero bytes because that would have been EOF */ /* Safety check */ if (n >= READ_FULL_BYTES_MAX) { @@ -561,12 +558,21 @@ int read_full_stream_full( } if (flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) { + _cleanup_free_ void *decoded = NULL; + size_t decoded_size; + buf[l++] = 0; if (flags & READ_FULL_FILE_UNBASE64) - r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size); + r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size); else - r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size); - goto finalize; + r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size); + if (r < 0) + goto finalize; + + if (flags & READ_FULL_FILE_SECURE) + explicit_bzero_safe(buf, n); + free_and_replace(buf, decoded); + n = l = decoded_size; } if (!ret_size) { @@ -603,8 +609,54 @@ int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flag assert(contents); r = xfopenat(dir_fd, filename, "re", 0, &f); - if (r < 0) - return r; + if (r < 0) { + _cleanup_close_ int dfd = -1, sk = -1; + union sockaddr_union sa; + + /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ + if (r != -ENXIO) + return r; + + /* If this is enabled, let's try to connect to it */ + if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET)) + return -ENXIO; + + if (dir_fd == AT_FDCWD) + r = sockaddr_un_set_path(&sa.un, filename); + else { + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + + /* If we shall operate relative to some directory, then let's use O_PATH first to + * open the socket inode, and then connect to it via /proc/self/fd/. We have to do + * this since there's not connectat() that takes a directory fd as first arg. */ + + dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC); + if (dfd < 0) + return -errno; + + xsprintf(procfs_path, "/proc/self/fd/%i", dfd); + r = sockaddr_un_set_path(&sa.un, procfs_path); + } + if (r < 0) + return r; + + sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + if (sk < 0) + return -errno; + + if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) + return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is + * not a socket after all */ + + if (shutdown(sk, SHUT_WR) < 0) + return -errno; + + f = fdopen(sk, "r"); + if (!f) + return -errno; + + TAKE_FD(sk); + } (void) __fsetlocking(f, FSETLOCKING_BYCALLER); diff --git a/src/basic/fileio.h b/src/basic/fileio.h index e2830b7963..7d58fa7cfc 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -32,9 +32,11 @@ typedef enum { } WriteStringFileFlags; typedef enum { - READ_FULL_FILE_SECURE = 1 << 0, - READ_FULL_FILE_UNBASE64 = 1 << 1, - READ_FULL_FILE_UNHEX = 1 << 2, + READ_FULL_FILE_SECURE = 1 << 0, /* erase any buffers we employ internally, after use */ + READ_FULL_FILE_UNBASE64 = 1 << 1, /* base64 decode what we read */ + READ_FULL_FILE_UNHEX = 1 << 2, /* hex decode what we read */ + READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, /* if regular file, log at LOG_WARNING level if access mode above 0700 */ + READ_FULL_FILE_CONNECT_SOCKET = 1 << 4, /* if socket inode, connect to it and read off it */ } ReadFullFileFlags; int fopen_unlocked(const char *path, const char *options, FILE **ret); diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 3ab7c98b0b..48106d1bdb 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -906,7 +906,7 @@ 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(optarg, &arg_key_pem, NULL); + r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_key_pem, NULL); if (r < 0) return log_error_errno(r, "Failed to read key file: %m"); assert(arg_key_pem); @@ -916,7 +916,7 @@ 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(optarg, &arg_cert_pem, NULL); + r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_cert_pem, NULL); if (r < 0) return log_error_errno(r, "Failed to read certificate file: %m"); assert(arg_cert_pem); @@ -927,7 +927,7 @@ 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(optarg, &arg_trust_pem, NULL); + r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_trust_pem, NULL); if (r < 0) return log_error_errno(r, "Failed to read CA certificate file: %m"); assert(arg_trust_pem); diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index 273fdf9196..77dfdefd64 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -1077,12 +1077,12 @@ static int parse_argv(int argc, char *argv[]) { static int load_certificates(char **key, char **cert, char **trust) { int r; - r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL); + r = read_full_file_full(AT_FDCWD, arg_key ?: PRIV_KEY_FILE, READ_FULL_FILE_CONNECT_SOCKET, 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(arg_cert ?: CERT_FILE, cert, NULL); + r = read_full_file_full(AT_FDCWD, arg_cert ?: CERT_FILE, READ_FULL_FILE_CONNECT_SOCKET, cert, NULL); if (r < 0) return log_error_errno(r, "Failed to read certificate from file '%s': %m", arg_cert ?: CERT_FILE); @@ -1090,7 +1090,7 @@ static int load_certificates(char **key, char **cert, char **trust) { if (arg_trust_all) log_info("Certificate checking disabled."); else { - r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL); + r = read_full_file_full(AT_FDCWD, arg_trust ?: TRUST_FILE, READ_FULL_FILE_CONNECT_SOCKET, trust, NULL); if (r < 0) return log_error_errno(r, "Failed to read CA certificate file '%s': %m", arg_trust ?: TRUST_FILE); diff --git a/src/network/netdev/macsec.c b/src/network/netdev/macsec.c index 57d8f567b9..2ffa5ec8c6 100644 --- a/src/network/netdev/macsec.c +++ b/src/network/netdev/macsec.c @@ -983,7 +983,10 @@ 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, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX, (char **) &key, &key_len); + r = read_full_file_full( + AT_FDCWD, sa->key_file, + READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET, + (char **) &key, &key_len); if (r < 0) return log_netdev_error_errno(netdev, r, "Failed to read key from '%s', ignoring: %m", diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index b6af9925b7..6812b07bff 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -888,7 +888,10 @@ 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, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64, &key, &key_len); + r = read_full_file_full( + AT_FDCWD, filename, + READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET, + &key, &key_len); if (r < 0) return r; diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index af9696ef59..95dcd4fdb1 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -15,6 +15,8 @@ #include "io-util.h" #include "parse-util.h" #include "process-util.h" +#include "rm-rf.h" +#include "socket-util.h" #include "string-util.h" #include "strv.h" #include "tests.h" @@ -842,6 +844,53 @@ static void test_read_nul_string(void) { assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 0 && streq_ptr(s, "")); } +static void test_read_full_file_socket(void) { + _cleanup_(rm_rf_physical_and_freep) char *z = NULL; + _cleanup_close_ int listener = -1; + _cleanup_free_ char *data = NULL; + union sockaddr_union sa; + const char *j; + size_t size; + pid_t pid; + int r; + + log_info("/* %s */", __func__); + + listener = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + assert_se(listener >= 0); + + assert_se(mkdtemp_malloc(NULL, &z) >= 0); + j = strjoina(z, "/socket"); + + assert_se(sockaddr_un_set_path(&sa.un, j) >= 0); + + assert_se(bind(listener, &sa.sa, SOCKADDR_UN_LEN(sa.un)) >= 0); + assert_se(listen(listener, 1) >= 0); + + r = safe_fork("(server)", FORK_DEATHSIG|FORK_LOG, &pid); + assert_se(r >= 0); + if (r == 0) { + _cleanup_close_ int rfd = -1; + /* child */ + + rfd = accept4(listener, NULL, 0, SOCK_CLOEXEC); + assert_se(rfd >= 0); + +#define TEST_STR "This is a test\nreally." + + assert_se(write(rfd, TEST_STR, strlen(TEST_STR)) == strlen(TEST_STR)); + _exit(EXIT_SUCCESS); + } + + assert_se(read_full_file_full(AT_FDCWD, j, 0, &data, &size) == -ENXIO); + assert_se(read_full_file_full(AT_FDCWD, j, READ_FULL_FILE_CONNECT_SOCKET, &data, &size) >= 0); + assert_se(size == strlen(TEST_STR)); + assert_se(streq(data, TEST_STR)); + + assert_se(wait_for_terminate_and_check("(server)", pid, WAIT_LOG) >= 0); +#undef TEST_STR +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -867,6 +916,7 @@ int main(int argc, char *argv[]) { test_read_line3(); test_read_line4(); test_read_nul_string(); + test_read_full_file_socket(); return 0; } diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c index 465d194b40..e475402d9d 100644 --- a/src/veritysetup/veritysetup.c +++ b/src/veritysetup/veritysetup.c @@ -100,7 +100,7 @@ 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], 0, &hash_sig, &hash_sig_size); + r = read_full_file_full(AT_FDCWD, argv[6], READ_FULL_FILE_CONNECT_SOCKET, &hash_sig, &hash_sig_size); if (r < 0) return log_error_errno(r, "Failed to read root hash signature: %m"); }