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