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