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