A reachable node is always more preferable to an unreachable one...
[tinc] / src / protocol_key.c
1 /*
2     protocol_key.c -- handle the meta-protocol, key exchange
3     Copyright (C) 1999-2002 Ivo Timmermans <ivo@o2w.nl>,
4                   2000-2002 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.8 2002/09/03 20:43:25 guus Exp $
21 */
22
23 #include "config.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <syslog.h>
28 #include <stdio.h>
29 #include <stdarg.h>
30 #include <errno.h>
31
32 #include <utils.h>
33 #include <xalloc.h>
34 #include <avl_tree.h>
35
36 #include "conf.h"
37 #include "net.h"
38 #include "netutl.h"
39 #include "protocol.h"
40 #include "meta.h"
41 #include "connection.h"
42 #include "node.h"
43
44 #include "system.h"
45
46 int mykeyused = 0;
47
48 int send_key_changed(connection_t *c, node_t *n)
49 {
50   connection_t *other;
51   avl_node_t *node;
52 cp
53   /* Only send this message if some other daemon requested our key previously.
54      This reduces unnecessary key_changed broadcasts.
55   */
56
57   if(n == myself && !mykeyused)
58     return 0;
59
60   for(node = connection_tree->head; node; node = node->next)
61     {
62       other = (connection_t *)node->data;
63       if(other->status.active && other != c)
64         send_request(other, "%d %lx %s", KEY_CHANGED, random(), n->name);
65     }
66 cp
67   return 0;
68 }
69
70 int key_changed_h(connection_t *c)
71 {
72   char name[MAX_STRING_SIZE];
73   avl_node_t *node;
74   connection_t *other;
75   node_t *n;
76 cp
77   if(sscanf(c->buffer, "%*d %*x "MAX_STRING, name) != 1)
78     {
79       syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED",
80              c->name, c->hostname);
81       return -1;
82     }
83
84   if(seen_request(c->buffer))
85     return 0;
86
87   n = lookup_node(name);
88
89   if(!n)
90     {
91       syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist"), "KEY_CHANGED",
92              c->name, c->hostname, name);
93       return -1;
94     }
95
96   n->status.validkey = 0;
97   n->status.waitingforkey = 0;
98
99   /* Tell the others */
100
101   for(node = connection_tree->head; node; node = node->next)
102     {
103       other = (connection_t *)node->data;
104       if(other->status.active && other != c)
105         send_request(other, "%s", c->buffer);
106     }
107 cp
108   return 0;
109 }
110
111 int send_req_key(connection_t *c, node_t *from, node_t *to)
112 {
113 cp
114   return send_request(c, "%d %s %s", REQ_KEY,
115                       from->name, to->name);
116 }
117
118 int req_key_h(connection_t *c)
119 {
120   char from_name[MAX_STRING_SIZE];
121   char to_name[MAX_STRING_SIZE];
122   node_t *from, *to;
123 cp
124   if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING, from_name, to_name) != 2)
125     {
126        syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY",
127               c->name, c->hostname);
128        return -1;
129     }
130
131   from = lookup_node(from_name);
132
133   if(!from)
134     {
135       syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"), "REQ_KEY",
136              c->name, c->hostname, from_name);
137       return -1;
138     }
139
140   to = lookup_node(to_name);
141   
142   if(!to)
143     {
144       syslog(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"), "REQ_KEY",
145              c->name, c->hostname, to_name);
146       return -1;
147     }
148
149   /* Check if this key request is for us */
150
151   if(to == myself)      /* Yes, send our own key back */
152     {
153       mykeyused = 1;
154       from->sent_seqno = 0;
155       send_ans_key(c, myself, from);
156     }
157   else
158     {
159 /* Proxy keys
160       if(to->status.validkey)
161         {
162           send_ans_key(c, to, from);
163         }
164       else
165 */
166         send_req_key(to->nexthop->connection, from, to);
167     }
168
169 cp
170   return 0;
171 }
172
173 int send_ans_key(connection_t *c, node_t *from, node_t *to)
174 {
175   char key[MAX_STRING_SIZE];
176 cp
177   bin2hex(from->key, key, from->keylength);
178   key[from->keylength * 2] = '\0';
179 cp
180   return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
181                       from->name, to->name, key, from->cipher?from->cipher->nid:0, from->digest?from->digest->type:0, from->maclength, from->compression);
182 }
183
184 int ans_key_h(connection_t *c)
185 {
186   char from_name[MAX_STRING_SIZE];
187   char to_name[MAX_STRING_SIZE];
188   char key[MAX_STRING_SIZE];
189   int cipher, digest, maclength, compression;
190   node_t *from, *to;
191 cp
192   if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d", from_name, to_name, key, &cipher, &digest, &maclength, &compression) != 7)
193     {
194        syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY",
195               c->name, c->hostname);
196        return -1;
197     }
198
199   from = lookup_node(from_name);
200
201   if(!from)
202     {
203       syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"), "ANS_KEY",
204              c->name, c->hostname, from_name);
205       return -1;
206     }
207
208   to = lookup_node(to_name);
209
210   if(!to)
211     {
212       syslog(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"), "ANS_KEY",
213              c->name, c->hostname, to_name);
214       return -1;
215     }
216
217   /* Forward it if necessary */
218
219   if(to != myself)
220     {
221       return send_request(to->nexthop->connection, "%s", c->buffer);
222     }
223
224   /* Update our copy of the origin's packet key */
225
226   if(from->key)
227     free(from->key);
228
229   from->key = xstrdup(key);
230   from->keylength = strlen(key) / 2;
231   hex2bin(from->key, from->key, from->keylength);
232   from->key[from->keylength] = '\0';
233
234   from->status.validkey = 1;
235   from->status.waitingforkey = 0;
236   from->received_seqno = 0;
237     
238   /* Check and lookup cipher and digest algorithms */
239
240   if(cipher)
241     {
242       from->cipher = EVP_get_cipherbynid(cipher);
243       if(!from->cipher)
244         {
245           syslog(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname);
246           return -1;
247         }
248       if(from->keylength != from->cipher->key_len + from->cipher->iv_len)
249         {
250           syslog(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname);
251           return -1;
252         }
253     }
254   else
255     {
256       from->cipher = NULL;
257     }
258
259   from->maclength = maclength;
260
261   if(digest)
262     {
263       from->digest = EVP_get_digestbynid(digest);
264       if(!from->digest)
265         {
266           syslog(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname);
267           return -1;
268         }
269       if(from->maclength > from->digest->md_size || from->maclength < 0)
270         {
271           syslog(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname);
272           return -1;
273         }
274     }
275   else
276     {
277       from->digest = NULL;
278     }
279
280   from->compression = compression;
281   
282   flush_queue(from);
283 cp
284   return 0;
285 }