GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/isakmpd/connection.c Lines: 0 161 0.0 %
Date: 2017-11-07 Branches: 0 104 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: connection.c,v 1.38 2017/08/06 13:54:04 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/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
	char *name;
150
151
	gettimeofday(&now, 0);
152
	now.tv_sec += conf_get_num("General", "check-interval",
153
	    CHECK_INTERVAL);
154
	conn->ev = timer_add_event("connection_checker",
155
	    connection_checker, conn, &now);
156
	if (!conn->ev)
157
		log_print("%s: could not add timer event", __func__);
158
	if (ui_daemon_passive)
159
		return;
160
161
	name = strdup(conn->name);
162
	if (!name) {
163
		log_print("%s: strdup (\"%s\") failed", __func__, conn->name);
164
		return;
165
	}
166
	pf_key_v2_connection_check(name);
167
}
168
169
/* Find the connection named NAME.  */
170
static struct connection *
171
connection_lookup(char *name)
172
{
173
	struct connection *conn;
174
175
	for (conn = TAILQ_FIRST(&connections); conn;
176
	    conn = TAILQ_NEXT(conn, link))
177
		if (strcasecmp(conn->name, name) == 0)
178
			return conn;
179
	return 0;
180
}
181
182
/* Does the connection named NAME exist?  */
183
int
184
connection_exist(char *name)
185
{
186
	return (connection_lookup(name) != 0);
187
}
188
189
/* Find the passive connection named NAME.  */
190
static struct connection_passive *
191
connection_passive_lookup_by_name(char *name)
192
{
193
	struct connection_passive *conn;
194
195
	for (conn = TAILQ_FIRST(&connections_passive); conn;
196
	    conn = TAILQ_NEXT(conn, link))
197
		if (strcasecmp(conn->name, name) == 0)
198
			return conn;
199
	return 0;
200
}
201
202
/*
203
 * IDs of different types cannot be the same.
204
 * XXX Rename to ipsec_compare_id, and move to ipsec.c ?
205
 */
206
static int
207
compare_ids(u_int8_t *id1, u_int8_t *id2, size_t idlen)
208
{
209
	int	id1_type, id2_type;
210
211
	id1_type = GET_ISAKMP_ID_TYPE(id1);
212
	id2_type = GET_ISAKMP_ID_TYPE(id2);
213
214
	return id1_type == id2_type ? memcmp(id1 + ISAKMP_ID_DATA_OFF,
215
	    id2 + ISAKMP_ID_DATA_OFF, idlen - ISAKMP_ID_DATA_OFF) : -1;
216
}
217
218
/* Find the connection named with matching IDs.  */
219
char *
220
connection_passive_lookup_by_ids(u_int8_t *id1, u_int8_t *id2)
221
{
222
	struct connection_passive *conn;
223
224
	for (conn = TAILQ_FIRST(&connections_passive); conn;
225
	    conn = TAILQ_NEXT(conn, link)) {
226
		if (!conn->remote_id)
227
			continue;
228
229
		/*
230
		 * If both IDs match what we have saved, return the name.
231
		 * Don't bother in which order they are.
232
		 */
233
		if ((compare_ids(id1, conn->local_id, conn->local_sz) == 0 &&
234
		    compare_ids(id2, conn->remote_id, conn->remote_sz) == 0) ||
235
		    (compare_ids(id1, conn->remote_id, conn->remote_sz) == 0 &&
236
		    compare_ids(id2, conn->local_id, conn->local_sz) == 0)) {
237
			LOG_DBG((LOG_MISC, 60,
238
			    "connection_passive_lookup_by_ids: "
239
			    "returned \"%s\"", conn->name));
240
			return conn->name;
241
		}
242
	}
243
244
	/*
245
	 * In the road warrior case, we do not know the remote ID. In that
246
	 * case we will just match against the local ID.
247
	 */
248
	for (conn = TAILQ_FIRST(&connections_passive); conn;
249
	    conn = TAILQ_NEXT(conn, link)) {
250
		if (!conn->remote_id)
251
			continue;
252
253
		if (compare_ids(id1, conn->local_id, conn->local_sz) == 0 ||
254
		    compare_ids(id2, conn->local_id, conn->local_sz) == 0) {
255
			LOG_DBG((LOG_MISC, 60,
256
			    "connection_passive_lookup_by_ids: returned \"%s\""
257
			    " only matched local id", conn->name));
258
			return conn->name;
259
		}
260
	}
261
	LOG_DBG((LOG_MISC, 60,
262
	    "connection_passive_lookup_by_ids: no match"));
263
	return 0;
264
}
265
266
/*
267
 * Setup NAME to be a connection that should be up "always", i.e. if it dies,
268
 * for whatever reason, it should be tried to be brought up, over and over
269
 * again.
270
 */
271
int
272
connection_setup(char *name)
273
{
274
	struct connection *conn = 0;
275
	struct timeval  now;
276
277
	/* Check for trials to add duplicate connections.  */
278
	if (connection_lookup(name)) {
279
		LOG_DBG((LOG_MISC, 10,
280
		    "connection_setup: cannot add \"%s\" twice", name));
281
		return 0;
282
	}
283
	conn = calloc(1, sizeof *conn);
284
	if (!conn) {
285
		log_error("connection_setup: calloc (1, %lu) failed",
286
		    (unsigned long)sizeof *conn);
287
		goto fail;
288
	}
289
	conn->name = strdup(name);
290
	if (!conn->name) {
291
		log_error("connection_setup: strdup (\"%s\") failed", name);
292
		goto fail;
293
	}
294
	gettimeofday(&now, 0);
295
	conn->ev = timer_add_event("connection_checker", connection_checker,
296
	    conn, &now);
297
	if (!conn->ev) {
298
		log_print("connection_setup: could not add timer event");
299
		goto fail;
300
	}
301
	TAILQ_INSERT_TAIL(&connections, conn, link);
302
	return 0;
303
304
fail:
305
	if (conn) {
306
		free(conn->name);
307
		free(conn);
308
	}
309
	return -1;
310
}
311
312
int
313
connection_record_passive(char *name)
314
{
315
	struct connection_passive *conn;
316
	char           *local_id, *remote_id;
317
318
	if (connection_passive_lookup_by_name(name)) {
319
		LOG_DBG((LOG_MISC, 10,
320
		    "connection_record_passive: cannot add \"%s\" twice",
321
		    name));
322
		return 0;
323
	}
324
	local_id = conf_get_str(name, "Local-ID");
325
	if (!local_id) {
326
		log_print("connection_record_passive: "
327
		    "\"Local-ID\" is missing from section [%s]", name);
328
		return -1;
329
	}
330
	/* If the remote id lookup fails we defer it to later */
331
	remote_id = conf_get_str(name, "Remote-ID");
332
333
	conn = calloc(1, sizeof *conn);
334
	if (!conn) {
335
		log_error("connection_record_passive: calloc (1, %lu) failed",
336
		    (unsigned long)sizeof *conn);
337
		return -1;
338
	}
339
	conn->name = strdup(name);
340
	if (!conn->name) {
341
		log_error("connection_record_passive: strdup (\"%s\") failed",
342
		    name);
343
		goto fail;
344
	}
345
	/* XXX IPsec DOI-specific.  */
346
	conn->local_id = ipsec_build_id(local_id, &conn->local_sz);
347
	if (!conn->local_id)
348
		goto fail;
349
350
	if (remote_id) {
351
		conn->remote_id = ipsec_build_id(remote_id, &conn->remote_sz);
352
		if (!conn->remote_id)
353
			goto fail;
354
	} else
355
		conn->remote_id = 0;
356
357
	TAILQ_INSERT_TAIL(&connections_passive, conn, link);
358
359
	LOG_DBG((LOG_MISC, 60,
360
	    "connection_record_passive: passive connection \"%s\" added",
361
	    conn->name));
362
	return 0;
363
364
fail:
365
	free(conn->local_id);
366
	free(conn->name);
367
	free(conn);
368
	return -1;
369
}
370
371
/* Remove the connection named NAME.  */
372
void
373
connection_teardown(char *name)
374
{
375
	struct connection *conn;
376
377
	conn = connection_lookup(name);
378
	if (!conn)
379
		return;
380
381
	TAILQ_REMOVE(&connections, conn, link);
382
	timer_remove_event(conn->ev);
383
	free(conn->name);
384
	free(conn);
385
}
386
387
/* Remove the passive connection named NAME.  */
388
static void
389
connection_passive_teardown(char *name)
390
{
391
	struct connection_passive *conn;
392
393
	conn = connection_passive_lookup_by_name(name);
394
	if (!conn)
395
		return;
396
397
	TAILQ_REMOVE(&connections_passive, conn, link);
398
	free(conn->name);
399
	free(conn->local_id);
400
	free(conn->remote_id);
401
	free(conn);
402
}
403
404
void
405
connection_report(void)
406
{
407
	struct connection *conn;
408
	struct timeval  now;
409
	struct connection_passive *pconn;
410
	struct doi     *doi = doi_lookup(ISAKMP_DOI_ISAKMP);
411
412
	gettimeofday(&now, 0);
413
	for (conn = TAILQ_FIRST(&connections); conn;
414
	    conn = TAILQ_NEXT(conn, link))
415
		LOG_DBG((LOG_REPORT, 0,
416
		    "connection_report: connection %s next check %lld seconds",
417
		    (conn->name ? conn->name : "<unnamed>"),
418
		    (long long)(conn->ev->expiration.tv_sec - now.tv_sec)));
419
	for (pconn = TAILQ_FIRST(&connections_passive); pconn;
420
	    pconn = TAILQ_NEXT(pconn, link))
421
		LOG_DBG((LOG_REPORT, 0,
422
		    "connection_report: passive connection %s %s", pconn->name,
423
		    doi->decode_ids("local_id: %s, remote_id: %s",
424
		    pconn->local_id, pconn->local_sz,
425
		    pconn->remote_id, pconn->remote_sz, 1)));
426
}
427
428
/* Reinitialize all connections (SIGHUP handling).  */
429
void
430
connection_reinit(void)
431
{
432
	struct connection *conn, *next;
433
	struct connection_passive *pconn, *pnext;
434
435
	LOG_DBG((LOG_MISC, 30,
436
	    "connection_reinit: reinitializing connection list"));
437
438
	/* Remove all present connections.  */
439
	for (conn = TAILQ_FIRST(&connections); conn; conn = next) {
440
		next = TAILQ_NEXT(conn, link);
441
		connection_teardown(conn->name);
442
	}
443
444
	for (pconn = TAILQ_FIRST(&connections_passive); pconn; pconn = pnext) {
445
		pnext = TAILQ_NEXT(pconn, link);
446
		connection_passive_teardown(pconn->name);
447
	}
448
449
	/* Setup new connections, as the (new) config directs.  */
450
	connection_init();
451
}