add support for loading signed network files
authorFelix Fietkau <nbd@nbd.name>
Mon, 1 Aug 2022 15:57:46 +0000 (17:57 +0200)
committerFelix Fietkau <nbd@nbd.name>
Tue, 23 Aug 2022 11:48:18 +0000 (13:48 +0200)
Signed-off-by: Felix Fietkau <nbd@nbd.name>
examples/net0.bin [new file with mode: 0644]
examples/net0.key [new file with mode: 0644]
examples/net0.pub [new file with mode: 0644]
examples/test-net0.sh
main.c
network.c
network.h
unetd.h

diff --git a/examples/net0.bin b/examples/net0.bin
new file mode 100644 (file)
index 0000000..4249d85
Binary files /dev/null and b/examples/net0.bin differ
diff --git a/examples/net0.key b/examples/net0.key
new file mode 100644 (file)
index 0000000..745d15e
--- /dev/null
@@ -0,0 +1 @@
+EHCvNDZW7v+WjAeFT6EYXhIHm8exRdjnlz35hxefgWg=
diff --git a/examples/net0.pub b/examples/net0.pub
new file mode 100644 (file)
index 0000000..e458fbe
--- /dev/null
@@ -0,0 +1 @@
+z+DZB9UkXV+69h8cdukSfLxTdMWDcF8wyrjqWa1THuQ=
\ No newline at end of file
index 06de301175a09105ad9ec3f3de5b1209b7dbae52..1c5159d09d760cd5dad85cfbc97ca3f371c0b558 100755 (executable)
@@ -4,9 +4,12 @@ host="${2:-ap1}"
 
 ip link add dev $ifname type wireguard > /dev/null 2>&1
 
-../unetd -d -h $PWD/hosts -N '{
+[ "$ifname" != "net0" ] && ln -sf net0.bin "${ifname}.bin"
+
+../unetd -D $PWD -d -h $PWD/hosts -N '{
        "name": "'"$ifname"'",
-       "type": "file",
+       "type": "dynamic",
+       "auth_key": "'"$(cat ./net0.pub)"'",
        "key": "'"$(cat ./net0-${host}.key)"'",
        "file": "'"$PWD/net0.json"'",
        "tunnels": {
diff --git a/main.c b/main.c
index cd398438f253e4bea8bfa00fa5ce6389d4954f90..6e1d76582d4d6c9aea84f69e0aa9938de426fc57 100644 (file)
--- a/main.c
+++ b/main.c
@@ -17,6 +17,7 @@ struct cmdline_network {
 static struct cmdline_network *cmd_nets;
 static const char *hosts_file;
 const char *mssfix_path = UNETD_MSS_BPF_PATH;
+const char *data_dir = UNETD_DATA_DIR;
 bool debug;
 
 static void
@@ -97,8 +98,11 @@ int main(int argc, char **argv)
        struct cmdline_network *net;
        int ch;
 
-       while ((ch = getopt(argc, argv, "dh:M:N:")) != -1) {
+       while ((ch = getopt(argc, argv, "D:dh:M:N:")) != -1) {
                switch (ch) {
+               case 'D':
+                       data_dir = optarg;
+                       break;
                case 'd':
                        debug = true;
                        break;
index e4229540cc877eeabb659942cc11335e57d95045..3f598faf3f03e3eb0b6e71b177386bdd537b1d00 100644 (file)
--- a/network.c
+++ b/network.c
@@ -44,6 +44,7 @@ static const struct blobmsg_policy netconf_policy[__NETCONF_ATTR_MAX] = {
 const struct blobmsg_policy network_policy[__NETWORK_ATTR_MAX] = {
        [NETWORK_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
        [NETWORK_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING },
+       [NETWORK_ATTR_AUTH_KEY] = { "auth_key", BLOBMSG_TYPE_STRING },
        [NETWORK_ATTR_KEY] = { "key", BLOBMSG_TYPE_STRING },
        [NETWORK_ATTR_FILE] = { "file", BLOBMSG_TYPE_STRING },
        [NETWORK_ATTR_DATA] = { "data", BLOBMSG_TYPE_TABLE },
@@ -117,6 +118,50 @@ static int network_load_file(struct network *net)
        return network_load_data(net, b.head);
 }
 
+static int network_load_dynamic(struct network *net)
+{
+       const char *json = NULL;
+       char *fname = NULL;
+       struct stat st;
+       FILE *f = NULL;
+       int ret = -1;
+
+       asprintf(&fname, "%s/%s.bin", data_dir, network_name(net));
+       f = fopen(fname, "r");
+       free(fname);
+
+       if (!f) {
+               D_NET(net, "failed to open %s/%s.bin\n", data_dir, network_name(net));
+               return -1;
+       }
+
+       if (fstat(fileno(f), &st) < 0)
+               goto out;
+
+       net->net_data_len = st.st_size;
+       net->net_data = realloc(net->net_data, net->net_data_len + 1);
+       memset(net->net_data + net->net_data_len, 0, 1);
+       if (fread(net->net_data, 1, net->net_data_len, f) != net->net_data_len ||
+           unet_auth_data_validate(net->config.auth_key, net->net_data,
+                                   net->net_data_len, &json)) {
+               net->net_data_len = 0;
+               goto out;
+       }
+
+       fclose(f);
+       blob_buf_init(&b, 0);
+       if (!blobmsg_add_json_from_string(&b, json)) {
+               net->net_data_len = 0;
+               return -1;
+       }
+
+       return network_load_data(net, b.head);
+
+out:
+       fclose(f);
+       return ret;
+}
+
 static void
 network_fill_ip(struct blob_buf *buf, int af, union network_addr *addr, int mask)
 {
@@ -309,6 +354,9 @@ static int network_reload(struct network *net)
        case NETWORK_TYPE_INLINE:
                ret = network_load_data(net, net->config.net_data);
                break;
+       case NETWORK_TYPE_DYNAMIC:
+               ret = network_load_dynamic(net);
+               break;
        }
 
        network_services_update_done(net);
@@ -354,6 +402,7 @@ network_destroy(struct network *net)
 {
        network_teardown(net);
        avl_delete(&networks, &net->node);
+       free(net->net_data);
        free(net->config.data);
        free(net);
 }
@@ -377,10 +426,10 @@ network_set_config(struct network *net, struct blob_attr *config)
                      blobmsg_data(net->config.data),
                      blobmsg_len(net->config.data));
 
-       if ((cur = tb[NETWORK_ATTR_TYPE]) == NULL)
-               goto invalid;
-
-       if (!strcmp(blobmsg_get_string(cur), "file"))
+       if ((cur = tb[NETWORK_ATTR_TYPE]) == NULL ||
+           !strcmp(blobmsg_get_string(cur), "dynamic"))
+               net->config.type = NETWORK_TYPE_DYNAMIC;
+       else if (!strcmp(blobmsg_get_string(cur), "file"))
                net->config.type = NETWORK_TYPE_FILE;
        else if (!strcmp(blobmsg_get_string(cur), "inline"))
                net->config.type = NETWORK_TYPE_INLINE;
@@ -404,6 +453,14 @@ network_set_config(struct network *net, struct blob_attr *config)
                if (!net->config.net_data)
                        goto invalid;
                break;
+       case NETWORK_TYPE_DYNAMIC:
+               if ((cur = tb[NETWORK_ATTR_AUTH_KEY]) == NULL)
+                       goto invalid;
+
+               if (b64_decode(blobmsg_get_string(cur), net->config.auth_key,
+                              sizeof(net->config.auth_key)) != sizeof(net->config.auth_key))
+                       goto invalid;
+               break;
        }
 
        if ((cur = tb[NETWORK_ATTR_INTERFACE]) != NULL)
index ea52be3cc69cab0e9bfe84bfe96d05d02c6d8ba8..81fe892a1ee49cac5260fc963d28a1d80e81634d 100644 (file)
--- a/network.h
+++ b/network.h
@@ -12,6 +12,7 @@
 enum network_type {
        NETWORK_TYPE_FILE,
        NETWORK_TYPE_INLINE,
+       NETWORK_TYPE_DYNAMIC,
 };
 
 struct wg_ops;
@@ -29,6 +30,7 @@ struct network {
                int keepalive;
                uint8_t key[CURVE25519_KEY_SIZE];
                uint8_t pubkey[CURVE25519_KEY_SIZE];
+               uint8_t auth_key[CURVE25519_KEY_SIZE];
                const char *file;
                const char *interface;
                const char *update_cmd;
@@ -46,6 +48,9 @@ struct network {
                bool local_host_changed;
        } net_config;
 
+       void *net_data;
+       size_t net_data_len;
+
        int ifindex;
        struct network_host *prev_local_host;
        struct avl_tree hosts;
@@ -63,6 +68,7 @@ enum {
        NETWORK_ATTR_NAME,
        NETWORK_ATTR_TYPE,
        NETWORK_ATTR_KEY,
+       NETWORK_ATTR_AUTH_KEY,
        NETWORK_ATTR_FILE,
        NETWORK_ATTR_DATA,
        NETWORK_ATTR_INTERFACE,
diff --git a/unetd.h b/unetd.h
index 56d88c4bf75d30551b57c4f2e2413ba9d08b246a..d4a816b8f5a2cdded1e107f813ebef2131ea5530 100644 (file)
--- a/unetd.h
+++ b/unetd.h
 #include "host.h"
 #include "service.h"
 #include "ubus.h"
+#include "auth-data.h"
 
 extern const char *mssfix_path;
+extern const char *data_dir;
 extern bool debug;
 
 #define D(format, ...)                                                         \
@@ -34,6 +36,7 @@ extern bool debug;
 #define D_PEER(net, peer, format, ...) D_NET(net, "host %s " format, network_peer_name(peer), ##__VA_ARGS__)
 #define D_SERVICE(net, service, format, ...) D_NET(net, "service %s " format, network_service_name(service), ##__VA_ARGS__)
 
+#define UNETD_DATA_DIR "/etc/unetd"
 #define UNETD_MSS_BPF_PATH     "/lib/bpf/mss.o"
 #define UNETD_MSS_PRIO_BASE    0x130