1798edeb112c4354bcc1317b835bb5348d610d7f
[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.3 2002/04/28 12:46:26 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 #ifdef USE_OPENSSL
183   return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
184                       from->name, to->name, key, from->cipher?from->cipher->nid:0, from->digest?from->digest->type:0, from->maclength, from->compression);
185 #endif
186 #ifdef USE_GCRYPT
187   return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
188                       from->name, to->name, key, from->cipher?-1:0, from->digest?-1:0, from->maclength, from->compression);
189 #endif
190 }
191
192 int ans_key_h(connection_t *c)
193 {
194   char from_name[MAX_STRING_SIZE];
195   char to_name[MAX_STRING_SIZE];
196   char key[MAX_STRING_SIZE];
197   int cipher, digest, maclength, compression;
198   node_t *from, *to;
199 cp
200   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)
201     {
202        syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY",
203               c->name, c->hostname);
204        return -1;
205     }
206
207   from = lookup_node(from_name);
208
209   if(!from)
210     {
211       syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"), "ANS_KEY",
212              c->name, c->hostname, from_name);
213       return -1;
214     }
215
216   to = lookup_node(to_name);
217
218   if(!to)
219     {
220       syslog(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"), "ANS_KEY",
221              c->name, c->hostname, to_name);
222       return -1;
223     }
224
225   /* Forward it if necessary */
226
227   if(to != myself)
228     {
229       return send_request(to->nexthop->connection, "%s", c->buffer);
230     }
231
232   /* Update our copy of the origin's packet key */
233
234   if(from->key)
235     free(from->key);
236
237   from->key = xstrdup(key);
238   from->keylength = strlen(key) / 2;
239   hex2bin(from->key, from->key, from->keylength);
240   from->key[from->keylength] = '\0';
241
242   from->status.validkey = 1;
243   from->status.waitingforkey = 0;
244   
245   /* Check and lookup cipher and digest algorithms */
246
247   if(cipher)
248     {
249 #ifdef USE_OPENSSL
250       from->cipher = EVP_get_cipherbynid(cipher);
251       if(!from->cipher)
252         {
253           syslog(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname);
254           return -1;
255         }
256       if(from->keylength != from->cipher->key_len + from->cipher->iv_len)
257         {
258           syslog(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname);
259           return -1;
260         }
261 #endif
262     }
263   else
264     {
265       from->cipher = NULL;
266     }
267
268   from->maclength = maclength;
269
270   if(digest)
271     {
272 #ifdef USE_OPENSSL
273       from->digest = EVP_get_digestbynid(digest);
274       if(!from->digest)
275         {
276           syslog(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname);
277           return -1;
278         }
279       if(from->maclength > from->digest->md_size || from->maclength < 0)
280         {
281           syslog(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname);
282           return -1;
283         }
284 #endif
285     }
286   else
287     {
288       from->digest = NULL;
289     }
290
291   from->compression = compression;
292   
293   flush_queue(from);
294 cp
295   return 0;
296 }