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