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 |
|
|
} |