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