]> git.meshlink.io Git - meshlink-tiny/blob - src/protocol_auth.c
Remove graph, edges and communication via UDP.
[meshlink-tiny] / src / protocol_auth.c
1 /*
2     protocol_auth.c -- handle the meta-protocol, authentication
3     Copyright (C) 2014-2017 Guus Sliepen <guus@meshlink.io>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "system.h"
21
22 #include "conf.h"
23 #include "connection.h"
24 #include "devtools.h"
25 #include "ecdsa.h"
26 #include "logger.h"
27 #include "meshlink_internal.h"
28 #include "meta.h"
29 #include "net.h"
30 #include "netutl.h"
31 #include "node.h"
32 #include "packmsg.h"
33 #include "prf.h"
34 #include "protocol.h"
35 #include "sptps.h"
36 #include "utils.h"
37 #include "xalloc.h"
38 #include "ed25519/sha512.h"
39
40 #include <assert.h>
41
42 extern bool node_write_devclass(meshlink_handle_t *mesh, node_t *n);
43
44 bool send_id(meshlink_handle_t *mesh, connection_t *c) {
45         return send_request(mesh, c, NULL, "%d %s %d.%d %s", ID, mesh->self->name, PROT_MAJOR, PROT_MINOR, mesh->appname);
46 }
47
48 bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
49         assert(request);
50         assert(*request);
51
52         char name[MAX_STRING_SIZE];
53
54         if(sscanf(request, "%*d " MAX_STRING " %d.%d", name, &c->protocol_major, &c->protocol_minor) < 2) {
55                 logger(mesh, MESHLINK_ERROR, "Got bad %s from %s", "ID", c->name);
56                 return false;
57         }
58
59         /* Check if identity is a valid name */
60
61         if(!check_id(name)) {
62                 logger(mesh, MESHLINK_ERROR, "Got bad %s from %s: %s", "ID", c->name, "invalid name");
63                 return false;
64         }
65
66         /* If this is an outgoing connection, make sure we are connected to the right host */
67
68         if(c->outgoing) {
69                 if(strcmp(c->name, name)) {
70                         logger(mesh, MESHLINK_ERROR, "Peer is %s instead of %s", name, c->name);
71                         return false;
72                 }
73         } else {
74                 if(c->name) {
75                         free(c->name);
76                 }
77
78                 c->name = xstrdup(name);
79         }
80
81         /* Check if version matches */
82
83         if(c->protocol_major != PROT_MAJOR) {
84                 logger(mesh, MESHLINK_ERROR, "Peer %s uses incompatible version %d.%d",
85                        c->name, c->protocol_major, c->protocol_minor);
86                 return false;
87         }
88
89         /* Check if we know this node */
90
91         node_t *n = lookup_node(mesh, c->name);
92
93         if(!n) {
94                 logger(mesh, MESHLINK_ERROR, "Peer %s has unknown identity", c->name);
95                 return false;
96         }
97
98         if(!node_read_public_key(mesh, n)) {
99                 logger(mesh, MESHLINK_ERROR, "No key known for peer %s", c->name);
100
101                 if(n->status.reachable && !n->status.waitingforkey) {
102                         logger(mesh, MESHLINK_INFO, "Requesting key from peer %s", c->name);
103                         send_req_key(mesh, n);
104                 }
105
106                 return false;
107         }
108
109         /* Forbid version rollback for nodes whose ECDSA key we know */
110
111         if(ecdsa_active(c->ecdsa) && c->protocol_minor < 2) {
112                 logger(mesh, MESHLINK_ERROR, "Peer %s tries to roll back protocol version to %d.%d",
113                        c->name, c->protocol_major, c->protocol_minor);
114                 return false;
115         }
116
117         c->allow_request = ACK;
118         c->last_ping_time = mesh->loop.now.tv_sec;
119         char label[sizeof(meshlink_tcp_label) + strlen(mesh->self->name) + strlen(c->name) + 2];
120
121         if(c->outgoing) {
122                 snprintf(label, sizeof(label), "%s %s %s", meshlink_tcp_label, mesh->self->name, c->name);
123         } else {
124                 snprintf(label, sizeof(label), "%s %s %s", meshlink_tcp_label, c->name, mesh->self->name);
125         }
126
127         if(mesh->log_level <= MESHLINK_DEBUG) {
128                 char buf1[1024], buf2[1024];
129                 bin2hex((uint8_t *)mesh->private_key + 64, buf1, 32);
130                 bin2hex((uint8_t *)n->ecdsa + 64, buf2, 32);
131                 logger(mesh, MESHLINK_DEBUG, "Connection to %s mykey %s hiskey %s", c->name, buf1, buf2);
132         }
133
134         return sptps_start(&c->sptps, c, c->outgoing, false, mesh->private_key, n->ecdsa, label, sizeof(label) - 1, send_meta_sptps, receive_meta_sptps);
135 }
136
137 bool send_ack(meshlink_handle_t *mesh, connection_t *c) {
138         node_t *n = lookup_node(mesh, c->name);
139
140         if(n && n->status.blacklisted) {
141                 logger(mesh, MESHLINK_WARNING, "Peer %s is blacklisted", c->name);
142                 return send_error(mesh, c, BLACKLISTED, "blacklisted");
143         }
144
145         c->last_ping_time = mesh->loop.now.tv_sec;
146         return send_request(mesh, c, NULL, "%d %s %d %x", ACK, mesh->myport, mesh->devclass, OPTION_PMTU_DISCOVERY | (PROT_MINOR << 24));
147 }
148
149 bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
150         assert(request);
151         assert(*request);
152
153         char hisport[MAX_STRING_SIZE];
154         int devclass;
155         uint32_t options;
156         node_t *n;
157
158         if(sscanf(request, "%*d " MAX_STRING " %d %x", hisport, &devclass, &options) != 3) {
159                 logger(mesh, MESHLINK_ERROR, "Got bad %s from %s", "ACK", c->name);
160                 return false;
161         }
162
163         if(devclass < 0 || devclass >= DEV_CLASS_COUNT) {
164                 logger(mesh, MESHLINK_ERROR, "Got bad %s from %s: %s", "ACK", c->name, "devclass invalid");
165                 return false;
166         }
167
168         /* Check if we already have a node_t for him */
169
170         n = lookup_node(mesh, c->name);
171
172         if(!n) {
173                 n = new_node();
174                 n->name = xstrdup(c->name);
175                 node_add(mesh, n);
176         } else {
177                 if(n->connection) {
178                         /* Oh dear, we already have a connection to this node. */
179                         logger(mesh, MESHLINK_INFO, "Established a second connection with %s, closing old connection", n->connection->name);
180
181                         if(n->connection->outgoing) {
182                                 if(c->outgoing) {
183                                         logger(mesh, MESHLINK_WARNING, "Two outgoing connections to the same node!");
184                                 } else {
185                                         c->outgoing = n->connection->outgoing;
186                                 }
187
188                                 n->connection->outgoing = NULL;
189                         }
190
191                         terminate_connection(mesh, n->connection, false);
192                 }
193         }
194
195         n->devclass = devclass;
196         n->status.dirty = true;
197
198         n->last_successfull_connection = mesh->loop.now.tv_sec;
199
200         n->connection = c;
201         n->nexthop = n;
202         c->node = n;
203
204         /* Activate this connection */
205
206         c->allow_request = ALL;
207         c->last_key_renewal = mesh->loop.now.tv_sec;
208         c->status.active = true;
209
210         logger(mesh, MESHLINK_INFO, "Connection with %s activated", c->name);
211
212         if(mesh->meta_status_cb) {
213                 mesh->meta_status_cb(mesh, (meshlink_node_t *)n, true);
214         }
215
216         /*  Terminate any connections to this node that are not activated yet */
217
218         for list_each(connection_t, other, mesh->connections) {
219                 if(!other->status.active && !strcmp(other->name, c->name)) {
220                         if(other->outgoing) {
221                                 if(c->outgoing) {
222                                         logger(mesh, MESHLINK_WARNING, "Two outgoing connections to the same node!");
223                                 } else {
224                                         c->outgoing = other->outgoing;
225                                 }
226
227                                 other->outgoing = NULL;
228                         }
229
230                         logger(mesh, MESHLINK_DEBUG, "Terminating pending second connection with %s", n->name);
231                         terminate_connection(mesh, other, false);
232                 }
233         }
234
235         /* TODO: Create an edge_t for this connection, send it */
236
237         /* Request a session key to jump start UDP traffic */
238
239         if(c->status.initiator) {
240                 send_req_key(mesh, n);
241         }
242
243         return true;
244 }