Release notes for 1.0.2
[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.23 2003/10/11 12:16:13 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         forward_request(c);
84
85         return true;
86 }
87
88 bool send_req_key(connection_t *c, const node_t *from, const node_t *to)
89 {
90         cp();
91
92         return send_request(c, "%d %s %s", REQ_KEY, from->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                 mykeyused = true;
129                 from->received_seqno = 0;
130                 memset(from->late, 0, sizeof(from->late));
131                 send_ans_key(c, myself, from);
132         } else {
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 {
141         char key[MAX_STRING_SIZE];
142
143         cp();
144
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 {
157         char from_name[MAX_STRING_SIZE];
158         char to_name[MAX_STRING_SIZE];
159         char key[MAX_STRING_SIZE];
160         int cipher, digest, maclength, compression;
161         node_t *from, *to;
162
163         cp();
164
165         if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d",
166                 from_name, to_name, key, &cipher, &digest, &maclength,
167                 &compression) != 7) {
168                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY", c->name,
169                            c->hostname);
170                 return false;
171         }
172
173         from = lookup_node(from_name);
174
175         if(!from) {
176                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
177                            "ANS_KEY", c->name, c->hostname, from_name);
178                 return false;
179         }
180
181         to = lookup_node(to_name);
182
183         if(!to) {
184                 logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
185                            "ANS_KEY", c->name, c->hostname, to_name);
186                 return false;
187         }
188
189         /* Forward it if necessary */
190
191         if(to != myself) {
192                 return send_request(to->nexthop->connection, "%s", c->buffer);
193         }
194
195         /* Update our copy of the origin's packet key */
196
197         if(from->key)
198                 free(from->key);
199
200         from->key = xstrdup(key);
201         from->keylength = strlen(key) / 2;
202         hex2bin(from->key, from->key, from->keylength);
203         from->key[from->keylength] = '\0';
204
205         from->status.validkey = true;
206         from->status.waitingforkey = false;
207         from->sent_seqno = 0;
208
209         /* Check and lookup cipher and digest algorithms */
210
211         if(cipher) {
212                 from->cipher = EVP_get_cipherbynid(cipher);
213
214                 if(!from->cipher) {
215                         logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name,
216                                    from->hostname);
217                         return false;
218                 }
219
220                 if(from->keylength != from->cipher->key_len + from->cipher->iv_len) {
221                         logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name,
222                                    from->hostname);
223                         return false;
224                 }
225         } else {
226                 from->cipher = NULL;
227         }
228
229         from->maclength = maclength;
230
231         if(digest) {
232                 from->digest = EVP_get_digestbynid(digest);
233
234                 if(!from->digest) {
235                         logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name,
236                                    from->hostname);
237                         return false;
238                 }
239
240                 if(from->maclength > from->digest->md_size || from->maclength < 0) {
241                         logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"),
242                                    from->name, from->hostname);
243                         return false;
244                 }
245         } else {
246                 from->digest = NULL;
247         }
248
249         if(compression < 0 || compression > 11) {
250                 logger(LOG_ERR, _("Node %s (%s) uses bogus compression level!"), from->name, from->hostname);
251                 return false;
252         }
253         
254         from->compression = compression;
255
256         if(from->cipher)
257                 if(!EVP_EncryptInit_ex(&from->packet_ctx, from->cipher, NULL, from->key, from->key + from->cipher->key_len)) {
258                         logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"),
259                                         from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL));
260                         return false;
261                 }
262
263
264         flush_queue(from);
265
266         return true;
267 }