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