K&R style braces
[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         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(n == myself && !mykeyused)
48                 return true;
49
50         return send_request(c, "%d %lx %s", KEY_CHANGED, random(), n->name);
51 }
52
53 bool key_changed_h(connection_t *c) {
54         char name[MAX_STRING_SIZE];
55         node_t *n;
56
57         cp();
58
59         if(sscanf(c->buffer, "%*d %*x " MAX_STRING, name) != 1) {
60                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED",
61                            c->name, c->hostname);
62                 return false;
63         }
64
65         if(seen_request(c->buffer))
66                 return true;
67
68         n = lookup_node(name);
69
70         if(!n) {
71                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist"),
72                            "KEY_CHANGED", c->name, c->hostname, name);
73                 return false;
74         }
75
76         n->status.validkey = false;
77         n->status.waitingforkey = false;
78
79         /* Tell the others */
80
81         if(!tunnelserver)
82                 forward_request(c);
83
84         return true;
85 }
86
87 bool send_req_key(connection_t *c, const node_t *from, const node_t *to) {
88         cp();
89
90         return send_request(c, "%d %s %s", REQ_KEY, from->name, to->name);
91 }
92
93 bool req_key_h(connection_t *c) {
94         char from_name[MAX_STRING_SIZE];
95         char to_name[MAX_STRING_SIZE];
96         node_t *from, *to;
97
98         cp();
99
100         if(sscanf(c->buffer, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
101                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY", c->name,
102                            c->hostname);
103                 return false;
104         }
105
106         from = lookup_node(from_name);
107
108         if(!from) {
109                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
110                            "REQ_KEY", c->name, c->hostname, from_name);
111                 return false;
112         }
113
114         to = lookup_node(to_name);
115
116         if(!to) {
117                 logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
118                            "REQ_KEY", c->name, c->hostname, to_name);
119                 return false;
120         }
121
122         /* Check if this key request is for us */
123
124         if(to == myself) {                      /* Yes, send our own key back */
125                 mykeyused = true;
126                 from->received_seqno = 0;
127                 memset(from->late, 0, sizeof(from->late));
128                 send_ans_key(c, myself, from);
129         } else {
130                 if(tunnelserver)
131                         return false;
132
133                 send_req_key(to->nexthop->connection, from, to);
134         }
135
136         return true;
137 }
138
139 bool send_ans_key(connection_t *c, const node_t *from, const node_t *to) {
140         char *key;
141
142         cp();
143
144         key = alloca(2 * from->keylength + 1);
145         bin2hex(from->key, key, from->keylength);
146         key[from->keylength * 2] = '\0';
147
148         return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
149                                                 from->name, to->name, key,
150                                                 from->cipher ? from->cipher->nid : 0,
151                                                 from->digest ? from->digest->type : 0, from->maclength,
152                                                 from->compression);
153 }
154
155 bool ans_key_h(connection_t *c) {
156         char from_name[MAX_STRING_SIZE];
157         char to_name[MAX_STRING_SIZE];
158         char key[MAX_STRING_SIZE];
159         int cipher, digest, maclength, compression;
160         node_t *from, *to;
161
162         cp();
163
164         if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d",
165                 from_name, to_name, key, &cipher, &digest, &maclength,
166                 &compression) != 7) {
167                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY", c->name,
168                            c->hostname);
169                 return false;
170         }
171
172         from = lookup_node(from_name);
173
174         if(!from) {
175                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
176                            "ANS_KEY", c->name, c->hostname, from_name);
177                 return false;
178         }
179
180         to = lookup_node(to_name);
181
182         if(!to) {
183                 logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
184                            "ANS_KEY", c->name, c->hostname, to_name);
185                 return false;
186         }
187
188         /* Forward it if necessary */
189
190         if(to != myself) {
191                 if(tunnelserver)
192                         return false;
193
194                 return send_request(to->nexthop->connection, "%s", c->buffer);
195         }
196
197         /* Update our copy of the origin's packet key */
198
199         if(from->key)
200                 free(from->key);
201
202         from->key = xstrdup(key);
203         from->keylength = strlen(key) / 2;
204         hex2bin(from->key, from->key, from->keylength);
205         from->key[from->keylength] = '\0';
206
207         from->status.validkey = true;
208         from->status.waitingforkey = false;
209         from->sent_seqno = 0;
210
211         /* Check and lookup cipher and digest algorithms */
212
213         if(cipher) {
214                 from->cipher = EVP_get_cipherbynid(cipher);
215
216                 if(!from->cipher) {
217                         logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name,
218                                    from->hostname);
219                         return false;
220                 }
221
222                 if(from->keylength != from->cipher->key_len + from->cipher->iv_len) {
223                         logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name,
224                                    from->hostname);
225                         return false;
226                 }
227         } else {
228                 from->cipher = NULL;
229         }
230
231         from->maclength = maclength;
232
233         if(digest) {
234                 from->digest = EVP_get_digestbynid(digest);
235
236                 if(!from->digest) {
237                         logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name,
238                                    from->hostname);
239                         return false;
240                 }
241
242                 if(from->maclength > from->digest->md_size || from->maclength < 0) {
243                         logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"),
244                                    from->name, from->hostname);
245                         return false;
246                 }
247         } else {
248                 from->digest = NULL;
249         }
250
251         if(compression < 0 || compression > 11) {
252                 logger(LOG_ERR, _("Node %s (%s) uses bogus compression level!"), from->name, from->hostname);
253                 return false;
254         }
255         
256         from->compression = compression;
257
258         if(from->cipher)
259                 if(!EVP_EncryptInit_ex(&from->packet_ctx, from->cipher, NULL, (unsigned char *)from->key, (unsigned char *)from->key + from->cipher->key_len)) {
260                         logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"),
261                                         from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL));
262                         return false;
263                 }
264
265         if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes)
266                 send_mtu_probe(from);
267
268         flush_queue(from);
269
270         return true;
271 }