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