Do not try to send REQ_KEY or ANS_KEY requests to unreachable nodes.
[tinc] / src / protocol_key.c
1 /*
2     protocol_key.c -- handle the meta-protocol, key exchange
3     Copyright (C) 1999-2005 Ivo Timmermans,
4                   2000-2006 Guus Sliepen <guus@tinc-vpn.org>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id$
21 */
22
23 #include "system.h"
24
25 #include <openssl/evp.h>
26 #include <openssl/err.h>
27
28 #include "avl_tree.h"
29 #include "connection.h"
30 #include "logger.h"
31 #include "net.h"
32 #include "netutl.h"
33 #include "node.h"
34 #include "protocol.h"
35 #include "utils.h"
36 #include "xalloc.h"
37
38 bool mykeyused = false;
39
40 bool send_key_changed(connection_t *c, const node_t *n)
41 {
42         cp();
43
44         /* Only send this message if some other daemon requested our key previously.
45            This reduces unnecessary key_changed broadcasts.
46          */
47
48         if(n == myself && !mykeyused)
49                 return true;
50
51         return send_request(c, "%d %lx %s", KEY_CHANGED, random(), n->name);
52 }
53
54 bool key_changed_h(connection_t *c)
55 {
56         char name[MAX_STRING_SIZE];
57         node_t *n;
58
59         cp();
60
61         if(sscanf(c->buffer, "%*d %*x " MAX_STRING, name) != 1) {
62                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED",
63                            c->name, c->hostname);
64                 return false;
65         }
66
67         if(seen_request(c->buffer))
68                 return true;
69
70         n = lookup_node(name);
71
72         if(!n) {
73                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist"),
74                            "KEY_CHANGED", c->name, c->hostname, name);
75                 return false;
76         }
77
78         n->status.validkey = false;
79         n->status.waitingforkey = false;
80
81         /* Tell the others */
82
83         if(!tunnelserver)
84                 forward_request(c);
85
86         return true;
87 }
88
89 bool send_req_key(connection_t *c, const node_t *from, const node_t *to)
90 {
91         cp();
92
93         return send_request(c, "%d %s %s", REQ_KEY, from->name, to->name);
94 }
95
96 bool req_key_h(connection_t *c)
97 {
98         char from_name[MAX_STRING_SIZE];
99         char to_name[MAX_STRING_SIZE];
100         node_t *from, *to;
101
102         cp();
103
104         if(sscanf(c->buffer, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
105                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY", c->name,
106                            c->hostname);
107                 return false;
108         }
109
110         from = lookup_node(from_name);
111
112         if(!from) {
113                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
114                            "REQ_KEY", c->name, c->hostname, from_name);
115                 return false;
116         }
117
118         to = lookup_node(to_name);
119
120         if(!to) {
121                 logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
122                            "REQ_KEY", c->name, c->hostname, to_name);
123                 return false;
124         }
125
126         /* Check if this key request is for us */
127
128         if(to == myself) {                      /* Yes, send our own key back */
129                 mykeyused = true;
130                 from->received_seqno = 0;
131                 memset(from->late, 0, sizeof(from->late));
132                 send_ans_key(c, myself, from);
133         } else {
134                 if(tunnelserver)
135                         return false;
136
137                 if(!to->status.reachable) {
138                         logger(LOG_WARNING, _("Got %s from %s (%s) destination %s which is not reachable"),
139                                 "REQ_KEY", c->name, c->hostname, to_name);
140                         return true;
141                 }
142
143                 send_req_key(to->nexthop->connection, from, to);
144         }
145
146         return true;
147 }
148
149 bool send_ans_key(connection_t *c, const node_t *from, const node_t *to)
150 {
151         char *key;
152
153         cp();
154
155         key = alloca(2 * from->keylength + 1);
156         bin2hex(from->key, key, from->keylength);
157         key[from->keylength * 2] = '\0';
158
159         return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
160                                                 from->name, to->name, key,
161                                                 from->cipher ? from->cipher->nid : 0,
162                                                 from->digest ? from->digest->type : 0, from->maclength,
163                                                 from->compression);
164 }
165
166 bool ans_key_h(connection_t *c)
167 {
168         char from_name[MAX_STRING_SIZE];
169         char to_name[MAX_STRING_SIZE];
170         char key[MAX_STRING_SIZE];
171         int cipher, digest, maclength, compression;
172         node_t *from, *to;
173
174         cp();
175
176         if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d",
177                 from_name, to_name, key, &cipher, &digest, &maclength,
178                 &compression) != 7) {
179                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY", c->name,
180                            c->hostname);
181                 return false;
182         }
183
184         from = lookup_node(from_name);
185
186         if(!from) {
187                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
188                            "ANS_KEY", c->name, c->hostname, from_name);
189                 return false;
190         }
191
192         to = lookup_node(to_name);
193
194         if(!to) {
195                 logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
196                            "ANS_KEY", c->name, c->hostname, to_name);
197                 return false;
198         }
199
200         /* Forward it if necessary */
201
202         if(to != myself) {
203                 if(tunnelserver)
204                         return false;
205
206                 if(!to->status.reachable) {
207                         logger(LOG_WARNING, _("Got %s from %s (%s) destination %s which is not reachable"),
208                                 "ANS_KEY", c->name, c->hostname, to_name);
209                         return true;
210                 }
211
212                 return send_request(to->nexthop->connection, "%s", c->buffer);
213         }
214
215         /* Update our copy of the origin's packet key */
216
217         if(from->key)
218                 free(from->key);
219
220         from->key = xstrdup(key);
221         from->keylength = strlen(key) / 2;
222         hex2bin(from->key, from->key, from->keylength);
223         from->key[from->keylength] = '\0';
224
225         from->status.validkey = true;
226         from->status.waitingforkey = false;
227         from->sent_seqno = 0;
228
229         /* Check and lookup cipher and digest algorithms */
230
231         if(cipher) {
232                 from->cipher = EVP_get_cipherbynid(cipher);
233
234                 if(!from->cipher) {
235                         logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name,
236                                    from->hostname);
237                         return false;
238                 }
239
240                 if(from->keylength != from->cipher->key_len + from->cipher->iv_len) {
241                         logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name,
242                                    from->hostname);
243                         return false;
244                 }
245         } else {
246                 from->cipher = NULL;
247         }
248
249         from->maclength = maclength;
250
251         if(digest) {
252                 from->digest = EVP_get_digestbynid(digest);
253
254                 if(!from->digest) {
255                         logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name,
256                                    from->hostname);
257                         return false;
258                 }
259
260                 if(from->maclength > from->digest->md_size || from->maclength < 0) {
261                         logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"),
262                                    from->name, from->hostname);
263                         return false;
264                 }
265         } else {
266                 from->digest = NULL;
267         }
268
269         if(compression < 0 || compression > 11) {
270                 logger(LOG_ERR, _("Node %s (%s) uses bogus compression level!"), from->name, from->hostname);
271                 return false;
272         }
273         
274         from->compression = compression;
275
276         if(from->cipher)
277                 if(!EVP_EncryptInit_ex(&from->packet_ctx, from->cipher, NULL, (unsigned char *)from->key, (unsigned char *)from->key + from->cipher->key_len)) {
278                         logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"),
279                                         from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL));
280                         return false;
281                 }
282
283         if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes)
284                 send_mtu_probe(from);
285
286         flush_queue(from);
287
288         return true;
289 }