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