xref: /openbsd-src/sbin/isakmpd/connection.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /* $OpenBSD: connection.c,v 1.35 2007/04/16 13:01:39 moritz 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 #include "pf_key_v2.h"
46 
47 /* XXX isakmp.h only required for compare_ids().  */
48 #include "isakmp.h"
49 
50 #include "log.h"
51 #include "timer.h"
52 #include "ui.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
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
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 	if (!ui_daemon_passive)
160 		pf_key_v2_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;
170 	    conn = TAILQ_NEXT(conn, link))
171 		if (strcasecmp(conn->name, name) == 0)
172 			return conn;
173 	return 0;
174 }
175 
176 /* Does the connection named NAME exist?  */
177 int
178 connection_exist(char *name)
179 {
180 	return (connection_lookup(name) != 0);
181 }
182 
183 /* Find the passive connection named NAME.  */
184 static struct connection_passive *
185 connection_passive_lookup_by_name(char *name)
186 {
187 	struct connection_passive *conn;
188 
189 	for (conn = TAILQ_FIRST(&connections_passive); conn;
190 	    conn = TAILQ_NEXT(conn, link))
191 		if (strcasecmp(conn->name, name) == 0)
192 			return conn;
193 	return 0;
194 }
195 
196 /*
197  * IDs of different types cannot be the same.
198  * XXX Rename to ipsec_compare_id, and move to ipsec.c ?
199  */
200 static int
201 compare_ids(u_int8_t *id1, u_int8_t *id2, size_t idlen)
202 {
203 	int	id1_type, id2_type;
204 
205 	id1_type = GET_ISAKMP_ID_TYPE(id1);
206 	id2_type = GET_ISAKMP_ID_TYPE(id2);
207 
208 	return id1_type == id2_type ? memcmp(id1 + ISAKMP_ID_DATA_OFF,
209 	    id2 + ISAKMP_ID_DATA_OFF, 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 		if (!conn->remote_id)
221 			continue;
222 
223 		/*
224 		 * If both IDs match what we have saved, return the name.
225 		 * Don't bother in which order they are.
226 		 */
227 		if ((compare_ids(id1, conn->local_id, conn->local_sz) == 0 &&
228 		    compare_ids(id2, conn->remote_id, conn->remote_sz) == 0) ||
229 		    (compare_ids(id1, conn->remote_id, conn->remote_sz) == 0 &&
230 		    compare_ids(id2, conn->local_id, conn->local_sz) == 0)) {
231 			LOG_DBG((LOG_MISC, 60,
232 			    "connection_passive_lookup_by_ids: "
233 			    "returned \"%s\"", conn->name));
234 			return conn->name;
235 		}
236 	}
237 
238 	/*
239 	 * In the road warrior case, we do not know the remote ID. In that
240 	 * case we will just match against the local ID.
241 	 */
242 	for (conn = TAILQ_FIRST(&connections_passive); conn;
243 	    conn = TAILQ_NEXT(conn, link)) {
244 		if (!conn->remote_id)
245 			continue;
246 
247 		if (compare_ids(id1, conn->local_id, conn->local_sz) == 0 ||
248 		    compare_ids(id2, conn->local_id, conn->local_sz) == 0) {
249 			LOG_DBG((LOG_MISC, 60,
250 			    "connection_passive_lookup_by_ids: returned \"%s\""
251 			    " only matched local id", conn->name));
252 			return conn->name;
253 		}
254 	}
255 	LOG_DBG((LOG_MISC, 60,
256 	    "connection_passive_lookup_by_ids: no match"));
257 	return 0;
258 }
259 
260 /*
261  * Setup NAME to be a connection that should be up "always", i.e. if it dies,
262  * for whatever reason, it should be tried to be brought up, over and over
263  * again.
264  */
265 int
266 connection_setup(char *name)
267 {
268 	struct connection *conn = 0;
269 	struct timeval  now;
270 
271 	/* Check for trials to add duplicate connections.  */
272 	if (connection_lookup(name)) {
273 		LOG_DBG((LOG_MISC, 10,
274 		    "connection_setup: cannot add \"%s\" twice", name));
275 		return 0;
276 	}
277 	conn = calloc(1, sizeof *conn);
278 	if (!conn) {
279 		log_error("connection_setup: calloc (1, %lu) failed",
280 		    (unsigned long)sizeof *conn);
281 		goto fail;
282 	}
283 	conn->name = strdup(name);
284 	if (!conn->name) {
285 		log_error("connection_setup: strdup (\"%s\") failed", name);
286 		goto fail;
287 	}
288 	gettimeofday(&now, 0);
289 	conn->ev = timer_add_event("connection_checker", connection_checker,
290 	    conn, &now);
291 	if (!conn->ev) {
292 		log_print("connection_setup: could not add timer event");
293 		goto fail;
294 	}
295 	TAILQ_INSERT_TAIL(&connections, conn, link);
296 	return 0;
297 
298 fail:
299 	if (conn) {
300 		free(conn->name);
301 		free(conn);
302 	}
303 	return -1;
304 }
305 
306 int
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 	free(conn->local_id);
360 	free(conn->name);
361 	free(conn);
362 	return -1;
363 }
364 
365 /* Remove the connection named NAME.  */
366 void
367 connection_teardown(char *name)
368 {
369 	struct connection *conn;
370 
371 	conn = connection_lookup(name);
372 	if (!conn)
373 		return;
374 
375 	TAILQ_REMOVE(&connections, conn, link);
376 	timer_remove_event(conn->ev);
377 	free(conn->name);
378 	free(conn);
379 }
380 
381 /* Remove the passive connection named NAME.  */
382 static void
383 connection_passive_teardown(char *name)
384 {
385 	struct connection_passive *conn;
386 
387 	conn = connection_passive_lookup_by_name(name);
388 	if (!conn)
389 		return;
390 
391 	TAILQ_REMOVE(&connections_passive, conn, link);
392 	free(conn->name);
393 	free(conn->local_id);
394 	free(conn->remote_id);
395 	free(conn);
396 }
397 
398 void
399 connection_report(void)
400 {
401 	struct connection *conn;
402 	struct timeval  now;
403 	struct connection_passive *pconn;
404 	struct doi     *doi = doi_lookup(ISAKMP_DOI_ISAKMP);
405 
406 	gettimeofday(&now, 0);
407 	for (conn = TAILQ_FIRST(&connections); conn;
408 	    conn = TAILQ_NEXT(conn, link))
409 		LOG_DBG((LOG_REPORT, 0,
410 		    "connection_report: connection %s next check %ld seconds",
411 		    (conn->name ? conn->name : "<unnamed>"),
412 		    conn->ev->expiration.tv_sec - now.tv_sec));
413 	for (pconn = TAILQ_FIRST(&connections_passive); pconn;
414 	    pconn = TAILQ_NEXT(pconn, link))
415 		LOG_DBG((LOG_REPORT, 0,
416 		    "connection_report: passive connection %s %s", pconn->name,
417 		    doi->decode_ids("local_id: %s, remote_id: %s",
418 		    pconn->local_id, pconn->local_sz,
419 		    pconn->remote_id, pconn->remote_sz, 1)));
420 }
421 
422 /* Reinitialize all connections (SIGHUP handling).  */
423 void
424 connection_reinit(void)
425 {
426 	struct connection *conn, *next;
427 	struct connection_passive *pconn, *pnext;
428 
429 	LOG_DBG((LOG_MISC, 30,
430 	    "connection_reinit: reinitializing connection list"));
431 
432 	/* Remove all present connections.  */
433 	for (conn = TAILQ_FIRST(&connections); conn; conn = next) {
434 		next = TAILQ_NEXT(conn, link);
435 		connection_teardown(conn->name);
436 	}
437 
438 	for (pconn = TAILQ_FIRST(&connections_passive); pconn; pconn = pnext) {
439 		pnext = TAILQ_NEXT(pconn, link);
440 		connection_passive_teardown(pconn->name);
441 	}
442 
443 	/* Setup new connections, as the (new) config directs.  */
444 	connection_init();
445 }
446