]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #12223 from yuwata/network-wireguard-preshared-key-file
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 9 Apr 2019 08:52:52 +0000 (10:52 +0200)
committerGitHub <noreply@github.com>
Tue, 9 Apr 2019 08:52:52 +0000 (10:52 +0200)
network: add PresharedKeyFile= setting and make reading key file failure fatal

14 files changed:
NEWS
man/systemd.netdev.xml
src/basic/fileio.c
src/basic/fileio.h
src/basic/hexdecoct.c
src/basic/hexdecoct.h
src/network/netdev/netdev-gperf.gperf
src/network/netdev/wireguard.c
src/network/netdev/wireguard.h
test/fuzz/fuzz-netdev-parser/directives.netdev
test/test-network/conf/25-wireguard-preshared-key.txt [new file with mode: 0644]
test/test-network/conf/25-wireguard.netdev
test/test-network/conf/25-wireguard.netdev.d/peer.conf
test/test-network/systemd-networkd-tests.py

diff --git a/NEWS b/NEWS
index a0c722839a3e026f6928b1c20768061d04615cf7..8c8d7c08bacfa8dd2b6aafb3a6c21bd20c1c697f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -116,8 +116,8 @@ CHANGES WITH 242:
         * The new TripleSampling= option in .network files may be used to
           configure CAN triple sampling.
 
-        * A new .netdev setting PrivateKeyFile= may be used to point to private
-          key for a WireGuard interface.
+        * A new .netdev settings PrivateKeyFile= and PresharedKeyFile= may be
+          used to point to private or preshared key for a WireGuard interface.
 
         * /etc/crypttab now supports the same-cpu-crypt and
           submit-from-crypt-cpus options to tweak encryption work scheduling
index a44018cad6d6cb2104375883d72036a69331c5a4..1836b5fe00e3a91e9af449675f5cc2844dc5f491 100644 (file)
       <varlistentry>
         <term><varname>PrivateKeyFile=</varname></term>
         <listitem>
-          <para>Takes a absolute path to a file which contains the Base64 encoded private key for the interface.
-          If both <varname>PrivateKey=</varname> and <varname>PrivateKeyFile=</varname> are specified, and if
-          the file specified in <varname>PrivateKeyFile=</varname> contains valid wireguard key, then
-          the key provided by <varname>PrivateKey=</varname> is ignored.
+          <para>Takes an absolute path to a file which contains the Base64 encoded private key for the interface.
+          When this option is specified, then <varname>PrivateKey=</varname> is ignored.
           Note that the file must be readable by the user <literal>systemd-network</literal>, so it
           should be, e.g., owned by <literal>root:systemd-network</literal> with a
           <literal>0640</literal> file mode.</para>
             with a <literal>0640</literal> file mode.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>PresharedKeyFile=</varname></term>
+        <listitem>
+          <para>Takes an absolute path to a file which contains the Base64 encoded preshared key for the
+          peer. When this option is specified, then <varname>PresharedKey=</varname> is ignored.
+          Note that the file must be readable by the user <literal>systemd-network</literal>, so it
+          should be, e.g., owned by <literal>root:systemd-network</literal> with a
+          <literal>0640</literal> file mode.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>AllowedIPs=</varname></term>
         <listitem>
index 91e0c9ec8bc8acbdae10c22744eb85c952505fad..9ab2f501c7681f12100e1e43c87ab12c53063da9 100644 (file)
@@ -17,6 +17,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "hexdecoct.h"
 #include "log.h"
 #include "macro.h"
 #include "missing.h"
@@ -264,26 +265,29 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
         return 1;
 }
 
-int read_full_stream(
+int read_full_stream_full(
                 FILE *f,
+                const char *filename,
+                ReadFullFileFlags flags,
                 char **ret_contents,
                 size_t *ret_size) {
 
         _cleanup_free_ char *buf = NULL;
         struct stat st;
-        size_t n, l;
-        int fd;
+        size_t n, n_next, l;
+        int fd, r;
 
         assert(f);
         assert(ret_contents);
+        assert(!(flags & READ_FULL_FILE_UNBASE64) || ret_size);
 
-        n = LINE_MAX; /* Start 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 (fstat(fileno(f), &st) < 0)
+                if (fstat(fd, &st) < 0)
                         return -errno;
 
                 if (S_ISREG(st.st_mode)) {
@@ -296,27 +300,44 @@ int read_full_stream(
                          * size of 0. 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 = st.st_size + 1;
+                                n_next = st.st_size + 1;
+
+                        if (flags & READ_FULL_FILE_SECURE)
+                                (void) warn_file_is_world_accessible(filename, &st, NULL, 0);
                 }
         }
 
-        l = 0;
+        n = l = 0;
         for (;;) {
                 char *t;
                 size_t k;
 
-                t = realloc(buf, n + 1);
-                if (!t)
-                        return -ENOMEM;
+                if (flags & READ_FULL_FILE_SECURE) {
+                        t = malloc(n_next + 1);
+                        if (!t) {
+                                r = -ENOMEM;
+                                goto finalize;
+                        }
+                        memcpy_safe(t, buf, n);
+                        explicit_bzero_safe(buf, n);
+                } else {
+                        t = realloc(buf, n_next + 1);
+                        if (!t)
+                                return -ENOMEM;
+                }
 
                 buf = t;
+                n = n_next;
+
                 errno = 0;
                 k = fread(buf + l, 1, n - l, f);
                 if (k > 0)
                         l += k;
 
-                if (ferror(f))
-                        return errno > 0 ? -errno : -EIO;
+                if (ferror(f)) {
+                        r = errno > 0 ? -errno : -EIO;
+                        goto finalize;
+                }
 
                 if (feof(f))
                         break;
@@ -327,10 +348,18 @@ int read_full_stream(
                 assert(l == n);
 
                 /* Safety check */
-                if (n >= READ_FULL_BYTES_MAX)
-                        return -E2BIG;
+                if (n >= READ_FULL_BYTES_MAX) {
+                        r = -E2BIG;
+                        goto finalize;
+                }
+
+                n_next = MIN(n * 2, READ_FULL_BYTES_MAX);
+        }
 
-                n = MIN(n * 2, READ_FULL_BYTES_MAX);
+        if (flags & READ_FULL_FILE_UNBASE64) {
+                buf[l++] = 0;
+                r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
+                goto finalize;
         }
 
         if (!ret_size) {
@@ -338,8 +367,10 @@ int read_full_stream(
                  * trailing NUL byte. But if there's an embedded NUL byte, then we should refuse operation as otherwise
                  * there'd be ambiguity about what we just read. */
 
-                if (memchr(buf, 0, l))
-                        return -EBADMSG;
+                if (memchr(buf, 0, l)) {
+                        r = -EBADMSG;
+                        goto finalize;
+                }
         }
 
         buf[l] = 0;
@@ -349,21 +380,27 @@ int read_full_stream(
                 *ret_size = l;
 
         return 0;
+
+finalize:
+        if (flags & READ_FULL_FILE_SECURE)
+                explicit_bzero_safe(buf, n);
+
+        return r;
 }
 
-int read_full_file(const char *fn, char **contents, size_t *size) {
+int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) {
         _cleanup_fclose_ FILE *f = NULL;
 
-        assert(fn);
+        assert(filename);
         assert(contents);
 
-        f = fopen(fn, "re");
+        f = fopen(filename, "re");
         if (!f)
                 return -errno;
 
         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
 
-        return read_full_stream(f, contents, size);
+        return read_full_stream_full(f, filename, flags, contents, size);
 }
 
 int executable_is_script(const char *path, char **interpreter) {
@@ -818,3 +855,28 @@ int safe_fgetc(FILE *f, char *ret) {
 
         return 1;
 }
+
+int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line) {
+        struct stat _st;
+
+        if (!filename)
+                return 0;
+
+        if (!st) {
+                if (stat(filename, &_st) < 0)
+                        return -errno;
+                st = &_st;
+        }
+
+        if ((st->st_mode & S_IRWXO) == 0)
+                return 0;
+
+        if (unit)
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "%s has %04o mode that is too permissive, please adjust the access mode.",
+                           filename, st->st_mode & 07777);
+        else
+                log_warning("%s has %04o mode that is too permissive, please adjust the access mode.",
+                            filename, st->st_mode & 07777);
+        return 0;
+}
index 53e3f4ef5f46caf2587d7756e4199eab439a99fd..760e7386884ef80ccf9181e5239b868e07a466de 100644 (file)
@@ -5,6 +5,7 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdio.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 
 #include "macro.h"
@@ -27,6 +28,11 @@ typedef enum {
 
 } WriteStringFileFlags;
 
+typedef enum {
+        READ_FULL_FILE_SECURE   = 1 << 0,
+        READ_FULL_FILE_UNBASE64 = 1 << 1,
+} ReadFullFileFlags;
+
 int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts);
 static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) {
         return write_string_stream_ts(f, line, flags, NULL);
@@ -38,9 +44,15 @@ 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 *fn, char **line);
-int read_full_file(const char *fn, char **contents, size_t *size);
-int read_full_stream(FILE *f, char **contents, size_t *size);
+int read_one_line_file(const char *filename, char **line);
+int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size);
+static inline int read_full_file(const char *filename, char **contents, size_t *size) {
+        return read_full_file_full(filename, 0, contents, 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 verify_file(const char *fn, const char *blob, bool accept_extra_nl);
 
@@ -76,3 +88,5 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) {
 }
 
 int safe_fgetc(FILE *f, char *ret);
+
+int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);
index a5660a0ff3a23045f01176c83ae032655fdd2dff..132439fd1c5c8063b959dc28879f2c957ba2e556 100644 (file)
@@ -685,11 +685,12 @@ static int unbase64_next(const char **p, size_t *l) {
         return ret;
 }
 
-int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
+int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_size) {
         _cleanup_free_ uint8_t *buf = NULL;
         const char *x;
         uint8_t *z;
         size_t len;
+        int r;
 
         assert(p || l == 0);
         assert(ret);
@@ -712,36 +713,54 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
                 a = unbase64_next(&x, &l);
                 if (a == -EPIPE) /* End of string */
                         break;
-                if (a < 0)
-                        return a;
-                if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
-                        return -EINVAL;
+                if (a < 0) {
+                        r = a;
+                        goto on_failure;
+                }
+                if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */
+                        r = -EINVAL;
+                        goto on_failure;
+                }
 
                 b = unbase64_next(&x, &l);
-                if (b < 0)
-                        return b;
-                if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
-                        return -EINVAL;
+                if (b < 0) {
+                        r = b;
+                        goto on_failure;
+                }
+                if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */
+                        r = -EINVAL;
+                        goto on_failure;
+                }
 
                 c = unbase64_next(&x, &l);
-                if (c < 0)
-                        return c;
+                if (c < 0) {
+                        r = c;
+                        goto on_failure;
+                }
 
                 d = unbase64_next(&x, &l);
-                if (d < 0)
-                        return d;
+                if (d < 0) {
+                        r = d;
+                        goto on_failure;
+                }
 
                 if (c == INT_MAX) { /* Padding at the third character */
 
-                        if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
-                                return -EINVAL;
+                        if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */
+                                r = -EINVAL;
+                                goto on_failure;
+                        }
 
                         /* b == 00YY0000 */
-                        if (b & 15)
-                                return -EINVAL;
+                        if (b & 15) {
+                                r = -EINVAL;
+                                goto on_failure;
+                        }
 
-                        if (l > 0) /* Trailing rubbish? */
-                                return -ENAMETOOLONG;
+                        if (l > 0) { /* Trailing rubbish? */
+                                r = -ENAMETOOLONG;
+                                goto on_failure;
+                        }
 
                         *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
                         break;
@@ -749,11 +768,15 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
 
                 if (d == INT_MAX) {
                         /* c == 00ZZZZ00 */
-                        if (c & 3)
-                                return -EINVAL;
+                        if (c & 3) {
+                                r = -EINVAL;
+                                goto on_failure;
+                        }
 
-                        if (l > 0) /* Trailing rubbish? */
-                                return -ENAMETOOLONG;
+                        if (l > 0) { /* Trailing rubbish? */
+                                r = -ENAMETOOLONG;
+                                goto on_failure;
+                        }
 
                         *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
                         *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
@@ -771,6 +794,12 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
         *ret = TAKE_PTR(buf);
 
         return 0;
+
+on_failure:
+        if (secure)
+                explicit_bzero_safe(buf, len);
+
+        return r;
 }
 
 void hexdump(FILE *f, const void *p, size_t s) {
index 9477d16e37e725fe3f157f8965ca012af0743c0b..fa6013ee7506090284fe1e977ff87d85e2ab1b23 100644 (file)
@@ -33,6 +33,9 @@ ssize_t base64mem(const void *p, size_t l, char **out);
 int base64_append(char **prefix, int plen,
                   const void *p, size_t l,
                   int margin, int width);
-int unbase64mem(const char *p, size_t l, void **mem, size_t *len);
+int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
+static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) {
+        return unbase64mem_full(p, l, false, mem, len);
+}
 
 void hexdump(FILE *f, const void *p, size_t s);
index fcd2ec2097babacc7b697b454135e5cac3615276..1a3d6caeb9f77ce66be89f8a24a1a133f37aa557 100644 (file)
@@ -187,4 +187,5 @@ WireGuardPeer.AllowedIPs,          config_parse_wireguard_allowed_ips,   0,
 WireGuardPeer.Endpoint,            config_parse_wireguard_endpoint,      0,                             0
 WireGuardPeer.PublicKey,           config_parse_wireguard_public_key,    0,                             0
 WireGuardPeer.PresharedKey,        config_parse_wireguard_preshared_key, 0,                             0
+WireGuardPeer.PresharedKeyFile,    config_parse_wireguard_preshared_key_file, 0,                        0
 WireGuardPeer.PersistentKeepalive, config_parse_wireguard_keepalive,     0,                             0
index 5d9df16349c85228f0fb8c7928f3f3923f65d21c..5aa10fc4dea197addeb1759d4761c74d654563e4 100644 (file)
@@ -53,6 +53,8 @@ static void wireguard_peer_free(WireguardPeer *peer) {
 
         free(peer->endpoint_host);
         free(peer->endpoint_port);
+        free(peer->preshared_key_file);
+        explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN);
 
         free(peer);
 }
@@ -438,16 +440,18 @@ static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_m
         return 0;
 }
 
-int config_parse_wireguard_listen_port(const char *unit,
-                                       const char *filename,
-                                       unsigned line,
-                                       const char *section,
-                                       unsigned section_line,
-                                       const char *lvalue,
-                                       int ltype,
-                                       const char *rvalue,
-                                       void *data,
-                                       void *userdata) {
+int config_parse_wireguard_listen_port(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
         uint16_t *s = data;
         uint16_t port = 0;
         int r;
@@ -465,17 +469,17 @@ int config_parse_wireguard_listen_port(const char *unit,
         }
 
         *s = port;
-
         return 0;
 }
 
 static int wireguard_decode_key_and_warn(
                 const char *rvalue,
-                uint8_t *ret,
+                uint8_t ret[static WG_KEY_LEN],
                 const char *unit,
                 const char *filename,
                 unsigned line,
                 const char *lvalue) {
+
         _cleanup_free_ void *key = NULL;
         size_t len;
         int r;
@@ -490,43 +494,50 @@ static int wireguard_decode_key_and_warn(
                 return 0;
         }
 
-        r = unbase64mem(rvalue, strlen(rvalue), &key, &len);
+        if (!streq(lvalue, "PublicKey"))
+                (void) warn_file_is_world_accessible(filename, NULL, unit, line);
+
+        r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, r,
                            "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
-                return 0;
+                goto finalize;
         }
         if (len != WG_KEY_LEN) {
                 log_syntax(unit, LOG_ERR, filename, line, 0,
                            "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
                            lvalue, len);
-                return 0;
+                goto finalize;
         }
 
         memcpy(ret, key, WG_KEY_LEN);
-        return true;
+        r = 0;
+
+finalize:
+        explicit_bzero_safe(key, len);
+        return r;
 }
 
-int config_parse_wireguard_private_key(const char *unit,
-                                       const char *filename,
-                                       unsigned line,
-                                       const char *section,
-                                       unsigned section_line,
-                                       const char *lvalue,
-                                       int ltype,
-                                       const char *rvalue,
-                                       void *data,
-                                       void *userdata) {
+int config_parse_wireguard_private_key(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
         Wireguard *w;
 
         assert(data);
-
         w = WIREGUARD(data);
-
         assert(w);
 
-        return wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
-
+        (void) wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
+        return 0;
 }
 
 int config_parse_wireguard_private_key_file(
@@ -563,25 +574,24 @@ int config_parse_wireguard_private_key_file(
         return free_and_replace(w->private_key_file, path);
 }
 
-int config_parse_wireguard_preshared_key(const char *unit,
-                                         const char *filename,
-                                         unsigned line,
-                                         const char *section,
-                                         unsigned section_line,
-                                         const char *lvalue,
-                                         int ltype,
-                                         const char *rvalue,
-                                         void *data,
-                                         void *userdata) {
+int config_parse_wireguard_preshared_key(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
         Wireguard *w;
         int r;
 
         assert(data);
-
         w = WIREGUARD(data);
-
         assert(w);
 
         r = wireguard_peer_new_static(w, filename, section_line, &peer);
@@ -596,25 +606,67 @@ int config_parse_wireguard_preshared_key(const char *unit,
         return 0;
 }
 
-int config_parse_wireguard_public_key(const char *unit,
-                                      const char *filename,
-                                      unsigned line,
-                                      const char *section,
-                                      unsigned section_line,
-                                      const char *lvalue,
-                                      int ltype,
-                                      const char *rvalue,
-                                      void *data,
-                                      void *userdata) {
+int config_parse_wireguard_preshared_key_file(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
+        _cleanup_free_ char *path = NULL;
         Wireguard *w;
         int r;
 
         assert(data);
-
         w = WIREGUARD(data);
+        assert(w);
+
+        r = wireguard_peer_new_static(w, filename, section_line, &peer);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                peer->preshared_key_file = mfree(peer->preshared_key_file);
+                TAKE_PTR(peer);
+                return 0;
+        }
+
+        path = strdup(rvalue);
+        if (!path)
+                return log_oom();
+
+        if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
+                return 0;
+
+        free_and_replace(peer->preshared_key_file, path);
+        TAKE_PTR(peer);
+        return 0;
+}
 
+int config_parse_wireguard_public_key(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
+        Wireguard *w;
+        int r;
+
+        assert(data);
+        w = WIREGUARD(data);
         assert(w);
 
         r = wireguard_peer_new_static(w, filename, section_line, &peer);
@@ -629,16 +681,17 @@ int config_parse_wireguard_public_key(const char *unit,
         return 0;
 }
 
-int config_parse_wireguard_allowed_ips(const char *unit,
-                                       const char *filename,
-                                       unsigned line,
-                                       const char *section,
-                                       unsigned section_line,
-                                       const char *lvalue,
-                                       int ltype,
-                                       const char *rvalue,
-                                       void *data,
-                                       void *userdata) {
+int config_parse_wireguard_allowed_ips(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
         union in_addr_union addr;
@@ -651,7 +704,6 @@ int config_parse_wireguard_allowed_ips(const char *unit,
         assert(data);
 
         w = WIREGUARD(data);
-
         assert(w);
 
         r = wireguard_peer_new_static(w, filename, section_line, &peer);
@@ -696,16 +748,17 @@ int config_parse_wireguard_allowed_ips(const char *unit,
         return 0;
 }
 
-int config_parse_wireguard_endpoint(const char *unit,
-                                    const char *filename,
-                                    unsigned line,
-                                    const char *section,
-                                    unsigned section_line,
-                                    const char *lvalue,
-                                    int ltype,
-                                    const char *rvalue,
-                                    void *data,
-                                    void *userdata) {
+int config_parse_wireguard_endpoint(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
         const char *begin, *end;
@@ -717,7 +770,6 @@ int config_parse_wireguard_endpoint(const char *unit,
         assert(rvalue);
 
         w = WIREGUARD(data);
-
         assert(w);
 
         r = wireguard_peer_new_static(w, filename, section_line, &peer);
@@ -775,16 +827,17 @@ int config_parse_wireguard_endpoint(const char *unit,
         return 0;
 }
 
-int config_parse_wireguard_keepalive(const char *unit,
-                                     const char *filename,
-                                     unsigned line,
-                                     const char *section,
-                                     unsigned section_line,
-                                     const char *lvalue,
-                                     int ltype,
-                                     const char *rvalue,
-                                     void *data,
-                                     void *userdata) {
+int config_parse_wireguard_keepalive(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
         uint16_t keepalive = 0;
@@ -795,7 +848,6 @@ int config_parse_wireguard_keepalive(const char *unit,
         assert(data);
 
         w = WIREGUARD(data);
-
         assert(w);
 
         r = wireguard_peer_new_static(w, filename, section_line, &peer);
@@ -824,9 +876,7 @@ static void wireguard_init(NetDev *netdev) {
         Wireguard *w;
 
         assert(netdev);
-
         w = WIREGUARD(netdev);
-
         assert(w);
 
         w->flags = WGDEVICE_F_REPLACE_PEERS;
@@ -841,6 +891,7 @@ static void wireguard_done(NetDev *netdev) {
 
         sd_event_source_unref(w->resolve_retry_event_source);
 
+        explicit_bzero_safe(w->private_key, WG_KEY_LEN);
         free(w->private_key_file);
 
         hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
@@ -848,45 +899,34 @@ static void wireguard_done(NetDev *netdev) {
         set_free(w->peers_with_failed_endpoint);
 }
 
-static int wireguard_read_private_key_file(Wireguard *w, bool fatal) {
-        _cleanup_free_ char *contents = NULL;
-        _cleanup_free_ void *key = NULL;
-        size_t size, key_len;
-        NetDev *netdev;
-        int level, r;
-
-        assert(w);
-
-        netdev = NETDEV(w);
+static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
+        _cleanup_free_ char *key = NULL;
+        size_t key_len;
+        int r;
 
-        if (!w->private_key_file)
+        if (!filename)
                 return 0;
 
-        level = fatal ? LOG_ERR : LOG_INFO;
-
-        r = read_full_file(w->private_key_file, &contents, &size);
+        r = read_full_file_full(filename, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64, &key, &key_len);
         if (r < 0)
-                return log_netdev_full(netdev, level, r,
-                                       "Failed to read private key from '%s'%s: %m",
-                                       w->private_key_file, fatal ? "" : ", ignoring");
+                return r;
 
-        r = unbase64mem(contents, size, &key, &key_len);
-        if (r < 0)
-                return log_netdev_full(netdev, level, r,
-                                       "Failed to decode private key%s: %m",
-                                       fatal ? "" : ", ignoring");
+        if (key_len != WG_KEY_LEN) {
+                r = -EINVAL;
+                goto finalize;
+        }
 
-        if (key_len != WG_KEY_LEN)
-                return log_netdev_full(netdev, level, SYNTHETIC_ERRNO(EINVAL),
-                                       "Wireguard private key has invalid length (%zu bytes)%s: %m",
-                                       key_len, fatal ? "" : ", ignoring");
+        memcpy(dest, key, WG_KEY_LEN);
+        r = 0;
 
-        memcpy(w->private_key, key, WG_KEY_LEN);
-        return 0;
+finalize:
+        explicit_bzero_safe(key, key_len);
+        return r;
 }
 
 static int wireguard_peer_verify(WireguardPeer *peer) {
         NetDev *netdev = NETDEV(peer->wireguard);
+        int r;
 
         if (section_is_invalid(peer->section))
                 return -EINVAL;
@@ -897,28 +937,37 @@ static int wireguard_peer_verify(WireguardPeer *peer) {
                                               "Ignoring [WireGuardPeer] section from line %u.",
                                               peer->section->filename, peer->section->line);
 
+        r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r,
+                                              "%s: Failed to read preshared key from '%s'. "
+                                              "Ignoring [WireGuardPeer] section from line %u.",
+                                              peer->section->filename, peer->preshared_key_file,
+                                              peer->section->line);
+
         return 0;
 }
 
 static int wireguard_verify(NetDev *netdev, const char *filename) {
         WireguardPeer *peer, *peer_next;
         Wireguard *w;
-        bool empty;
         int r;
 
         assert(netdev);
         w = WIREGUARD(netdev);
         assert(w);
 
-        empty = eqzero(w->private_key);
-        if (empty && !w->private_key_file)
-                return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
-                                              "%s: Missing PrivateKey= or PrivateKeyFile=, ignoring.",
-                                              filename);
+        r = wireguard_read_key_file(w->private_key_file, w->private_key);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r,
+                                              "Failed to read private key from %s. Dropping network device %s.",
+                                              w->private_key_file, netdev->ifname);
 
-        r = wireguard_read_private_key_file(w, empty);
-        if (r < 0 && empty)
-                return r;
+        if (eqzero(w->private_key))
+                return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+                                              "%s: Missing PrivateKey= or PrivateKeyFile=, "
+                                              "Dropping network device %s.",
+                                              filename, netdev->ifname);
 
         LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers)
                 if (wireguard_peer_verify(peer) < 0)
index 6cf6eec14db49a266543f3ac8f53948ceb7ded24..4ae520c52ba7811741e6fbb5adc2fb0c7ba4f07c 100644 (file)
@@ -21,6 +21,7 @@ typedef struct WireguardPeer {
 
         uint8_t public_key[WG_KEY_LEN];
         uint8_t preshared_key[WG_KEY_LEN];
+        char *preshared_key_file;
         uint32_t flags;
         uint16_t persistent_keepalive_interval;
 
@@ -63,4 +64,5 @@ CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_public_key);
 CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key);
 CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key_file);
 CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_preshared_key);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_preshared_key_file);
 CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_keepalive);
index e0756dc755485a6cec450bfaa6ea5a3c2cce7a12..7da3955af6b275f1e2044075b312c939d927cadf 100644 (file)
@@ -52,6 +52,7 @@ Name=
 [WireGuardPeer]
 Endpoint=
 PresharedKey=
+PresharedKeyFile=
 PersistentKeepalive=
 PublicKey=
 AllowedIPs=
diff --git a/test/test-network/conf/25-wireguard-preshared-key.txt b/test/test-network/conf/25-wireguard-preshared-key.txt
new file mode 100644 (file)
index 0000000..021c443
--- /dev/null
@@ -0,0 +1,3 @@
+cPLOy1YUrEI0EM
+  MIycPJmOo0aTu3RZnw8bL5
+        meVD6m0=
index 61afd1f5e79d7e5009ad0ddf1122ca82185e8b62..4866c31ccac68b39837e86561e613631e634d4bd 100644 (file)
@@ -4,7 +4,6 @@ Kind=wireguard
 
 [WireGuard]
 PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
-PrivateKeyFile=/run/systemd/network/not-exist
 ListenPort=51820
 FwMark=1234
 
index 7ba692378e2123c81e86b67406c6deb9b86f2732..f559ea68f6c45a17836f4f2f5ae3fa68af878655 100644 (file)
@@ -2,3 +2,4 @@
 PublicKey=lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=
 AllowedIPs=fdbc:bae2:7871:0500:e1fe:0793:8636:dad1/128
 AllowedIPs=fdbc:bae2:7871:e1fe:0793:8636::/96
+PresharedKeyFile=/run/systemd/network/25-wireguard-preshared-key.txt
index ffc63d0838378318455d33a5f5b36cb6e218600a..b44cfe8d4597b3e533b70534b6c6040d0fd0b355 100755 (executable)
@@ -307,6 +307,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '25-vxlan.netdev',
         '25-wireguard-23-peers.netdev',
         '25-wireguard-23-peers.network',
+        '25-wireguard-preshared-key.txt',
         '25-wireguard-private-key.txt',
         '25-wireguard.netdev',
         '25-wireguard.network',
@@ -528,7 +529,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
     def test_wireguard(self):
         self.copy_unit_to_networkd_unit_path('25-wireguard.netdev', '25-wireguard.network',
                                              '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
-                                             '25-wireguard-private-key.txt')
+                                             '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt')
         self.start_networkd(0)
         self.wait_online(['wg99:carrier', 'wg98:routable'])
 
@@ -551,6 +552,9 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
             self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820')
             output = subprocess.check_output(['wg', 'show', 'wg99', 'private-key']).rstrip().decode('utf-8')
             self.assertTrue(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
+            output = subprocess.check_output(['wg', 'show', 'wg99', 'preshared-keys']).rstrip().decode('utf-8')
+            self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=      IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=')
+            self.assertTrue(output, 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=      cPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=')
 
             output = subprocess.check_output(['wg', 'show', 'wg98', 'private-key']).rstrip().decode('utf-8')
             self.assertTrue(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')