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");
}