xref: /openbsd-src/sbin/isakmpd/connection.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: connection.c,v 1.22 2001/07/06 14:37:11 ho Exp $	*/
2 /*	$EOM: connection.c,v 1.28 2000/11/23 12:21:18 niklas Exp $	*/
3 
4 /*
5  * Copyright (c) 1999, 2000, 2001 Niklas Hallqvist.  All rights reserved.
6  * Copyright (c) 1999 Hakan Olsson.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by Ericsson Radio Systems.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * This code was written under funding by Ericsson Radio Systems.
36  */
37 
38 #include <sys/queue.h>
39 #include <sys/time.h>
40 #include <sys/socket.h>
41 #include <stdlib.h>
42 #include <string.h>
43 
44 #include "sysdep.h"
45 
46 #include "conf.h"
47 #include "connection.h"
48 #include "doi.h"
49 #include "ipsec.h"
50 
51 /* XXX isakmp.h only required for compare_ids().  */
52 #include "isakmp.h"
53 
54 #include "log.h"
55 #include "timer.h"
56 #include "util.h"
57 
58 /* How often should we check that connections we require to be up, are up?  */
59 #define CHECK_INTERVAL 60
60 
61 static void connection_passive_teardown (char *);
62 
63 struct connection
64 {
65   TAILQ_ENTRY (connection) link;
66   char *name;
67   struct event *ev;
68 };
69 
70 struct connection_passive
71 {
72   TAILQ_ENTRY (connection_passive) link;
73   char *name;
74   u_int8_t *local_id, *remote_id;
75   size_t local_sz, remote_sz;
76 
77 #if 0
78   /* XXX Potential additions to 'connection_passive'.  */
79   char *isakmp_peer;
80   struct sa *sa;                /* XXX "Soft" ref to active sa?  */
81   struct timeval sa_expiration; /* XXX *sa may expire.  */
82 #endif
83 };
84 
85 TAILQ_HEAD (connection_head, connection) connections;
86 TAILQ_HEAD (passive_head, connection_passive) connections_passive;
87 
88 /*
89  * This is where we setup all the connections we want there right from the
90  * start.
91  */
92 void
93 connection_init (void)
94 {
95   struct conf_list *conns, *attrs;
96   struct conf_list_node *conn, *attr = NULL;
97 
98   /*
99    * Passive connections normally include: all "active" connections that
100    * are not flagged "Active-Only", plus all connections listed in
101    * the 'Passive-Connections' list.
102    */
103 
104   TAILQ_INIT (&connections);
105   TAILQ_INIT (&connections_passive);
106 
107   conns = conf_get_list ("Phase 2", "Connections");
108   if (conns)
109     {
110       for (conn = TAILQ_FIRST (&conns->fields); conn;
111 	   conn = TAILQ_NEXT (conn, link))
112 	{
113 	  if (connection_setup (conn->field))
114 	    log_print ("connection_init: could not setup \"%s\"", conn->field);
115 
116 	  /* XXX Break/abort here if connection_setup failed?  */
117 
118 	  /*
119 	   * XXX This code (i.e. the attribute lookup) seems like a
120 	   * likely candidate for factoring out into a function of its
121 	   * own.
122 	   */
123 	  attrs = conf_get_list (conn->field, "Flags");
124 	  if (attrs)
125 	    for (attr = TAILQ_FIRST (&attrs->fields); attr;
126 		 attr = TAILQ_NEXT (attr, link))
127 	      if (strcasecmp ("active-only", attr->field) == 0)
128 		break;
129 	  if (!attrs || (attrs && !attr))
130 	    if (connection_record_passive (conn->field))
131 	      log_print ("connection_init: could not record "
132 			 "connection \"%s\"", conn->field);
133 	  if (attrs)
134 	    conf_free_list (attrs);
135 
136 	}
137       conf_free_list (conns);
138     }
139 
140   conns = conf_get_list ("Phase 2", "Passive-Connections");
141   if (conns)
142     {
143       for (conn = TAILQ_FIRST (&conns->fields); conn;
144 	   conn = TAILQ_NEXT (conn, link))
145 	if (connection_record_passive (conn->field))
146 	  log_print ("connection_init: could not record passive "
147 		     "connection \"%s\"", conn->field);
148       conf_free_list (conns);
149     }
150 }
151 
152 /* Check the connection in VCONN and schedule another check later.  */
153 static void
154 connection_checker (void *vconn)
155 {
156   struct timeval now;
157   struct connection *conn = vconn;
158 
159   gettimeofday (&now, 0);
160   now.tv_sec += conf_get_num ("General", "check-interval", CHECK_INTERVAL);
161   conn->ev
162     = timer_add_event ("connection_checker", connection_checker, conn, &now);
163   if (!conn->ev)
164     log_print ("connection_checker: could not add timer event");
165   sysdep_connection_check (conn->name);
166 }
167 
168 /* Find the connection named NAME.  */
169 static struct connection *
170 connection_lookup (char *name)
171 {
172   struct connection *conn;
173 
174   for (conn = TAILQ_FIRST (&connections); conn; conn = TAILQ_NEXT (conn, link))
175     if (strcasecmp (conn->name, name) == 0)
176       return conn;
177   return 0;
178 }
179 
180 /* Does the connection named NAME exist?  */
181 int
182 connection_exist (char *name)
183 {
184   return (connection_lookup (name) != 0);
185 }
186 
187 /* Find the passive connection named NAME.  */
188 static struct connection_passive *
189 connection_passive_lookup_by_name (char *name)
190 {
191   struct connection_passive *conn;
192 
193   for (conn = TAILQ_FIRST (&connections_passive); conn;
194        conn = TAILQ_NEXT (conn, link))
195     if (strcasecmp (conn->name, name) == 0)
196       return conn;
197   return 0;
198 }
199 
200 /*
201  * IDs of different types cannot be the same.
202  * XXX Rename to ipsec_compare_id, and move to ipsec.c ?
203  */
204 static int
205 compare_ids (u_int8_t *id1, u_int8_t *id2, size_t idlen)
206 {
207   int id1_type, id2_type;
208 
209   id1_type = GET_ISAKMP_ID_TYPE (id1);
210   id2_type = GET_ISAKMP_ID_TYPE (id2);
211 
212   return id1_type == id2_type
213     ? memcmp (id1 + ISAKMP_ID_DATA_OFF, id2 + ISAKMP_ID_DATA_OFF,
214 	      idlen - ISAKMP_ID_DATA_OFF) : -1;
215 }
216 
217 /* Find the connection named with matching IDs.  */
218 char *
219 connection_passive_lookup_by_ids (u_int8_t *id1, u_int8_t *id2)
220 {
221   struct connection_passive *conn;
222 
223   for (conn = TAILQ_FIRST (&connections_passive); conn;
224        conn = TAILQ_NEXT (conn, link))
225     {
226       if (!conn->remote_id)
227 	continue;
228 
229       /*
230        * If both IDs match what we have saved, return the name.  Don't bother
231        * in which order they are.
232        */
233       if ((compare_ids (id1, conn->local_id, conn->local_sz) == 0
234 	   && compare_ids (id2, conn->remote_id, conn->remote_sz) == 0)
235 	  || (compare_ids (id1, conn->remote_id, conn->remote_sz) == 0
236 	      && compare_ids (id2, conn->local_id, conn->local_sz) == 0))
237 	{
238 	  LOG_DBG ((LOG_MISC, 60,
239 		    "connection_passive_lookup_by_ids: returned \"%s\"",
240 		    conn->name));
241 	  return conn->name;
242 	}
243     }
244 
245   /* In the road warrior case, we do not know the remote ID. In that
246    * case we will just match against the local ID.
247    */
248   for (conn = TAILQ_FIRST (&connections_passive); conn;
249        conn = TAILQ_NEXT (conn, link))
250     {
251       if (!conn->remote_id)
252 	continue;
253 
254       if (compare_ids (id1, conn->local_id, conn->local_sz) == 0
255 	  || compare_ids (id2, conn->local_id, conn->local_sz) == 0)
256 	{
257 	  LOG_DBG ((LOG_MISC, 60,
258 		    "connection passive_lookup_by_ids: returned \"%s\""
259 		    " only matched local id", conn->name));
260 	  return conn->name;
261 	}
262     }
263   LOG_DBG ((LOG_MISC, 60,
264 	    "connection_passive_lookup_by_ids: no match"));
265   return 0;
266 }
267 
268 /*
269  * Setup NAME to be a connection that should be up "always", i.e. if it dies,
270  * for whatever reason, it should be tried to be brought up, over and over
271  * again.
272  */
273 int
274 connection_setup (char *name)
275 {
276   struct connection *conn = 0;
277   struct timeval now;
278 
279   /* Check for trials to add duplicate connections.  */
280   if (connection_lookup (name))
281     {
282       LOG_DBG ((LOG_MISC, 10, "connection_setup: cannot add \"%s\" twice",
283 		name));
284       return 0;
285     }
286 
287   conn = calloc (1, sizeof *conn);
288   if (!conn)
289     {
290       log_error ("connection_setup: calloc (1, %d) failed", sizeof *conn);
291       goto fail;
292     }
293 
294   conn->name = strdup (name);
295   if (!conn->name)
296     {
297       log_error ("connection_setup: strdup (\"%s\") failed", name);
298       goto fail;
299     }
300 
301   gettimeofday (&now, 0);
302   conn->ev
303     = timer_add_event ("connection_checker", connection_checker, conn, &now);
304   if (!conn->ev)
305     {
306       log_print ("connection_setup: could not add timer event");
307       goto fail;
308     }
309 
310   TAILQ_INSERT_TAIL (&connections, conn, link);
311   return 0;
312 
313  fail:
314   if (conn)
315     {
316       if (conn->name)
317 	free (conn->name);
318       free (conn);
319     }
320   return -1;
321 }
322 
323 int
324 connection_record_passive (char *name)
325 {
326   struct connection_passive *conn;
327   char *local_id, *remote_id;
328 
329   if (connection_passive_lookup_by_name (name))
330     {
331       LOG_DBG ((LOG_MISC, 10,
332 		"connection_record_passive: cannot add \"%s\" twice",
333 		name));
334       return 0;
335     }
336 
337   local_id = conf_get_str (name, "Local-ID");
338   if (!local_id)
339     {
340       log_print ("connection_record_passive: "
341 		 "\"Local-ID\" is missing from section [%s]",
342 		 name);
343       return -1;
344     }
345 
346   /* If the remote id lookup fails we defer it to later */
347   remote_id = conf_get_str (name, "Remote-ID");
348 
349   conn = calloc (1, sizeof *conn);
350   if (!conn)
351     {
352       log_error ("connection_record_passive: calloc (1, %d) failed",
353 		 sizeof *conn);
354       return -1;
355     }
356 
357   conn->name = strdup (name);
358   if (!conn->name)
359     {
360       log_error ("connection_record_passive: strdup (\"%s\") failed", name);
361       goto fail;
362     }
363 
364   /* XXX IPsec DOI-specific.  */
365   conn->local_id = ipsec_build_id (local_id, &conn->local_sz);
366   if (!conn->local_id)
367     goto fail;
368 
369   if (remote_id)
370     {
371       conn->remote_id = ipsec_build_id (remote_id, &conn->remote_sz);
372       if (!conn->remote_id)
373 	goto fail;
374     }
375   else
376     conn->remote_id = 0;
377 
378   TAILQ_INSERT_TAIL (&connections_passive, conn, link);
379 
380   LOG_DBG ((LOG_MISC, 60,
381 	    "connection_record_passive: passive connection \"%s\" "
382 	    "added", conn->name));
383   return 0;
384 
385  fail:
386   if (conn->local_id)
387     free (conn->local_id);
388   if (conn->name)
389     free (conn->name);
390   free (conn);
391   return -1;
392 }
393 
394 /* Remove the connection named NAME.  */
395 void
396 connection_teardown (char *name)
397 {
398   struct connection *conn;
399 
400   conn = connection_lookup (name);
401   if (!conn)
402     return;
403 
404   TAILQ_REMOVE (&connections, conn, link);
405   timer_remove_event (conn->ev);
406   free (conn->name);
407   free (conn);
408 }
409 
410 /* Remove the passive connection named NAME.  */
411 static void
412 connection_passive_teardown (char *name)
413 {
414   struct connection_passive *conn;
415 
416   conn = connection_passive_lookup_by_name (name);
417   if (!conn)
418     return;
419 
420   TAILQ_REMOVE (&connections_passive, conn, link);
421   free (conn->name);
422   free (conn->local_id);
423   free (conn->remote_id);
424   free (conn);
425 }
426 
427 void
428 connection_report (void)
429 {
430   struct connection *conn;
431   struct timeval now;
432 #ifdef USE_DEBUG
433   struct connection_passive *pconn;
434   struct doi *doi = doi_lookup (ISAKMP_DOI_ISAKMP);
435 #endif
436 
437   gettimeofday (&now, 0);
438   for (conn = TAILQ_FIRST (&connections); conn; conn = TAILQ_NEXT (conn, link))
439     LOG_DBG ((LOG_REPORT, 0,
440 	      "connection_report: connection %s next check %ld seconds",
441 	      (conn->name ? conn->name : "<unnamed>"),
442 	      conn->ev->expiration.tv_sec - now.tv_sec));
443 #ifdef USE_DEBUG
444   for (pconn = TAILQ_FIRST (&connections_passive); pconn;
445        pconn = TAILQ_NEXT (pconn, link))
446     LOG_DBG ((LOG_REPORT, 0,
447 	      "connection_report: passive connection %s %s", pconn->name,
448 	      doi->decode_ids ("local_id: %s, remote_id: %s",
449 			       pconn->local_id, pconn->local_sz,
450 			       pconn->remote_id, pconn->remote_sz, 1)));
451 #endif
452 }
453 
454 /* Reinitialize all connections (SIGHUP handling).  */
455 void
456 connection_reinit (void)
457 {
458   struct connection *conn, *next;
459   struct connection_passive *pconn, *pnext;
460 
461   LOG_DBG ((LOG_MISC, 30,
462 	    "connection_reinit: reinitializing connection list"));
463 
464   /* Remove all present connections.  */
465   for (conn = TAILQ_FIRST (&connections); conn; conn = next)
466     {
467       next = TAILQ_NEXT (conn, link);
468       connection_teardown (conn->name);
469     }
470 
471   for (pconn = TAILQ_FIRST (&connections_passive); pconn; pconn = pnext)
472     {
473       pnext = TAILQ_NEXT (pconn, link);
474       connection_passive_teardown (pconn->name);
475     }
476 
477   /* Setup new connections, as the (new) config directs.  */
478   connection_init ();
479 }
480