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