Remove all occurences of $Id$.
[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-2009 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
21 #include "system.h"
22
23 #include <openssl/evp.h>
24 #include <openssl/err.h>
25 #include <openssl/rand.h>
26
27 #include "avl_tree.h"
28 #include "connection.h"
29 #include "logger.h"
30 #include "net.h"
31 #include "netutl.h"
32 #include "node.h"
33 #include "protocol.h"
34 #include "utils.h"
35 #include "xalloc.h"
36
37 bool mykeyused = false;
38
39 bool send_key_changed()
40 {
41         cp();
42
43         /* Only send this message if some other daemon requested our key previously.
44            This reduces unnecessary key_changed broadcasts.
45          */
46
47         if(!mykeyused)
48                 return true;
49
50         return send_request(broadcast, "%d %x %s", KEY_CHANGED, rand(), myself->name);
51 }
52
53 bool key_changed_h(connection_t *c)
54 {
55         char name[MAX_STRING_SIZE];
56         node_t *n;
57
58         cp();
59
60         if(sscanf(c->buffer, "%*d %*x " MAX_STRING, name) != 1) {
61                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED",
62                            c->name, c->hostname);
63                 return false;
64         }
65
66         if(seen_request(c->buffer))
67                 return true;
68
69         n = lookup_node(name);
70
71         if(!n) {
72                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist"),
73                            "KEY_CHANGED", c->name, c->hostname, name);
74                 return false;
75         }
76
77         n->status.validkey = false;
78         n->status.waitingforkey = false;
79
80         /* Tell the others */
81
82         if(!tunnelserver)
83                 forward_request(c);
84
85         return true;
86 }
87
88 bool send_req_key(node_t *to)
89 {
90         cp();
91
92         return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
93 }
94
95 bool req_key_h(connection_t *c)
96 {
97         char from_name[MAX_STRING_SIZE];
98         char to_name[MAX_STRING_SIZE];
99         node_t *from, *to;
100
101         cp();
102
103         if(sscanf(c->buffer, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
104                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY", c->name,
105                            c->hostname);
106                 return false;
107         }
108
109         from = lookup_node(from_name);
110
111         if(!from) {
112                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
113                            "REQ_KEY", c->name, c->hostname, from_name);
114                 return false;
115         }
116
117         to = lookup_node(to_name);
118
119         if(!to) {
120                 logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
121                            "REQ_KEY", c->name, c->hostname, to_name);
122                 return false;
123         }
124
125         /* Check if this key request is for us */
126
127         if(to == myself) {                      /* Yes, send our own key back */
128                 send_ans_key(from);
129         } else {
130                 if(tunnelserver)
131                         return false;
132
133                 if(!to->status.reachable) {
134                         logger(LOG_WARNING, _("Got %s from %s (%s) destination %s which is not reachable"),
135                                 "REQ_KEY", c->name, c->hostname, to_name);
136                         return true;
137                 }
138
139                 send_request(to->nexthop->connection, "%s", c->buffer);
140         }
141
142         return true;
143 }
144
145 bool send_ans_key(node_t *to)
146 {
147         char *key;
148
149         cp();
150
151         // Set key parameters
152         to->incipher = myself->incipher;
153         to->inkeylength = myself->inkeylength;
154         to->indigest = myself->indigest;
155         to->inmaclength = myself->inmaclength;
156         to->incompression = myself->incompression;
157
158         // Allocate memory for key
159         to->inkey = xrealloc(to->inkey, to->inkeylength);
160
161         // Create a new key
162         RAND_pseudo_bytes((unsigned char *)to->inkey, to->inkeylength);
163         if(to->incipher)
164                 EVP_DecryptInit_ex(&to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + to->incipher->key_len);
165
166         // Reset sequence number and late packet window
167         mykeyused = true;
168         to->received_seqno = 0;
169         memset(to->late, 0, sizeof(to->late));
170
171         // Convert to hexadecimal and send
172         key = alloca(2 * to->inkeylength + 1);
173         bin2hex(to->inkey, key, to->inkeylength);
174         key[to->inkeylength * 2] = '\0';
175
176         return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
177                         myself->name, to->name, key,
178                         to->incipher ? to->incipher->nid : 0,
179                         to->indigest ? to->indigest->type : 0, to->inmaclength,
180                         to->incompression);
181 }
182
183 bool ans_key_h(connection_t *c)
184 {
185         char from_name[MAX_STRING_SIZE];
186         char to_name[MAX_STRING_SIZE];
187         char key[MAX_STRING_SIZE];
188         int cipher, digest, maclength, compression;
189         node_t *from, *to;
190
191         cp();
192
193         if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d",
194                 from_name, to_name, key, &cipher, &digest, &maclength,
195                 &compression) != 7) {
196                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY", c->name,
197                            c->hostname);
198                 return false;
199         }
200
201         from = lookup_node(from_name);
202
203         if(!from) {
204                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
205                            "ANS_KEY", c->name, c->hostname, from_name);
206                 return false;
207         }
208
209         to = lookup_node(to_name);
210
211         if(!to) {
212                 logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
213                            "ANS_KEY", c->name, c->hostname, to_name);
214                 return false;
215         }
216
217         /* Forward it if necessary */
218
219         if(to != myself) {
220                 if(tunnelserver)
221                         return false;
222
223                 if(!to->status.reachable) {
224                         logger(LOG_WARNING, _("Got %s from %s (%s) destination %s which is not reachable"),
225                                 "ANS_KEY", c->name, c->hostname, to_name);
226                         return true;
227                 }
228
229                 return send_request(to->nexthop->connection, "%s", c->buffer);
230         }
231
232         /* Update our copy of the origin's packet key */
233         from->outkey = xrealloc(from->outkey, strlen(key) / 2);
234
235         from->outkey = xstrdup(key);
236         from->outkeylength = strlen(key) / 2;
237         hex2bin(key, from->outkey, from->outkeylength);
238
239         from->status.waitingforkey = false;
240         /* Check and lookup cipher and digest algorithms */
241
242         if(cipher) {
243                 from->outcipher = EVP_get_cipherbynid(cipher);
244
245                 if(!from->outcipher) {
246                         logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name,
247                                    from->hostname);
248                         return false;
249                 }
250
251                 if(from->outkeylength != from->outcipher->key_len + from->outcipher->iv_len) {
252                         logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name,
253                                    from->hostname);
254                         return false;
255                 }
256         } else {
257                 from->outcipher = NULL;
258         }
259
260         from->outmaclength = maclength;
261
262         if(digest) {
263                 from->outdigest = EVP_get_digestbynid(digest);
264
265                 if(!from->outdigest) {
266                         logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name,
267                                    from->hostname);
268                         return false;
269                 }
270
271                 if(from->outmaclength > from->outdigest->md_size || from->outmaclength < 0) {
272                         logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"),
273                                    from->name, from->hostname);
274                         return false;
275                 }
276         } else {
277                 from->outdigest = NULL;
278         }
279
280         if(compression < 0 || compression > 11) {
281                 logger(LOG_ERR, _("Node %s (%s) uses bogus compression level!"), from->name, from->hostname);
282                 return false;
283         }
284         
285         from->outcompression = compression;
286
287         if(from->outcipher)
288                 if(!EVP_EncryptInit_ex(&from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + from->outcipher->key_len)) {
289                         logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"),
290                                         from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL));
291                         return false;
292                 }
293
294         from->status.validkey = true;
295         from->sent_seqno = 0;
296
297         if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes)
298                 send_mtu_probe(from);
299
300         return true;
301 }