Imported gnutls based branch.
[tinc] / src / protocol_key.c
1 /*
2     protocol_key.c -- handle the meta-protocol, key exchange
3     Copyright (C) 1999-2003 Ivo Timmermans <ivo@o2w.nl>,
4                   2000-2003 Guus Sliepen <guus@sliepen.eu.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: protocol_key.c,v 1.1.4.26 2003/12/20 21:25:17 guus Exp $
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, from);
133         } else {
134                 if(tunnelserver)
135                         return false;
136
137                 send_req_key(to->nexthop->connection, from, to);
138         }
139
140         return true;
141 }
142
143 bool send_ans_key(connection_t *c, const node_t *to)
144 {
145         char cipherkey[myself->cipherkeylen * 2 + 1];
146         char digestkey[myself->digestlen * 2 + 1];
147
148         cp();
149
150         bin2hex(myself->cipherkey, cipherkey, myself->cipherkeylen);
151         cipherkey[myself->cipherkeylen * 2] = '\0';
152
153         bin2hex(myself->digestkey, digestkey, myself->digestlen);
154         digestkey[myself->digestlen * 2] = '\0';
155
156         return send_request(c, "%d %s %s %s %s %d %d %d %d", ANS_KEY,
157                                                 myself->name, to->name, cipherkey, digestkey,
158                                                 myself->cipher,
159                                                 myself->digest, myself->maclength,
160                                                 myself->compression);
161 }
162
163 bool ans_key_h(connection_t *c)
164 {
165         char from_name[MAX_STRING_SIZE];
166         char to_name[MAX_STRING_SIZE];
167         char cipherkey[MAX_STRING_SIZE];
168         char digestkey[MAX_STRING_SIZE];
169         int cipher, digest, maclength, compression;
170         node_t *from, *to;
171
172         cp();
173
174         if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d",
175                         from_name, to_name, cipherkey, digestkey, &cipher, &digest, &maclength, &compression) != 8) {
176                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY", c->name,
177                            c->hostname);
178                 return false;
179         }
180
181         from = lookup_node(from_name);
182
183         if(!from) {
184                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
185                            "ANS_KEY", c->name, c->hostname, from_name);
186                 return false;
187         }
188
189         to = lookup_node(to_name);
190
191         if(!to) {
192                 logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
193                            "ANS_KEY", c->name, c->hostname, to_name);
194                 return false;
195         }
196
197         /* Forward it if necessary */
198
199         if(to != myself) {
200                 if(tunnelserver)
201                         return false;
202
203                 return send_request(to->nexthop->connection, "%s", c->buffer);
204         }
205
206         /* Check and lookup cipher and digest algorithms */
207
208         if(cipher) {
209                 from->cipher = cipher;
210                 if(!*gcry_cipher_algo_name(from->cipher)) {
211                         logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name,
212                                    from->hostname);
213                         return false;
214                 }
215
216                 from->cipherblklen = gcry_cipher_get_algo_blklen(from->cipher);
217         } else {
218                 from->cipher = GCRY_CIPHER_NONE;
219         }
220
221         from->maclength = maclength;
222
223         if(digest) {
224                 from->digest = digest;
225
226                 if(!*gcry_md_algo_name(from->digest)) {
227                         logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name,
228                                    from->hostname);
229                         return false;
230                 }
231
232                 from->digestlen = gcry_md_get_algo_dlen(from->digest);
233                 
234                 if(from->maclength > from->digestlen || from->maclength < 0) {
235                         logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"),
236                                    from->name, from->hostname);
237                         return false;
238                 }
239         } else {
240                 from->digest = GCRY_MD_NONE;
241         }
242
243         if(compression < 0 || compression > 11) {
244                 logger(LOG_ERR, _("Node %s (%s) uses bogus compression level!"), from->name, from->hostname);
245                 return false;
246         }
247         
248         from->compression = compression;
249
250         /* Update our copy of the origin's packet key */
251
252         if(from->cipherkey)
253                 free(from->cipherkey);
254
255         from->cipherkeylen = strlen(cipherkey) / 2;
256         from->cipherkey = xmalloc(from->cipherkeylen);
257         hex2bin(cipherkey, from->cipherkey, from->cipherkeylen);
258
259         if(from->cipherkeylen != gcry_cipher_get_algo_keylen(from->cipher)) {
260                 logger(LOG_ERR, _("Node %s (%s) uses wrong keylength %d instead of %d!"), from->name,
261                            from->hostname, from->cipherkeylen, gcry_cipher_get_algo_keylen(from->cipher) );
262                 return false;
263         }
264
265         if(from->digestkey)
266                 free(from->digestkey);
267
268         from->digestlen = strlen(digestkey) / 2;
269         from->digestkey = xmalloc(from->digestlen);
270         hex2bin(digestkey, from->digestkey, from->digestlen);
271         
272         if(from->cipher) {
273                 int result;
274                 result = gcry_cipher_open(&from->cipher_ctx, from->cipher, GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_SECURE);
275                 gcry_cipher_setkey(from->cipher_ctx, from->cipherkey, from->cipherkeylen);
276                 if(result) {
277                         logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"),
278                                         from->name, from->hostname, gcry_strerror(result));
279                         return false;
280                 }
281         }
282
283         if(from->digest) {
284                 int result;
285                 result = gcry_md_open(&from->digest_ctx, from->digest, GCRY_MD_FLAG_SECURE | GCRY_MD_FLAG_HMAC);
286                 gcry_md_setkey(from->digest_ctx, from->digestkey, from->digestlen);
287                 if(result) {
288                         logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"),
289                                         from->name, from->hostname, gcry_strerror(result));
290                         return false;
291                 }
292         }
293
294         from->status.validkey = true;
295         from->status.waitingforkey = false;
296         from->sent_seqno = 0;
297
298         if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes)
299                 send_mtu_probe(from);
300
301         flush_queue(from);
302
303         return true;
304 }