Merge branch 'master' into 1.1
[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-2009 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 "splay_tree.h"
26 #include "cipher.h"
27 #include "connection.h"
28 #include "logger.h"
29 #include "net.h"
30 #include "netutl.h"
31 #include "node.h"
32 #include "protocol.h"
33 #include "utils.h"
34 #include "xalloc.h"
35
36 static bool mykeyused = false;
37
38 bool send_key_changed(connection_t *c, const node_t *n) {
39         cp();
40
41         /* Only send this message if some other daemon requested our key previously.
42            This reduces unnecessary key_changed broadcasts.
43          */
44
45         if(n == myself && !mykeyused)
46                 return true;
47
48         return send_request(c, "%d %lx %s", KEY_CHANGED, random(), n->name);
49 }
50
51 bool key_changed_h(connection_t *c, char *request) {
52         char name[MAX_STRING_SIZE];
53         node_t *n;
54
55         cp();
56
57         if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) {
58                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED",
59                            c->name, c->hostname);
60                 return false;
61         }
62
63         if(seen_request(request))
64                 return true;
65
66         n = lookup_node(name);
67
68         if(!n) {
69                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist"),
70                            "KEY_CHANGED", c->name, c->hostname, name);
71                 return false;
72         }
73
74         n->status.validkey = false;
75         n->status.waitingforkey = false;
76
77         /* Tell the others */
78
79         if(!tunnelserver)
80                 forward_request(c, request);
81
82         return true;
83 }
84
85 bool send_req_key(connection_t *c, const node_t *from, const node_t *to) {
86         cp();
87
88         return send_request(c, "%d %s %s", REQ_KEY, from->name, to->name);
89 }
90
91 bool req_key_h(connection_t *c, char *request) {
92         char from_name[MAX_STRING_SIZE];
93         char to_name[MAX_STRING_SIZE];
94         node_t *from, *to;
95
96         cp();
97
98         if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
99                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY", c->name,
100                            c->hostname);
101                 return false;
102         }
103
104         from = lookup_node(from_name);
105
106         if(!from) {
107                 logger(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"),
108                            "REQ_KEY", c->name, c->hostname, from_name);
109                 return false;
110         }
111
112         to = lookup_node(to_name);
113
114         if(!to) {
115                 logger(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"),
116                            "REQ_KEY", c->name, c->hostname, to_name);
117                 return false;
118         }
119
120         /* Check if this key request is for us */
121
122         if(to == myself) {                      /* Yes, send our own key back */
123                 mykeyused = true;
124                 from->received_seqno = 0;
125                 memset(from->late, 0, sizeof from->late);
126                 send_ans_key(c, myself, from);
127         } else {
128                 if(tunnelserver)
129                         return false;
130
131                 if(!to->status.reachable) {
132                         logger(LOG_WARNING, _("Got %s from %s (%s) destination %s which is not reachable"),
133                                 "REQ_KEY", c->name, c->hostname, to_name);
134                         return true;
135                 }
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         size_t keylen = cipher_keylength(&from->cipher);
145         char key[keylen * 2 + 1];
146
147         cp();
148
149         cipher_get_key(&from->cipher, key);
150         bin2hex(key, key, keylen);
151         key[keylen * 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                                                 cipher_get_nid(&from->cipher),
156                                                 digest_get_nid(&from->digest), from->maclength,
157                                                 from->compression);
158 }
159
160 bool ans_key_h(connection_t *c, char *request) {
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(request, "%*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                 if(!to->status.reachable) {
200                         logger(LOG_WARNING, _("Got %s from %s (%s) destination %s which is not reachable"),
201                                    "ANS_KEY", c->name, c->hostname, to_name);
202                         return true;
203                 }
204
205                 return send_request(to->nexthop->connection, "%s", request);
206         }
207
208         /* Check and lookup cipher and digest algorithms */
209
210         if(!cipher_open_by_nid(&from->cipher, cipher)) {
211                 logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname);
212                 return false;
213         }
214
215         if(strlen(key) / 2 != cipher_keylength(&from->cipher)) {
216                 logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname);
217                 return false;
218         }
219
220         from->maclength = maclength;
221
222         if(!digest_open_by_nid(&from->digest, digest)) {
223                 logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname);
224                 return false;
225         }
226
227         if(from->maclength > digest_length(&from->digest) || from->maclength < 0) {
228                 logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname);
229                 return false;
230         }
231
232         if(compression < 0 || compression > 11) {
233                 logger(LOG_ERR, _("Node %s (%s) uses bogus compression level!"), from->name, from->hostname);
234                 return false;
235         }
236         
237         from->compression = compression;
238
239         /* Update our copy of the origin's packet key */
240
241         hex2bin(key, key, cipher_keylength(&from->cipher));
242         cipher_set_key(&from->cipher, key, false);
243
244         from->status.validkey = true;
245         from->status.waitingforkey = false;
246         from->sent_seqno = 0;
247
248         if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes)
249                 send_mtu_probe(from);
250
251         return true;
252 }