Added graph dumping ability based on Markus Goetz's patch.
[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                 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 *from, const node_t *to)
144 {
145         char *key;
146
147         cp();
148
149         key = alloca(2 * from->keylength + 1);
150         bin2hex(from->key, key, from->keylength);
151         key[from->keylength * 2] = '\0';
152
153         return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
154                                                 from->name, to->name, key,
155                                                 from->cipher ? from->cipher->nid : 0,
156                                                 from->digest ? from->digest->type : 0, from->maclength,
157                                                 from->compression);
158 }
159
160 bool ans_key_h(connection_t *c)
161 {
162         char from_name[MAX_STRING_SIZE];
163         char to_name[MAX_STRING_SIZE];
164         char key[MAX_STRING_SIZE];
165         int cipher, digest, maclength, compression;
166         node_t *from, *to;
167
168         cp();
169
170         if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d",
171                 from_name, to_name, key, &cipher, &digest, &maclength,
172                 &compression) != 7) {
173                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY", c->name,
174                            c->hostname);
175                 return false;
176         }
177
178         from = lookup_node(from_name);
179
180         if(!from) {
181                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
182                            "ANS_KEY", c->name, c->hostname, from_name);
183                 return false;
184         }
185
186         to = lookup_node(to_name);
187
188         if(!to) {
189                 logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
190                            "ANS_KEY", c->name, c->hostname, to_name);
191                 return false;
192         }
193
194         /* Forward it if necessary */
195
196         if(to != myself) {
197                 if(tunnelserver)
198                         return false;
199
200                 return send_request(to->nexthop->connection, "%s", c->buffer);
201         }
202
203         /* Update our copy of the origin's packet key */
204
205         if(from->key)
206                 free(from->key);
207
208         from->key = xstrdup(key);
209         from->keylength = strlen(key) / 2;
210         hex2bin(from->key, from->key, from->keylength);
211         from->key[from->keylength] = '\0';
212
213         from->status.validkey = true;
214         from->status.waitingforkey = false;
215         from->sent_seqno = 0;
216
217         /* Check and lookup cipher and digest algorithms */
218
219         if(cipher) {
220                 from->cipher = EVP_get_cipherbynid(cipher);
221
222                 if(!from->cipher) {
223                         logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name,
224                                    from->hostname);
225                         return false;
226                 }
227
228                 if(from->keylength != from->cipher->key_len + from->cipher->iv_len) {
229                         logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name,
230                                    from->hostname);
231                         return false;
232                 }
233         } else {
234                 from->cipher = NULL;
235         }
236
237         from->maclength = maclength;
238
239         if(digest) {
240                 from->digest = EVP_get_digestbynid(digest);
241
242                 if(!from->digest) {
243                         logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name,
244                                    from->hostname);
245                         return false;
246                 }
247
248                 if(from->maclength > from->digest->md_size || from->maclength < 0) {
249                         logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"),
250                                    from->name, from->hostname);
251                         return false;
252                 }
253         } else {
254                 from->digest = NULL;
255         }
256
257         if(compression < 0 || compression > 11) {
258                 logger(LOG_ERR, _("Node %s (%s) uses bogus compression level!"), from->name, from->hostname);
259                 return false;
260         }
261         
262         from->compression = compression;
263
264         if(from->cipher)
265                 if(!EVP_EncryptInit_ex(&from->packet_ctx, from->cipher, NULL, (unsigned char *)from->key, (unsigned char *)from->key + from->cipher->key_len)) {
266                         logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"),
267                                         from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL));
268                         return false;
269                 }
270
271         if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes)
272                 send_mtu_probe(from);
273
274         flush_queue(from);
275
276         return true;
277 }