From 53522b1c11222273c7b41f72b374e759d13b2165 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Wed, 13 Mar 2019 23:13:06 +0100 Subject: [PATCH] Various fixes for the encrypted storage support. - create_initial_config_files() and node_write_config() are now the only functions that generate the content of new config files from scratch. - All public API functions that change config files now immediately write them out. - Config files of nodes that join using an invitation file are immediately written out. - Ensure nodes marked dirty have their config files written out in periodic_handler(), and on meshlink_stop(). - Fix some memory leaks. - Write out updated config files, and recreate mesh->self in meshlink_set_port(). --- doc/Configuration.md | 4 +- src/conf.c | 4 +- src/meshlink.c | 251 ++++++++++++++++++++----------------------- src/net.c | 21 +++- src/net_setup.c | 37 ++++++- src/netutl.c | 4 +- src/node.c | 1 + src/protocol_auth.c | 3 +- src/submesh.c | 5 +- 9 files changed, 178 insertions(+), 152 deletions(-) diff --git a/doc/Configuration.md b/doc/Configuration.md index 1520ee57..6b76457b 100644 --- a/doc/Configuration.md +++ b/doc/Configuration.md @@ -43,9 +43,10 @@ The contents of a host configuration are: - uint32: configuration format version - str: name of the node +- str: name of the submesh this node is part of, or "." if it's in the core mesh - int32: device class - bool: blacklisted -- bin: public Ed25519 key +- bin: public Ed25519 key, or zero-length if no key is known - str: canonical address (may be zero length if unset) - arr[ext]: recent addresses @@ -56,6 +57,7 @@ The contents of an invitation file are: - uint32: invitation format version - str: name of the invitee +- str: name of the submesh this node will be part of, or "." if it's in the core mesh - int32: device class of the invitee (may be unused) - arr[bin]: one or more host config files diff --git a/src/conf.c b/src/conf.c index 9664e2b7..1a3c82ae 100644 --- a/src/conf.c +++ b/src/conf.c @@ -223,6 +223,7 @@ bool config_read_file(meshlink_handle_t *mesh, FILE *f, config_t *config) { chacha_poly1305_set_key(ctx, mesh->config_key); if(len > 12 && chacha_poly1305_decrypt_iv96(ctx, buf, buf + 12, len - 12, decrypted, &decrypted_len)) { + chacha_poly1305_exit(ctx); free(buf); config->buf = decrypted; config->len = decrypted_len; @@ -230,6 +231,7 @@ bool config_read_file(meshlink_handle_t *mesh, FILE *f, config_t *config) { } else { logger(mesh, MESHLINK_ERROR, "Cannot decrypt config file\n"); meshlink_errno = MESHLINK_ESTORAGE; + chacha_poly1305_exit(ctx); free(decrypted); free(buf); return false; @@ -330,7 +332,7 @@ void config_scan_all(meshlink_handle_t *mesh, config_scan_action_t action) { DIR *dir; struct dirent *ent; char dname[PATH_MAX]; - make_host_path(mesh, NULL, dname, sizeof(dname)); + make_host_path(mesh, "", dname, sizeof(dname)); dir = opendir(dname); diff --git a/src/meshlink.c b/src/meshlink.c index cead9960..134abd83 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -530,6 +530,36 @@ int check_port(meshlink_handle_t *mesh) { return 0; } +static bool write_main_config_files(meshlink_handle_t *mesh) { + uint8_t buf[4096]; + + /* Write the main config file */ + packmsg_output_t out = {buf, sizeof buf}; + + packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION); + packmsg_add_str(&out, mesh->name); + packmsg_add_bin(&out, ecdsa_get_private_key(mesh->private_key), 96); + packmsg_add_bin(&out, ecdsa_get_private_key(mesh->invitation_key), 96); + packmsg_add_uint16(&out, atoi(mesh->myport)); + + if(!packmsg_output_ok(&out)) { + return false; + } + + config_t config = {buf, packmsg_output_size(&out, buf)}; + + if(!main_config_write(mesh, &config)) { + return false; + } + + /* Write our own host config file */ + if(!node_write_config(mesh, mesh->self)) { + return false; + } + + return true; +} + static bool finalize_join(meshlink_handle_t *mesh, const void *buf, uint16_t len) { packmsg_input_t in = {buf, len}; uint32_t version = packmsg_get_uint32(&in); @@ -551,48 +581,28 @@ static bool finalize_join(meshlink_handle_t *mesh, const void *buf, uint16_t len if(!check_id(name)) { logger(mesh, MESHLINK_DEBUG, "Invalid Name found in invitation: %s!\n", name); + free(name); return false; } if(!count) { logger(mesh, MESHLINK_ERROR, "Incomplete invitation file!\n"); + free(name); return false; } + free(mesh->name); + free(mesh->self->name); + mesh->name = name; + mesh->self->name = xstrdup(name); + mesh->self->devclass = devclass; + // Initialize configuration directory if(!config_init(mesh)) { return false; } - // Write main config file - uint8_t outbuf[4096]; - packmsg_output_t out = {outbuf, sizeof(outbuf)}; - packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION); - packmsg_add_str(&out, name); - packmsg_add_bin(&out, ecdsa_get_private_key(mesh->private_key), 96); - packmsg_add_uint16(&out, atoi(mesh->myport)); - - config_t config = {outbuf, packmsg_output_size(&out, outbuf)}; - - if(!main_config_write(mesh, &config)) { - return false; - } - - // Write our own host config file - out.ptr = outbuf; - out.len = sizeof(outbuf); - packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION); - packmsg_add_str(&out, name); - packmsg_add_str(&out, CORE_MESH); - packmsg_add_int32(&out, devclass); - packmsg_add_bool(&out, false); - packmsg_add_bin(&out, ecdsa_get_public_key(mesh->private_key), 32); - packmsg_add_str(&out, ""); // TODO: copy existing canonical address, in case it was added before meshlink_join(). - packmsg_add_array(&out, 0); - - config.len = packmsg_output_size(&out, outbuf); - - if(!config_write(mesh, name, &config)) { + if(!write_main_config_files(mesh)) { return false; } @@ -606,19 +616,36 @@ static bool finalize_join(meshlink_handle_t *mesh, const void *buf, uint16_t len return false; } - config_t config = {data, len}; - node_t *n = new_node(); + packmsg_input_t in2 = {data, len}; + uint32_t version = packmsg_get_uint32(&in2); + char *name = packmsg_get_str_dup(&in2); - if(!node_read_from_config(mesh, n, &config)) { - free_node(n); - logger(mesh, MESHLINK_ERROR, "Invalid host config file in invitation file!\n"); + if(!packmsg_input_ok(&in2) || version != MESHLINK_CONFIG_VERSION || !check_id(name)) { + free(name); + packmsg_input_invalidate(&in); + break; + } + + if(!check_id(name)) { + free(name); + break; + } + + if(!strcmp(name, mesh->name)) { + logger(mesh, MESHLINK_DEBUG, "Secondary chunk would overwrite our own host config file.\n"); + free(name); meshlink_errno = MESHLINK_EPEER; return false; } - if(!strcmp(n->name, name)) { - logger(mesh, MESHLINK_DEBUG, "Secondary chunk would overwrite our own host config file.\n"); + node_t *n = new_node(); + n->name = name; + + config_t config = {data, len}; + + if(!node_read_from_config(mesh, n, &config)) { free_node(n); + logger(mesh, MESHLINK_ERROR, "Invalid host config file in invitation file!\n"); meshlink_errno = MESHLINK_EPEER; return false; } @@ -632,11 +659,6 @@ static bool finalize_join(meshlink_handle_t *mesh, const void *buf, uint16_t len sptps_send_record(&(mesh->sptps), 1, ecdsa_get_public_key(mesh->private_key), 32); - free(mesh->name); - free(mesh->self->name); - mesh->name = xstrdup(name); - mesh->self->name = xstrdup(name); - logger(mesh, MESHLINK_DEBUG, "Configuration stored in: %s\n", mesh->confbase); return true; @@ -833,24 +855,6 @@ static void add_local_addresses(meshlink_handle_t *mesh) { } } -#if 0 -static bool meshlink_write_config(meshlink_handle_t *mesh) { - uint8_t buf[1024]; - packmsg_output_t out = {buf, sizeof buf}; - packmsg_add_str(&out, mesh->name); - packmsg_add_uint32(&out, mesh->devclass); - packmsg_add_uint16(&out, mesh->port); - packmsg_add_bin(&out, ecdsa, sizeof(ecdsa)); - uint32_t len = packmsg_output_size(&out, buf); - - if(!len) { - logger(mesh, MESHLINK_DEBUG, "Could not create configuration data\n",); - meshlink_errno = MESHLINK_EINTERNAL; - return false; - } -} -#endif - static bool meshlink_setup(meshlink_handle_t *mesh) { if(!config_init(mesh)) { logger(mesh, MESHLINK_ERROR, "Could not set up configuration in %s: %s\n", mesh->confbase, strerror(errno)); @@ -875,36 +879,13 @@ static bool meshlink_setup(meshlink_handle_t *mesh) { mesh->self->devclass = mesh->devclass; mesh->self->ecdsa = ecdsa_set_public_key(ecdsa_get_public_key(mesh->private_key)); - // Write the main config file - uint8_t buf[4096]; - packmsg_output_t out = {buf, sizeof(buf)}; - - packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION); - packmsg_add_str(&out, mesh->name); - packmsg_add_bin(&out, ecdsa_get_private_key(mesh->private_key), 96); - packmsg_add_bin(&out, ecdsa_get_private_key(mesh->invitation_key), 96); - packmsg_add_uint16(&out, atoi(mesh->myport)); - - config_t config = {buf, packmsg_output_size(&out, buf)}; - - if(!main_config_write(mesh, &config)) { + if(!write_main_config_files(mesh)) { return false; } - // Write our own host config file - out.ptr = buf; - out.len = sizeof(buf); - packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION); - packmsg_add_str(&out, mesh->name); - packmsg_add_int32(&out, mesh->devclass); - packmsg_add_bool(&out, false); - packmsg_add_bin(&out, ecdsa_get_public_key(mesh->private_key), 32); - packmsg_add_str(&out, ""); // TODO: copy existing canonical address, in case it was added before meshlink_join(). - packmsg_add_array(&out, 0); - - config.len = packmsg_output_size(&out, buf); - - if(!config_write(mesh, mesh->name, &config)) { + if(!main_config_lock(mesh)) { + logger(NULL, MESHLINK_ERROR, "Cannot lock main config file\n"); + meshlink_errno = MESHLINK_ESTORAGE; return false; } @@ -971,6 +952,7 @@ static bool meshlink_read_config(meshlink_handle_t *mesh) { if(!node_read_public_key(mesh, mesh->self)) { logger(NULL, MESHLINK_ERROR, "Could not read our host configuration file!"); + meshlink_errno = MESHLINK_ESTORAGE; free_node(mesh->self); mesh->self = NULL; return false; @@ -1204,6 +1186,7 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) { if(!prf(params->key, params->keylen, "MeshLink configuration key", 26, mesh->config_key, CHACHA_POLY1305_KEYLEN)) { logger(NULL, MESHLINK_ERROR, "Error creating configuration key!\n"); + meshlink_close(mesh); meshlink_errno = MESHLINK_EINTERNAL; return NULL; } @@ -1458,6 +1441,16 @@ void meshlink_stop(meshlink_handle_t *mesh) { exit_outgoings(mesh); + // Write out any changed node config files + if(mesh->nodes) { + for splay_each(node_t, n, mesh->nodes) { + if(n->status.dirty) { + node_write_config(mesh, n); + n->status.dirty = false; + } + } + } + pthread_mutex_unlock(&(mesh->mesh_mutex)); } @@ -1498,6 +1491,8 @@ void meshlink_close(meshlink_handle_t *mesh) { free(mesh->name); free(mesh->appname); free(mesh->confbase); + free(mesh->config_key); + ecdsa_free(mesh->private_key); pthread_mutex_destroy(&(mesh->mesh_mutex)); main_config_unlock(mesh); @@ -1966,7 +1961,7 @@ bool meshlink_set_canonical_address(meshlink_handle_t *mesh, meshlink_node_t *no node_t *n = (node_t *)node; free(n->canonical_address); n->canonical_address = canonical_address; - n->status.dirty = true; + node_write_config(mesh, n); pthread_mutex_unlock(&(mesh->mesh_mutex)); @@ -2035,11 +2030,26 @@ bool meshlink_set_port(meshlink_handle_t *mesh, int port) { goto done; } + free(mesh->myport); + xasprintf(&mesh->myport, "%d", port); + + /* Write meshlink.conf with the updated port number */ + write_main_config_files(mesh); + + /* Close down the network. This also deletes mesh->self. */ close_network_connections(mesh); - // TODO: write meshlink.conf again + /* Recreate mesh->self. */ + mesh->self = new_node(); + mesh->self->name = xstrdup(mesh->name); + mesh->self->devclass = mesh->devclass; - if(!setup_network(mesh)) { + if(!node_read_public_key(mesh, mesh->self)) { + logger(NULL, MESHLINK_ERROR, "Could not read our host configuration file!"); + meshlink_errno = MESHLINK_ESTORAGE; + free_node(mesh->self); + mesh->self = NULL; + } else if(!setup_network(mesh)) { meshlink_errno = MESHLINK_ENETWORK; } else { rval = true; @@ -2149,7 +2159,10 @@ char *meshlink_invite_ex(meshlink_handle_t *mesh, meshlink_submesh_t *submesh, c packmsg_add_str(&inv, s ? s->name : CORE_MESH); packmsg_add_int32(&inv, DEV_CLASS_UNKNOWN); /* TODO: allow this to be set by inviter? */ - /* TODO: Add several host config files to bootstrap connections */ + /* TODO: Add several host config files to bootstrap connections. + * Note: make sure we only add config files of nodes that are in the core mesh or the same submesh, + * and are not blacklisted. + */ config_t configs[5] = {NULL}; int count = 0; @@ -2321,7 +2334,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { mesh->blen = 0; - if(!sendline(mesh->sock, "0 ?%s %d.%d %s", b64key, PROT_MAJOR, 1, mesh->appname)) { + if(!sendline(mesh->sock, "0 ?%s %d.%d %s", b64key, PROT_MAJOR, PROT_MINOR, mesh->appname)) { logger(mesh, MESHLINK_DEBUG, "Error sending request to %s port %s: %s\n", address, port, strerror(errno)); closesocket(mesh->sock); meshlink_errno = MESHLINK_ENETWORK; @@ -2533,7 +2546,7 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) { if(!packmsg_input_ok(&in2) || version != MESHLINK_CONFIG_VERSION || !check_id(name)) { free(name); - packmsg_input_invalidate(&in2); + packmsg_input_invalidate(&in); break; } @@ -2545,58 +2558,24 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) { node_t *n = lookup_node(mesh, name); if(n) { - free(name); logger(mesh, MESHLINK_DEBUG, "Node %s already exists, not importing\n", name); + free(name); continue; } n = new_node(); n->name = name; - char *submesh_name = packmsg_get_str_dup(&in2); - - if(submesh_name) { - if(!strcmp(submesh_name, CORE_MESH)) { - n->submesh = NULL; - } else { - n->submesh = lookup_or_create_submesh(mesh, submesh_name); - if(!n->submesh) { - packmsg_input_invalidate(&in2); - } - } - - free(submesh_name); - } - - n->devclass = packmsg_get_int32(&in2); - n->status.blacklisted = packmsg_get_bool(&in2); - const void *key; - uint32_t keylen = packmsg_get_bin_raw(&in2, &key); - - if(keylen == 32) { - n->ecdsa = ecdsa_set_public_key(key); - } - - n->canonical_address = packmsg_get_str_dup(&in2); - uint32_t count = packmsg_get_array(&in2); - - if(count > 5) { - count = 5; - } - - for(uint32_t i = 0; i < count; i++) { - n->recent[i] = packmsg_get_sockaddr(&in2); - } + config_t config = {data, len}; - if(!packmsg_done(&in2) || keylen != 32) { - packmsg_input_invalidate(&in); + if(!node_read_from_config(mesh, n, &config)) { free_node(n); + packmsg_input_invalidate(&in); break; - } else { - config_t config = {data, len}; - config_write(mesh, n->name, &config); - node_add(mesh, n); } + + config_write(mesh, n->name, &config); + node_add(mesh, n); } pthread_mutex_unlock(&(mesh->mesh_mutex)); @@ -2635,7 +2614,7 @@ void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) { } n->status.blacklisted = true; - n->status.dirty = true; + node_write_config(mesh, n); logger(mesh, MESHLINK_DEBUG, "Blacklisted %s.\n", node->name); //Immediately terminate any connections we have with the blacklisted node @@ -2678,7 +2657,7 @@ void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) { } n->status.blacklisted = false; - n->status.dirty = true; + node_write_config(mesh, n); pthread_mutex_unlock(&(mesh->mesh_mutex)); return; @@ -2702,7 +2681,7 @@ void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const node_t *n = (node_t *)node; memmove(n->recent + 1, n->recent, 4 * sizeof(*n->recent)); memcpy(n->recent, addr, SALEN(*addr)); - n->status.dirty = true; + node_write_config(mesh, n); pthread_mutex_unlock(&(mesh->mesh_mutex)); // @TODO do we want to fire off a connection attempt right away? diff --git a/src/net.c b/src/net.c index ce7e00d8..348c9e34 100644 --- a/src/net.c +++ b/src/net.c @@ -490,23 +490,27 @@ static void periodic_handler(event_loop_t *loop, void *data) { logger(mesh, MESHLINK_DEBUG, "Autoconnect trying to connect to %s", connect_to->name); /* check if there is already a connection attempt to this node */ - bool found = false; + bool skip = false; for list_each(outgoing_t, outgoing, mesh->outgoings) { if(outgoing->node == connect_to) { - found = true; + logger(mesh, MESHLINK_DEBUG, "* skip autoconnect since it is an outgoing connection already"); + skip = true; break; } } - if(!found) { + if(!node_read_public_key(mesh, connect_to)) { + logger(mesh, MESHLINK_DEBUG, "* skip autoconnect since we don't know this node's public key"); + skip = true; + } + + if(!skip) { logger(mesh, MESHLINK_DEBUG, "Autoconnecting to %s", connect_to->name); outgoing_t *outgoing = xzalloc(sizeof(outgoing_t)); outgoing->node = connect_to; list_insert_tail(mesh->outgoings, outgoing); setup_outgoing_connection(mesh, outgoing); - } else { - logger(mesh, MESHLINK_DEBUG, "* skip autoconnect since it is an outgoing connection already"); } } @@ -587,6 +591,13 @@ static void periodic_handler(event_loop_t *loop, void *data) { logger(mesh, MESHLINK_DEBUG, "--- autoconnect end ---"); } + for splay_each(node_t, n, mesh->nodes) { + if(n->status.dirty) { + node_write_config(mesh, n); + n->status.dirty = false; + } + } + timeout_set(&mesh->loop, data, &(struct timeval) { timeout, rand() % 100000 }); diff --git a/src/net_setup.c b/src/net_setup.c index 4f2022b7..64088b3a 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -124,6 +124,34 @@ bool node_read_public_key(meshlink_handle_t *mesh, node_t *n) { } n->ecdsa = ecdsa_set_public_key(key); + + // While we are at it, read known address information + if(!n->canonical_address) { + n->canonical_address = packmsg_get_str_dup(&in); + } else { + packmsg_skip_element(&in); + } + + // Append any known addresses in the config file to the list we currently have + uint32_t known_count = 0; + + for(uint32_t i = 0; i < 5; i++) { + if(n->recent[i].sa.sa_family) { + known_count++; + } + } + + uint32_t count = packmsg_get_array(&in); + + if(count > 5 - known_count) { + count = 5 - known_count; + } + + for(uint32_t i = 0; i < count; i++) { + n->recent[i + known_count] = packmsg_get_sockaddr(&in); + } + + config_free(&config); return true; } @@ -207,7 +235,6 @@ bool node_write_config(meshlink_handle_t *mesh, node_t *n) { packmsg_add_str(&out, n->name); packmsg_add_str(&out, n->submesh ? n->submesh->name : CORE_MESH); packmsg_add_int32(&out, n->devclass); - assert(n->devclass != 3); packmsg_add_bool(&out, n->status.blacklisted); if(ecdsa_active(n->ecdsa)) { @@ -456,11 +483,13 @@ void close_network_connections(meshlink_handle_t *mesh) { exit_requests(mesh); exit_edges(mesh); exit_nodes(mesh); + exit_submeshes(mesh); exit_connections(mesh); - if(mesh->myport) { - free(mesh->myport); - } + free(mesh->myport); + mesh->myport = NULL; + + mesh->self = NULL; return; } diff --git a/src/netutl.c b/src/netutl.c index 1d0181b8..38f4d487 100644 --- a/src/netutl.c +++ b/src/netutl.c @@ -234,11 +234,11 @@ void sockaddrcpy_setport(sockaddr_t *a, const sockaddr_t *b, uint16_t port) { switch(b->sa.sa_family) { case AF_INET: - a->in.sin_port = port; + a->in.sin_port = htons(port); break; case AF_INET6: - a->in6.sin6_port = port; + a->in6.sin6_port = htons(port); break; default: diff --git a/src/node.c b/src/node.c index 3a43b8f1..690599c5 100644 --- a/src/node.c +++ b/src/node.c @@ -81,6 +81,7 @@ void free_node(node_t *n) { } free(n->name); + free(n->canonical_address); free(n); } diff --git a/src/protocol_auth.c b/src/protocol_auth.c index 9fec1ffa..d26b0f65 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -164,9 +164,8 @@ static bool finalize_invitation(meshlink_handle_t *mesh, connection_t *c, const n->devclass = DEV_CLASS_UNKNOWN; n->ecdsa = ecdsa_set_public_key(data); n->submesh = c->submesh; - n->status.dirty = true; node_add(mesh, n); - // TODO: immediately write the config file? + node_write_config(mesh, n); logger(mesh, MESHLINK_INFO, "Key successfully received from %s", c->name); diff --git a/src/submesh.c b/src/submesh.c index 661ec280..a3bccc14 100644 --- a/src/submesh.c +++ b/src/submesh.c @@ -35,7 +35,10 @@ void init_submeshes(meshlink_handle_t *mesh) { } void exit_submeshes(meshlink_handle_t *mesh) { - list_delete_list(mesh->submeshes); + if(mesh->submeshes) { + list_delete_list(mesh->submeshes); + } + mesh->submeshes = NULL; } -- 2.39.2