1 |
|
|
/* $OpenBSD: sa.c,v 1.123 2015/12/09 21:41:50 naddy Exp $ */ |
2 |
|
|
/* $EOM: sa.c,v 1.112 2000/12/12 00:22:52 niklas Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved. |
6 |
|
|
* Copyright (c) 1999, 2001 Angelos D. Keromytis. All rights reserved. |
7 |
|
|
* Copyright (c) 2003, 2004 Håkan Olsson. All rights reserved. |
8 |
|
|
* |
9 |
|
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
|
* modification, are permitted provided that the following conditions |
11 |
|
|
* are met: |
12 |
|
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
|
|
* notice, this list of conditions and the following disclaimer in the |
16 |
|
|
* documentation and/or other materials provided with the distribution. |
17 |
|
|
* |
18 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
19 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
20 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
21 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
22 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
23 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
25 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
27 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 |
|
|
*/ |
29 |
|
|
|
30 |
|
|
/* |
31 |
|
|
* This code was written under funding by Ericsson Radio Systems. |
32 |
|
|
*/ |
33 |
|
|
|
34 |
|
|
#include <sys/types.h> |
35 |
|
|
#include <sys/un.h> |
36 |
|
|
|
37 |
|
|
#include <stdlib.h> |
38 |
|
|
#include <string.h> |
39 |
|
|
#include <netdb.h> |
40 |
|
|
|
41 |
|
|
#include <regex.h> |
42 |
|
|
#include <keynote.h> |
43 |
|
|
|
44 |
|
|
#include "attribute.h" |
45 |
|
|
#include "conf.h" |
46 |
|
|
#include "connection.h" |
47 |
|
|
#include "cookie.h" |
48 |
|
|
#include "doi.h" |
49 |
|
|
#include "dpd.h" |
50 |
|
|
#include "exchange.h" |
51 |
|
|
#include "isakmp.h" |
52 |
|
|
#include "log.h" |
53 |
|
|
#include "message.h" |
54 |
|
|
#include "monitor.h" |
55 |
|
|
#include "sa.h" |
56 |
|
|
#include "timer.h" |
57 |
|
|
#include "transport.h" |
58 |
|
|
#include "util.h" |
59 |
|
|
#include "cert.h" |
60 |
|
|
#include "policy.h" |
61 |
|
|
#include "key.h" |
62 |
|
|
#include "ipsec.h" |
63 |
|
|
#include "ipsec_num.h" |
64 |
|
|
|
65 |
|
|
/* Initial number of bits from the cookies used as hash. */ |
66 |
|
|
#define INITIAL_BUCKET_BITS 6 |
67 |
|
|
|
68 |
|
|
/* |
69 |
|
|
* Don't try to use more bits than this as a hash. |
70 |
|
|
* We only XOR 16 bits so going above that means changing the code below |
71 |
|
|
* too. |
72 |
|
|
*/ |
73 |
|
|
#define MAX_BUCKET_BITS 16 |
74 |
|
|
|
75 |
|
|
#if 0 |
76 |
|
|
static void sa_resize(void); |
77 |
|
|
#endif |
78 |
|
|
static void sa_soft_expire(void *); |
79 |
|
|
static void sa_hard_expire(void *); |
80 |
|
|
|
81 |
|
|
static int _net_addrcmp(struct sockaddr *, struct sockaddr *); |
82 |
|
|
|
83 |
|
|
static LIST_HEAD(sa_list, sa) *sa_tab; |
84 |
|
|
|
85 |
|
|
/* Works both as a maximum index and a mask. */ |
86 |
|
|
static int bucket_mask; |
87 |
|
|
|
88 |
|
|
void |
89 |
|
|
sa_init(void) |
90 |
|
|
{ |
91 |
|
|
int i; |
92 |
|
|
|
93 |
|
|
bucket_mask = (1 << INITIAL_BUCKET_BITS) - 1; |
94 |
|
|
sa_tab = calloc(bucket_mask + 1, sizeof(struct sa_list)); |
95 |
|
|
if (!sa_tab) |
96 |
|
|
log_fatal("sa_init: malloc (%lu) failed", |
97 |
|
|
(bucket_mask + 1) * (unsigned long)sizeof(struct sa_list)); |
98 |
|
|
for (i = 0; i <= bucket_mask; i++) |
99 |
|
|
LIST_INIT(&sa_tab[i]); |
100 |
|
|
} |
101 |
|
|
|
102 |
|
|
#if 0 |
103 |
|
|
/* XXX We don't yet resize. */ |
104 |
|
|
static void |
105 |
|
|
sa_resize(void) |
106 |
|
|
{ |
107 |
|
|
int new_mask = (bucket_mask + 1) * 2 - 1; |
108 |
|
|
int i; |
109 |
|
|
struct sa_list *new_tab; |
110 |
|
|
|
111 |
|
|
new_tab = reallocarray(sa_tab, new_mask + 1, sizeof(struct sa_list)); |
112 |
|
|
if (!new_tab) |
113 |
|
|
return; |
114 |
|
|
sa_tab = new_tab; |
115 |
|
|
for (i = bucket_mask + 1; i <= new_mask; i++) |
116 |
|
|
LIST_INIT(&sa_tab[i]); |
117 |
|
|
bucket_mask = new_mask; |
118 |
|
|
|
119 |
|
|
/* XXX Rehash existing entries. */ |
120 |
|
|
} |
121 |
|
|
#endif |
122 |
|
|
|
123 |
|
|
/* Lookup an SA with the help from a user-supplied checking function. */ |
124 |
|
|
struct sa * |
125 |
|
|
sa_find(int (*check) (struct sa*, void *), void *arg) |
126 |
|
|
{ |
127 |
|
|
int i; |
128 |
|
|
struct sa *sa; |
129 |
|
|
|
130 |
|
|
for (i = 0; i <= bucket_mask; i++) |
131 |
|
|
for (sa = LIST_FIRST(&sa_tab[i]); sa; sa = LIST_NEXT(sa, link)) |
132 |
|
|
if (check(sa, arg)) { |
133 |
|
|
LOG_DBG((LOG_SA, 90, "sa_find: return SA %p", |
134 |
|
|
sa)); |
135 |
|
|
return sa; |
136 |
|
|
} |
137 |
|
|
LOG_DBG((LOG_SA, 90, "sa_find: no SA matched query")); |
138 |
|
|
return 0; |
139 |
|
|
} |
140 |
|
|
|
141 |
|
|
/* Check if SA is an ISAKMP SA with an initiator cookie equal to ICOOKIE. */ |
142 |
|
|
static int |
143 |
|
|
sa_check_icookie(struct sa *sa, void *icookie) |
144 |
|
|
{ |
145 |
|
|
return sa->phase == 1 && |
146 |
|
|
memcmp(sa->cookies, icookie, ISAKMP_HDR_ICOOKIE_LEN) == 0; |
147 |
|
|
} |
148 |
|
|
|
149 |
|
|
/* Lookup an ISAKMP SA out of just the initiator cookie. */ |
150 |
|
|
struct sa * |
151 |
|
|
sa_lookup_from_icookie(u_int8_t *cookie) |
152 |
|
|
{ |
153 |
|
|
return sa_find(sa_check_icookie, cookie); |
154 |
|
|
} |
155 |
|
|
|
156 |
|
|
struct name_phase_arg { |
157 |
|
|
char *name; |
158 |
|
|
u_int8_t phase; |
159 |
|
|
}; |
160 |
|
|
|
161 |
|
|
/* Check if SA has the name and phase given by V_ARG. */ |
162 |
|
|
static int |
163 |
|
|
sa_check_name_phase(struct sa *sa, void *v_arg) |
164 |
|
|
{ |
165 |
|
|
struct name_phase_arg *arg = v_arg; |
166 |
|
|
|
167 |
|
|
return sa->name && strcasecmp(sa->name, arg->name) == 0 && |
168 |
|
|
sa->phase == arg->phase && !(sa->flags & SA_FLAG_REPLACED); |
169 |
|
|
} |
170 |
|
|
|
171 |
|
|
/* Lookup an SA by name, case-independent, and phase. */ |
172 |
|
|
struct sa * |
173 |
|
|
sa_lookup_by_name(char *name, int phase) |
174 |
|
|
{ |
175 |
|
|
struct name_phase_arg arg; |
176 |
|
|
|
177 |
|
|
arg.name = name; |
178 |
|
|
arg.phase = phase; |
179 |
|
|
return sa_find(sa_check_name_phase, &arg); |
180 |
|
|
} |
181 |
|
|
|
182 |
|
|
struct addr_arg { |
183 |
|
|
struct sockaddr *addr; |
184 |
|
|
socklen_t len; |
185 |
|
|
int phase; |
186 |
|
|
int flags; |
187 |
|
|
}; |
188 |
|
|
|
189 |
|
|
/* |
190 |
|
|
* This function has been removed from libc and put here as this |
191 |
|
|
* file is the only user for it. |
192 |
|
|
*/ |
193 |
|
|
static int |
194 |
|
|
_net_addrcmp(struct sockaddr *sa1, struct sockaddr *sa2) |
195 |
|
|
{ |
196 |
|
|
|
197 |
|
|
if (sa1->sa_len != sa2->sa_len) |
198 |
|
|
return (sa1->sa_len < sa2->sa_len) ? -1 : 1; |
199 |
|
|
if (sa1->sa_family != sa2->sa_family) |
200 |
|
|
return (sa1->sa_family < sa2->sa_family) ? -1 : 1; |
201 |
|
|
|
202 |
|
|
switch(sa1->sa_family) { |
203 |
|
|
case AF_INET: |
204 |
|
|
return (memcmp(&((struct sockaddr_in *)sa1)->sin_addr, |
205 |
|
|
&((struct sockaddr_in *)sa2)->sin_addr, |
206 |
|
|
sizeof(struct in_addr))); |
207 |
|
|
case AF_INET6: |
208 |
|
|
if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != |
209 |
|
|
((struct sockaddr_in6 *)sa2)->sin6_scope_id) |
210 |
|
|
return (((struct sockaddr_in6 *)sa1)->sin6_scope_id < |
211 |
|
|
((struct sockaddr_in6 *)sa2)->sin6_scope_id) |
212 |
|
|
? -1 : 1; |
213 |
|
|
return memcmp(&((struct sockaddr_in6 *)sa1)->sin6_addr, |
214 |
|
|
&((struct sockaddr_in6 *)sa2)->sin6_addr, |
215 |
|
|
sizeof(struct in6_addr)); |
216 |
|
|
case AF_LOCAL: |
217 |
|
|
return (strcmp(((struct sockaddr_un *)sa1)->sun_path, |
218 |
|
|
((struct sockaddr_un *)sa2)->sun_path)); |
219 |
|
|
default: |
220 |
|
|
return -1; |
221 |
|
|
} |
222 |
|
|
} |
223 |
|
|
|
224 |
|
|
/* |
225 |
|
|
* Check if SA is ready and has a peer with an address equal the one given |
226 |
|
|
* by V_ADDR. Furthermore if we are searching for a specific phase, check |
227 |
|
|
* that too. |
228 |
|
|
*/ |
229 |
|
|
static int |
230 |
|
|
sa_check_peer(struct sa *sa, void *v_addr) |
231 |
|
|
{ |
232 |
|
|
struct addr_arg *addr = v_addr; |
233 |
|
|
struct sockaddr *dst; |
234 |
|
|
|
235 |
|
|
if (!sa->transport || (sa->flags & SA_FLAG_READY) == 0 || |
236 |
|
|
(addr->phase && addr->phase != sa->phase)) |
237 |
|
|
return 0; |
238 |
|
|
|
239 |
|
|
sa->transport->vtbl->get_dst(sa->transport, &dst); |
240 |
|
|
if (_net_addrcmp(dst, addr->addr) != 0) |
241 |
|
|
return 0; |
242 |
|
|
|
243 |
|
|
/* same family, length and address, check port if inet/inet6 */ |
244 |
|
|
switch (dst->sa_family) { |
245 |
|
|
case AF_INET: |
246 |
|
|
return ((struct sockaddr_in *)dst)->sin_port == ((struct sockaddr_in *)addr->addr)->sin_port; |
247 |
|
|
case AF_INET6: |
248 |
|
|
return ((struct sockaddr_in6 *)dst)->sin6_port == ((struct sockaddr_in6 *)addr->addr)->sin6_port; |
249 |
|
|
} |
250 |
|
|
|
251 |
|
|
return 1; |
252 |
|
|
} |
253 |
|
|
|
254 |
|
|
struct dst_isakmpspi_arg { |
255 |
|
|
struct sockaddr *dst; |
256 |
|
|
u_int8_t *spi; /* must be ISAKMP_SPI_SIZE octets */ |
257 |
|
|
}; |
258 |
|
|
|
259 |
|
|
/* |
260 |
|
|
* Check if SA matches what we are asking for through V_ARG. It has to |
261 |
|
|
* be a finished phase 1 (ISAKMP) SA. |
262 |
|
|
*/ |
263 |
|
|
static int |
264 |
|
|
isakmp_sa_check(struct sa *sa, void *v_arg) |
265 |
|
|
{ |
266 |
|
|
struct dst_isakmpspi_arg *arg = v_arg; |
267 |
|
|
struct sockaddr *dst, *src; |
268 |
|
|
|
269 |
|
|
if (sa->phase != 1 || !(sa->flags & SA_FLAG_READY)) |
270 |
|
|
return 0; |
271 |
|
|
|
272 |
|
|
/* verify address is either src or dst for this sa */ |
273 |
|
|
sa->transport->vtbl->get_dst(sa->transport, &dst); |
274 |
|
|
sa->transport->vtbl->get_src(sa->transport, &src); |
275 |
|
|
if (memcmp(src, arg->dst, SA_LEN(src)) && |
276 |
|
|
memcmp(dst, arg->dst, SA_LEN(dst))) |
277 |
|
|
return 0; |
278 |
|
|
|
279 |
|
|
/* match icookie+rcookie against spi */ |
280 |
|
|
if (memcmp(sa->cookies, arg->spi, ISAKMP_HDR_COOKIES_LEN) == 0) |
281 |
|
|
return 1; |
282 |
|
|
|
283 |
|
|
return 0; |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
/* |
287 |
|
|
* Find an ISAKMP SA with a "name" of DST & SPI. |
288 |
|
|
*/ |
289 |
|
|
struct sa * |
290 |
|
|
sa_lookup_isakmp_sa(struct sockaddr *dst, u_int8_t *spi) |
291 |
|
|
{ |
292 |
|
|
struct dst_isakmpspi_arg arg; |
293 |
|
|
|
294 |
|
|
arg.dst = dst; |
295 |
|
|
arg.spi = spi; |
296 |
|
|
|
297 |
|
|
return sa_find(isakmp_sa_check, &arg); |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
/* Lookup a ready SA by the peer's address. */ |
301 |
|
|
struct sa * |
302 |
|
|
sa_lookup_by_peer(struct sockaddr *dst, socklen_t dstlen, int phase) |
303 |
|
|
{ |
304 |
|
|
struct addr_arg arg; |
305 |
|
|
|
306 |
|
|
arg.addr = dst; |
307 |
|
|
arg.len = dstlen; |
308 |
|
|
arg.phase = phase; |
309 |
|
|
|
310 |
|
|
return sa_find(sa_check_peer, &arg); |
311 |
|
|
} |
312 |
|
|
|
313 |
|
|
/* Lookup a ready ISAKMP SA given its peer address. */ |
314 |
|
|
struct sa * |
315 |
|
|
sa_isakmp_lookup_by_peer(struct sockaddr *dst, socklen_t dstlen) |
316 |
|
|
{ |
317 |
|
|
struct addr_arg arg; |
318 |
|
|
|
319 |
|
|
arg.addr = dst; |
320 |
|
|
arg.len = dstlen; |
321 |
|
|
arg.phase = 1; |
322 |
|
|
|
323 |
|
|
return sa_find(sa_check_peer, &arg); |
324 |
|
|
} |
325 |
|
|
|
326 |
|
|
int |
327 |
|
|
sa_enter(struct sa *sa) |
328 |
|
|
{ |
329 |
|
|
u_int16_t bucket = 0; |
330 |
|
|
int i; |
331 |
|
|
u_int8_t *cp; |
332 |
|
|
|
333 |
|
|
/* XXX We might resize if we are crossing a certain threshold */ |
334 |
|
|
|
335 |
|
|
for (i = 0; i < ISAKMP_HDR_COOKIES_LEN; i += 2) { |
336 |
|
|
cp = sa->cookies + i; |
337 |
|
|
/* Doing it this way avoids alignment problems. */ |
338 |
|
|
bucket ^= cp[0] | cp[1] << 8; |
339 |
|
|
} |
340 |
|
|
for (i = 0; i < ISAKMP_HDR_MESSAGE_ID_LEN; i += 2) { |
341 |
|
|
cp = sa->message_id + i; |
342 |
|
|
/* Doing it this way avoids alignment problems. */ |
343 |
|
|
bucket ^= cp[0] | cp[1] << 8; |
344 |
|
|
} |
345 |
|
|
bucket &= bucket_mask; |
346 |
|
|
LIST_INSERT_HEAD(&sa_tab[bucket], sa, link); |
347 |
|
|
sa_reference(sa); |
348 |
|
|
LOG_DBG((LOG_SA, 70, "sa_enter: SA %p added to SA list", sa)); |
349 |
|
|
return 1; |
350 |
|
|
} |
351 |
|
|
|
352 |
|
|
/* |
353 |
|
|
* Lookup the SA given by the header fields MSG. PHASE2 is false when |
354 |
|
|
* looking for phase 1 SAa and true otherwise. |
355 |
|
|
*/ |
356 |
|
|
struct sa * |
357 |
|
|
sa_lookup_by_header(u_int8_t *msg, int phase2) |
358 |
|
|
{ |
359 |
|
|
return sa_lookup(msg + ISAKMP_HDR_COOKIES_OFF, |
360 |
|
|
phase2 ? msg + ISAKMP_HDR_MESSAGE_ID_OFF : 0); |
361 |
|
|
} |
362 |
|
|
|
363 |
|
|
/* |
364 |
|
|
* Lookup the SA given by the COOKIES and possibly the MESSAGE_ID unless |
365 |
|
|
* a null pointer, meaning we are looking for phase 1 SAs. |
366 |
|
|
*/ |
367 |
|
|
struct sa * |
368 |
|
|
sa_lookup(u_int8_t *cookies, u_int8_t *message_id) |
369 |
|
|
{ |
370 |
|
|
u_int16_t bucket = 0; |
371 |
|
|
int i; |
372 |
|
|
struct sa *sa; |
373 |
|
|
u_int8_t *cp; |
374 |
|
|
|
375 |
|
|
/* |
376 |
|
|
* We use the cookies to get bits to use as an index into sa_tab, as at |
377 |
|
|
* least one (our cookie) is a good hash, xoring all the bits, 16 at a |
378 |
|
|
* time, and then masking, should do. Doing it this way means we can |
379 |
|
|
* validate cookies very fast thus delimiting the effects of "Denial of |
380 |
|
|
* service"-attacks using packet flooding. |
381 |
|
|
*/ |
382 |
|
|
for (i = 0; i < ISAKMP_HDR_COOKIES_LEN; i += 2) { |
383 |
|
|
cp = cookies + i; |
384 |
|
|
/* Doing it this way avoids alignment problems. */ |
385 |
|
|
bucket ^= cp[0] | cp[1] << 8; |
386 |
|
|
} |
387 |
|
|
if (message_id) |
388 |
|
|
for (i = 0; i < ISAKMP_HDR_MESSAGE_ID_LEN; i += 2) { |
389 |
|
|
cp = message_id + i; |
390 |
|
|
/* Doing it this way avoids alignment problems. */ |
391 |
|
|
bucket ^= cp[0] | cp[1] << 8; |
392 |
|
|
} |
393 |
|
|
bucket &= bucket_mask; |
394 |
|
|
for (sa = LIST_FIRST(&sa_tab[bucket]); |
395 |
|
|
sa && (memcmp(cookies, sa->cookies, ISAKMP_HDR_COOKIES_LEN) != 0 || |
396 |
|
|
(message_id && memcmp(message_id, sa->message_id, |
397 |
|
|
ISAKMP_HDR_MESSAGE_ID_LEN) != 0) || |
398 |
|
|
(!message_id && !zero_test(sa->message_id, ISAKMP_HDR_MESSAGE_ID_LEN))); |
399 |
|
|
sa = LIST_NEXT(sa, link)) |
400 |
|
|
; |
401 |
|
|
|
402 |
|
|
return sa; |
403 |
|
|
} |
404 |
|
|
|
405 |
|
|
/* Create an SA. */ |
406 |
|
|
int |
407 |
|
|
sa_create(struct exchange *exchange, struct transport *t) |
408 |
|
|
{ |
409 |
|
|
struct sa *sa; |
410 |
|
|
|
411 |
|
|
/* |
412 |
|
|
* We want the SA zeroed for sa_free to be able to find out what fields |
413 |
|
|
* have been filled-in. |
414 |
|
|
*/ |
415 |
|
|
sa = calloc(1, sizeof *sa); |
416 |
|
|
if (!sa) { |
417 |
|
|
log_error("sa_create: calloc (1, %lu) failed", |
418 |
|
|
(unsigned long)sizeof *sa); |
419 |
|
|
return -1; |
420 |
|
|
} |
421 |
|
|
sa->transport = t; |
422 |
|
|
if (t) |
423 |
|
|
transport_reference(t); |
424 |
|
|
sa->phase = exchange->phase; |
425 |
|
|
memcpy(sa->cookies, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); |
426 |
|
|
memcpy(sa->message_id, exchange->message_id, |
427 |
|
|
ISAKMP_HDR_MESSAGE_ID_LEN); |
428 |
|
|
sa->doi = exchange->doi; |
429 |
|
|
sa->policy_id = -1; |
430 |
|
|
|
431 |
|
|
if (sa->doi->sa_size) { |
432 |
|
|
/* |
433 |
|
|
* Allocate the DOI-specific structure and initialize it to |
434 |
|
|
* zeroes. |
435 |
|
|
*/ |
436 |
|
|
sa->data = calloc(1, sa->doi->sa_size); |
437 |
|
|
if (!sa->data) { |
438 |
|
|
log_error("sa_create: calloc (1, %lu) failed", |
439 |
|
|
(unsigned long)sa->doi->sa_size); |
440 |
|
|
free(sa); |
441 |
|
|
return -1; |
442 |
|
|
} |
443 |
|
|
} |
444 |
|
|
TAILQ_INIT(&sa->protos); |
445 |
|
|
|
446 |
|
|
sa_enter(sa); |
447 |
|
|
TAILQ_INSERT_TAIL(&exchange->sa_list, sa, next); |
448 |
|
|
sa_reference(sa); |
449 |
|
|
|
450 |
|
|
LOG_DBG((LOG_SA, 60, |
451 |
|
|
"sa_create: sa %p phase %d added to exchange %p (%s)", sa, |
452 |
|
|
sa->phase, exchange, |
453 |
|
|
exchange->name ? exchange->name : "<unnamed>")); |
454 |
|
|
return 0; |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
/* |
458 |
|
|
* Dump the internal state of SA to the report channel, with HEADER |
459 |
|
|
* prepended to each line. |
460 |
|
|
*/ |
461 |
|
|
void |
462 |
|
|
sa_dump(int cls, int level, char *header, struct sa *sa) |
463 |
|
|
{ |
464 |
|
|
struct proto *proto; |
465 |
|
|
char spi_header[80]; |
466 |
|
|
int i; |
467 |
|
|
|
468 |
|
|
LOG_DBG((cls, level, "%s: %p %s phase %d doi %d flags 0x%x", header, |
469 |
|
|
sa, sa->name ? sa->name : "<unnamed>", sa->phase, sa->doi->id, |
470 |
|
|
sa->flags)); |
471 |
|
|
LOG_DBG((cls, level, "%s: icookie %08x%08x rcookie %08x%08x", header, |
472 |
|
|
decode_32(sa->cookies), decode_32(sa->cookies + 4), |
473 |
|
|
decode_32(sa->cookies + 8), decode_32(sa->cookies + 12))); |
474 |
|
|
LOG_DBG((cls, level, "%s: msgid %08x refcnt %d", header, |
475 |
|
|
decode_32(sa->message_id), sa->refcnt)); |
476 |
|
|
LOG_DBG((cls, level, "%s: life secs %llu kb %llu", header, sa->seconds, |
477 |
|
|
sa->kilobytes)); |
478 |
|
|
for (proto = TAILQ_FIRST(&sa->protos); proto; |
479 |
|
|
proto = TAILQ_NEXT(proto, link)) { |
480 |
|
|
LOG_DBG((cls, level, "%s: suite %d proto %d", header, |
481 |
|
|
proto->no, proto->proto)); |
482 |
|
|
LOG_DBG((cls, level, |
483 |
|
|
"%s: spi_sz[0] %d spi[0] %p spi_sz[1] %d spi[1] %p", |
484 |
|
|
header, proto->spi_sz[0], proto->spi[0], proto->spi_sz[1], |
485 |
|
|
proto->spi[1])); |
486 |
|
|
LOG_DBG((cls, level, "%s: %s, %s", header, |
487 |
|
|
!sa->doi ? "<nodoi>" : |
488 |
|
|
sa->doi->decode_ids("initiator id: %s, responder id: %s", |
489 |
|
|
sa->id_i, sa->id_i_len, |
490 |
|
|
sa->id_r, sa->id_r_len, 0), |
491 |
|
|
!sa->transport ? "<no transport>" : |
492 |
|
|
sa->transport->vtbl->decode_ids(sa->transport))); |
493 |
|
|
for (i = 0; i < 2; i++) |
494 |
|
|
if (proto->spi[i]) { |
495 |
|
|
snprintf(spi_header, sizeof spi_header, |
496 |
|
|
"%s: spi[%d]", header, i); |
497 |
|
|
LOG_DBG_BUF((cls, level, spi_header, |
498 |
|
|
proto->spi[i], proto->spi_sz[i])); |
499 |
|
|
} |
500 |
|
|
} |
501 |
|
|
} |
502 |
|
|
|
503 |
|
|
/* |
504 |
|
|
* Display the SA's two SPI values. |
505 |
|
|
*/ |
506 |
|
|
static void |
507 |
|
|
report_spi(FILE *fd, const u_int8_t *buf, size_t sz, int spi) |
508 |
|
|
{ |
509 |
|
|
#define SBUFSZ (2 * 32 + 9) |
510 |
|
|
char s[SBUFSZ]; |
511 |
|
|
size_t i, j; |
512 |
|
|
|
513 |
|
|
for (i = j = 0; i < sz;) { |
514 |
|
|
snprintf(s + j, sizeof s - j, "%02x", buf[i++]); |
515 |
|
|
j += strlen(s + j); |
516 |
|
|
if (i % 4 == 0) { |
517 |
|
|
if (i % 32 == 0) { |
518 |
|
|
s[j] = '\0'; |
519 |
|
|
fprintf(fd, "%s", s); |
520 |
|
|
j = 0; |
521 |
|
|
} else |
522 |
|
|
s[j++] = ' '; |
523 |
|
|
} |
524 |
|
|
} |
525 |
|
|
|
526 |
|
|
if (j) { |
527 |
|
|
s[j] = '\0'; |
528 |
|
|
fprintf(fd, "SPI %d: %s\n", spi, s); |
529 |
|
|
} |
530 |
|
|
} |
531 |
|
|
|
532 |
|
|
/* |
533 |
|
|
* Display the transform names to file. |
534 |
|
|
* Structure is taken from pf_key_v2.c, pf_key_v2_set_spi. |
535 |
|
|
* Transform names are taken from /usr/src/sys/crypto/xform.c. |
536 |
|
|
*/ |
537 |
|
|
static void |
538 |
|
|
report_proto(FILE *fd, struct proto *proto) |
539 |
|
|
{ |
540 |
|
|
struct ipsec_proto *iproto; |
541 |
|
|
int keylen, hashlen; |
542 |
|
|
|
543 |
|
|
switch (proto->proto) { |
544 |
|
|
case IPSEC_PROTO_IPSEC_ESP: |
545 |
|
|
keylen = ipsec_esp_enckeylength(proto); |
546 |
|
|
hashlen = ipsec_esp_authkeylength(proto); |
547 |
|
|
fprintf(fd, "Transform: IPsec ESP\n"); |
548 |
|
|
fprintf(fd, "Encryption key length: %d\n", keylen); |
549 |
|
|
fprintf(fd, "Authentication key length: %d\n", hashlen); |
550 |
|
|
|
551 |
|
|
fprintf(fd, "Encryption algorithm: "); |
552 |
|
|
switch (proto->id) { |
553 |
|
|
case IPSEC_ESP_3DES: |
554 |
|
|
fprintf(fd, "3DES\n"); |
555 |
|
|
break; |
556 |
|
|
|
557 |
|
|
case IPSEC_ESP_AES: |
558 |
|
|
fprintf(fd, "AES (CBC)\n"); |
559 |
|
|
break; |
560 |
|
|
|
561 |
|
|
case IPSEC_ESP_AES_CTR: |
562 |
|
|
fprintf(fd, "AES (CTR)\n"); |
563 |
|
|
break; |
564 |
|
|
|
565 |
|
|
case IPSEC_ESP_AES_GCM_16: |
566 |
|
|
fprintf(fd, "AES (GCM)\n"); |
567 |
|
|
break; |
568 |
|
|
|
569 |
|
|
case IPSEC_ESP_AES_GMAC: |
570 |
|
|
fprintf(fd, "AES (GMAC)\n"); |
571 |
|
|
break; |
572 |
|
|
|
573 |
|
|
case IPSEC_ESP_CAST: |
574 |
|
|
fprintf(fd, "Cast-128\n"); |
575 |
|
|
break; |
576 |
|
|
|
577 |
|
|
case IPSEC_ESP_BLOWFISH: |
578 |
|
|
fprintf(fd, "Blowfish\n"); |
579 |
|
|
break; |
580 |
|
|
|
581 |
|
|
default: |
582 |
|
|
fprintf(fd, "unknown (%d)\n", proto->id); |
583 |
|
|
} |
584 |
|
|
|
585 |
|
|
fprintf(fd, "Authentication algorithm: "); |
586 |
|
|
|
587 |
|
|
if (!proto->data) { |
588 |
|
|
fprintf(fd, "none\n"); |
589 |
|
|
break; |
590 |
|
|
} |
591 |
|
|
iproto = proto->data; |
592 |
|
|
|
593 |
|
|
switch (iproto->auth) { |
594 |
|
|
case IPSEC_AUTH_HMAC_MD5: |
595 |
|
|
fprintf(fd, "HMAC-MD5\n"); |
596 |
|
|
break; |
597 |
|
|
|
598 |
|
|
case IPSEC_AUTH_HMAC_SHA: |
599 |
|
|
fprintf(fd, "HMAC-SHA1\n"); |
600 |
|
|
break; |
601 |
|
|
|
602 |
|
|
case IPSEC_AUTH_HMAC_RIPEMD: |
603 |
|
|
fprintf(fd, "HMAC-RIPEMD-160\n"); |
604 |
|
|
break; |
605 |
|
|
|
606 |
|
|
case IPSEC_AUTH_HMAC_SHA2_256: |
607 |
|
|
fprintf(fd, "HMAC-SHA2-256\n"); |
608 |
|
|
break; |
609 |
|
|
|
610 |
|
|
case IPSEC_AUTH_HMAC_SHA2_384: |
611 |
|
|
fprintf(fd, "HMAC-SHA2-384\n"); |
612 |
|
|
break; |
613 |
|
|
|
614 |
|
|
case IPSEC_AUTH_HMAC_SHA2_512: |
615 |
|
|
fprintf(fd, "HMAC-SHA2-512\n"); |
616 |
|
|
break; |
617 |
|
|
|
618 |
|
|
case IPSEC_AUTH_DES_MAC: |
619 |
|
|
case IPSEC_AUTH_KPDK: |
620 |
|
|
/* XXX We should be supporting KPDK */ |
621 |
|
|
fprintf(fd, "unknown (%d)", iproto->auth); |
622 |
|
|
break; |
623 |
|
|
|
624 |
|
|
default: |
625 |
|
|
fprintf(fd, "none\n"); |
626 |
|
|
} |
627 |
|
|
break; |
628 |
|
|
|
629 |
|
|
case IPSEC_PROTO_IPSEC_AH: |
630 |
|
|
hashlen = ipsec_ah_keylength(proto); |
631 |
|
|
fprintf(fd, "Transform: IPsec AH\n"); |
632 |
|
|
fprintf(fd, "Encryption not used.\n"); |
633 |
|
|
fprintf(fd, "Authentication key length: %d\n", hashlen); |
634 |
|
|
|
635 |
|
|
fprintf(fd, "Authentication algorithm: "); |
636 |
|
|
switch (proto->id) { |
637 |
|
|
case IPSEC_AH_MD5: |
638 |
|
|
fprintf(fd, "HMAC-MD5\n"); |
639 |
|
|
break; |
640 |
|
|
|
641 |
|
|
case IPSEC_AH_SHA: |
642 |
|
|
fprintf(fd, "HMAC-SHA1\n"); |
643 |
|
|
break; |
644 |
|
|
|
645 |
|
|
case IPSEC_AH_RIPEMD: |
646 |
|
|
fprintf(fd, "HMAC-RIPEMD-160\n"); |
647 |
|
|
break; |
648 |
|
|
|
649 |
|
|
case IPSEC_AH_SHA2_256: |
650 |
|
|
fprintf(fd, "HMAC-SHA2-256\n"); |
651 |
|
|
break; |
652 |
|
|
|
653 |
|
|
case IPSEC_AH_SHA2_384: |
654 |
|
|
fprintf(fd, "HMAC-SHA2-384\n"); |
655 |
|
|
break; |
656 |
|
|
|
657 |
|
|
case IPSEC_AH_SHA2_512: |
658 |
|
|
fprintf(fd, "HMAC-SHA2-512\n"); |
659 |
|
|
break; |
660 |
|
|
|
661 |
|
|
default: |
662 |
|
|
fprintf(fd, "unknown (%d)", proto->id); |
663 |
|
|
} |
664 |
|
|
break; |
665 |
|
|
|
666 |
|
|
default: |
667 |
|
|
fprintf(fd, "report_proto: invalid proto %d\n", proto->proto); |
668 |
|
|
} |
669 |
|
|
} |
670 |
|
|
|
671 |
|
|
/* |
672 |
|
|
* Display SA lifetimes. |
673 |
|
|
*/ |
674 |
|
|
static void |
675 |
|
|
report_lifetimes(FILE *fd, struct sa *sa) |
676 |
|
|
{ |
677 |
|
|
long timeout; |
678 |
|
|
|
679 |
|
|
if (sa->seconds) |
680 |
|
|
fprintf(fd, "Lifetime: %llu seconds\n", sa->seconds); |
681 |
|
|
|
682 |
|
|
if (sa->soft_death) { |
683 |
|
|
timeout = get_timeout(&sa->soft_death->expiration); |
684 |
|
|
if (timeout < 0) |
685 |
|
|
fprintf(fd, "<no soft timeout>\n"); |
686 |
|
|
else |
687 |
|
|
fprintf(fd, "Soft timeout in %ld seconds\n", timeout); |
688 |
|
|
} |
689 |
|
|
|
690 |
|
|
if (sa->death) { |
691 |
|
|
timeout = get_timeout(&sa->death->expiration); |
692 |
|
|
if (timeout < 0) |
693 |
|
|
fprintf(fd, "No hard timeout>\n"); |
694 |
|
|
else |
695 |
|
|
fprintf(fd, "Hard timeout in %ld seconds\n", timeout); |
696 |
|
|
} |
697 |
|
|
|
698 |
|
|
if (sa->kilobytes) |
699 |
|
|
fprintf(fd, "Lifetime: %llu kilobytes\n", sa->kilobytes); |
700 |
|
|
} |
701 |
|
|
|
702 |
|
|
/* |
703 |
|
|
* Print phase 1 specific information. |
704 |
|
|
*/ |
705 |
|
|
static void |
706 |
|
|
report_phase1(FILE *fd, struct sa *sa) |
707 |
|
|
{ |
708 |
|
|
/* Cookies. */ |
709 |
|
|
fprintf(fd, "icookie %08x%08x rcookie %08x%08x\n", |
710 |
|
|
decode_32(sa->cookies), decode_32(sa->cookies + 4), |
711 |
|
|
decode_32(sa->cookies + 8), decode_32(sa->cookies + 12)); |
712 |
|
|
} |
713 |
|
|
|
714 |
|
|
/* |
715 |
|
|
* Print phase 2 specific information. |
716 |
|
|
*/ |
717 |
|
|
static void |
718 |
|
|
report_phase2(FILE *fd, struct sa *sa) |
719 |
|
|
{ |
720 |
|
|
struct proto *proto; |
721 |
|
|
int i; |
722 |
|
|
|
723 |
|
|
/* Transform information. */ |
724 |
|
|
for (proto = TAILQ_FIRST(&sa->protos); proto; |
725 |
|
|
proto = TAILQ_NEXT(proto, link)) { |
726 |
|
|
|
727 |
|
|
/* SPI values. */ |
728 |
|
|
for (i = 0; i < 2; i++) |
729 |
|
|
if (proto->spi[i]) |
730 |
|
|
report_spi(fd, proto->spi[i], |
731 |
|
|
proto->spi_sz[i], i); |
732 |
|
|
else |
733 |
|
|
fprintf(fd, "SPI %d not defined.\n", i); |
734 |
|
|
|
735 |
|
|
/* Proto values. */ |
736 |
|
|
report_proto(fd, proto); |
737 |
|
|
} |
738 |
|
|
} |
739 |
|
|
|
740 |
|
|
/* Report all the SAs to the report channel. */ |
741 |
|
|
void |
742 |
|
|
sa_report(void) |
743 |
|
|
{ |
744 |
|
|
struct sa *sa; |
745 |
|
|
int i; |
746 |
|
|
|
747 |
|
|
for (i = 0; i <= bucket_mask; i++) |
748 |
|
|
for (sa = LIST_FIRST(&sa_tab[i]); sa; sa = LIST_NEXT(sa, link)) |
749 |
|
|
sa_dump(LOG_REPORT, 0, "sa_report", sa); |
750 |
|
|
} |
751 |
|
|
|
752 |
|
|
/* |
753 |
|
|
* Print an SA's connection details to file SA_FILE. |
754 |
|
|
*/ |
755 |
|
|
static void |
756 |
|
|
sa_dump_all(FILE *fd, struct sa *sa) |
757 |
|
|
{ |
758 |
|
|
/* SA name and phase. */ |
759 |
|
|
fprintf(fd, "SA name: %s", sa->name ? sa->name : "<unnamed>"); |
760 |
|
|
fprintf(fd, " (Phase %d%s)\n", sa->phase, sa->phase == 1 ? |
761 |
|
|
(sa->initiator ? "/Initiator" : "/Responder") : ""); |
762 |
|
|
|
763 |
|
|
/* Source and destination IPs. */ |
764 |
|
|
fprintf(fd, "%s", sa->transport == NULL ? "<no transport>" : |
765 |
|
|
sa->transport->vtbl->decode_ids(sa->transport)); |
766 |
|
|
fprintf(fd, "\n"); |
767 |
|
|
|
768 |
|
|
/* Lifetimes */ |
769 |
|
|
report_lifetimes(fd, sa); |
770 |
|
|
|
771 |
|
|
fprintf(fd, "Flags 0x%08x\n", sa->flags); |
772 |
|
|
|
773 |
|
|
if (sa->phase == 1) |
774 |
|
|
report_phase1(fd, sa); |
775 |
|
|
else if (sa->phase == 2) |
776 |
|
|
report_phase2(fd, sa); |
777 |
|
|
else { |
778 |
|
|
/* Should not happen, but... */ |
779 |
|
|
fprintf(fd, "<unknown phase>\n"); |
780 |
|
|
} |
781 |
|
|
|
782 |
|
|
/* SA separator. */ |
783 |
|
|
fprintf(fd, "\n"); |
784 |
|
|
} |
785 |
|
|
|
786 |
|
|
/* Report info of all SAs to file 'fd'. */ |
787 |
|
|
void |
788 |
|
|
sa_report_all(FILE *fd) |
789 |
|
|
{ |
790 |
|
|
struct sa *sa; |
791 |
|
|
int i; |
792 |
|
|
|
793 |
|
|
for (i = 0; i <= bucket_mask; i++) |
794 |
|
|
for (sa = LIST_FIRST(&sa_tab[i]); sa; sa = LIST_NEXT(sa, link)) |
795 |
|
|
sa_dump_all(fd, sa); |
796 |
|
|
} |
797 |
|
|
|
798 |
|
|
/* Free the protocol structure pointed to by PROTO. */ |
799 |
|
|
void |
800 |
|
|
proto_free(struct proto *proto) |
801 |
|
|
{ |
802 |
|
|
struct proto_attr *pa; |
803 |
|
|
struct sa *sa = proto->sa; |
804 |
|
|
int i; |
805 |
|
|
|
806 |
|
|
for (i = 0; i < 2; i++) |
807 |
|
|
if (proto->spi[i]) { |
808 |
|
|
if (sa->doi->delete_spi) |
809 |
|
|
sa->doi->delete_spi(sa, proto, i); |
810 |
|
|
free(proto->spi[i]); |
811 |
|
|
} |
812 |
|
|
TAILQ_REMOVE(&sa->protos, proto, link); |
813 |
|
|
if (proto->data) { |
814 |
|
|
if (sa->doi && sa->doi->free_proto_data) |
815 |
|
|
sa->doi->free_proto_data(proto->data); |
816 |
|
|
free(proto->data); |
817 |
|
|
} |
818 |
|
|
if (proto->xf_cnt) |
819 |
|
|
while ((pa = TAILQ_FIRST(&proto->xfs)) != NULL) { |
820 |
|
|
free(pa->attrs); |
821 |
|
|
TAILQ_REMOVE(&proto->xfs, pa, next); |
822 |
|
|
free(pa); |
823 |
|
|
} |
824 |
|
|
|
825 |
|
|
LOG_DBG((LOG_SA, 90, "proto_free: freeing %p", proto)); |
826 |
|
|
free(proto); |
827 |
|
|
} |
828 |
|
|
|
829 |
|
|
/* Release all resources this SA is using. */ |
830 |
|
|
void |
831 |
|
|
sa_free(struct sa *sa) |
832 |
|
|
{ |
833 |
|
|
if (sa->death) { |
834 |
|
|
timer_remove_event(sa->death); |
835 |
|
|
sa->death = 0; |
836 |
|
|
sa->refcnt--; |
837 |
|
|
} |
838 |
|
|
if (sa->soft_death) { |
839 |
|
|
timer_remove_event(sa->soft_death); |
840 |
|
|
sa->soft_death = 0; |
841 |
|
|
sa->refcnt--; |
842 |
|
|
} |
843 |
|
|
if (sa->dpd_event) { |
844 |
|
|
timer_remove_event(sa->dpd_event); |
845 |
|
|
sa->dpd_event = 0; |
846 |
|
|
} |
847 |
|
|
sa_remove(sa); |
848 |
|
|
} |
849 |
|
|
|
850 |
|
|
/* Remove the SA from the hash table of live SAs. */ |
851 |
|
|
void |
852 |
|
|
sa_remove(struct sa *sa) |
853 |
|
|
{ |
854 |
|
|
LIST_REMOVE(sa, link); |
855 |
|
|
LOG_DBG((LOG_SA, 70, "sa_remove: SA %p removed from SA list", sa)); |
856 |
|
|
sa_release(sa); |
857 |
|
|
} |
858 |
|
|
|
859 |
|
|
/* Raise the reference count of SA. */ |
860 |
|
|
void |
861 |
|
|
sa_reference(struct sa *sa) |
862 |
|
|
{ |
863 |
|
|
sa->refcnt++; |
864 |
|
|
LOG_DBG((LOG_SA, 80, "sa_reference: SA %p now has %d references", |
865 |
|
|
sa, sa->refcnt)); |
866 |
|
|
} |
867 |
|
|
|
868 |
|
|
/* Release a reference to SA. */ |
869 |
|
|
void |
870 |
|
|
sa_release(struct sa *sa) |
871 |
|
|
{ |
872 |
|
|
struct cert_handler *handler; |
873 |
|
|
struct proto *proto; |
874 |
|
|
|
875 |
|
|
LOG_DBG((LOG_SA, 80, "sa_release: SA %p had %d references", |
876 |
|
|
sa, sa->refcnt)); |
877 |
|
|
|
878 |
|
|
if (--sa->refcnt) |
879 |
|
|
return; |
880 |
|
|
|
881 |
|
|
LOG_DBG((LOG_SA, 60, "sa_release: freeing SA %p", sa)); |
882 |
|
|
|
883 |
|
|
while ((proto = TAILQ_FIRST(&sa->protos)) != 0) |
884 |
|
|
proto_free(proto); |
885 |
|
|
if (sa->data) { |
886 |
|
|
if (sa->doi && sa->doi->free_sa_data) |
887 |
|
|
sa->doi->free_sa_data(sa->data); |
888 |
|
|
free(sa->data); |
889 |
|
|
} |
890 |
|
|
free(sa->id_i); |
891 |
|
|
free(sa->id_r); |
892 |
|
|
if (sa->recv_cert) { |
893 |
|
|
handler = cert_get(sa->recv_certtype); |
894 |
|
|
if (handler) |
895 |
|
|
handler->cert_free(sa->recv_cert); |
896 |
|
|
} |
897 |
|
|
if (sa->sent_cert) { |
898 |
|
|
handler = cert_get(sa->sent_certtype); |
899 |
|
|
if (handler) |
900 |
|
|
handler->cert_free(sa->sent_cert); |
901 |
|
|
} |
902 |
|
|
if (sa->recv_key) |
903 |
|
|
key_free(sa->recv_keytype, ISAKMP_KEYTYPE_PUBLIC, |
904 |
|
|
sa->recv_key); |
905 |
|
|
free(sa->keynote_key); /* This is just a string */ |
906 |
|
|
if (sa->policy_id != -1) |
907 |
|
|
kn_close(sa->policy_id); |
908 |
|
|
free(sa->name); |
909 |
|
|
free(sa->keystate); |
910 |
|
|
if (sa->nat_t_keepalive) |
911 |
|
|
timer_remove_event(sa->nat_t_keepalive); |
912 |
|
|
if (sa->dpd_event) |
913 |
|
|
timer_remove_event(sa->dpd_event); |
914 |
|
|
if (sa->transport) |
915 |
|
|
transport_release(sa->transport); |
916 |
|
|
free(sa->tag); |
917 |
|
|
free(sa); |
918 |
|
|
} |
919 |
|
|
|
920 |
|
|
/* |
921 |
|
|
* Rehash the ISAKMP SA this MSG is negotiating with the responder cookie |
922 |
|
|
* filled in. |
923 |
|
|
*/ |
924 |
|
|
void |
925 |
|
|
sa_isakmp_upgrade(struct message *msg) |
926 |
|
|
{ |
927 |
|
|
struct sa *sa = TAILQ_FIRST(&msg->exchange->sa_list); |
928 |
|
|
|
929 |
|
|
sa_remove(sa); |
930 |
|
|
GET_ISAKMP_HDR_RCOOKIE(msg->iov[0].iov_base, |
931 |
|
|
sa->cookies + ISAKMP_HDR_ICOOKIE_LEN); |
932 |
|
|
|
933 |
|
|
/* |
934 |
|
|
* We don't install a transport in the initiator case as we don't know |
935 |
|
|
* what local address will be chosen. Do it now instead. |
936 |
|
|
*/ |
937 |
|
|
sa->transport = msg->transport; |
938 |
|
|
transport_reference(sa->transport); |
939 |
|
|
sa_enter(sa); |
940 |
|
|
} |
941 |
|
|
|
942 |
|
|
#define ATTRS_SIZE (IKE_ATTR_BLOCK_SIZE + 1) /* XXX Should be dynamic. */ |
943 |
|
|
|
944 |
|
|
struct attr_validation_state { |
945 |
|
|
u_int8_t *attrp[ATTRS_SIZE]; |
946 |
|
|
u_int8_t checked[ATTRS_SIZE]; |
947 |
|
|
u_int16_t len[ATTRS_SIZE]; |
948 |
|
|
int phase; /* IKE (1) or IPSEC (2) attrs? */ |
949 |
|
|
int mode; /* 0 = 'load', 1 = check */ |
950 |
|
|
}; |
951 |
|
|
|
952 |
|
|
/* Validate an attribute. Return 0 on match. */ |
953 |
|
|
static int |
954 |
|
|
sa_validate_xf_attrs(u_int16_t type, u_int8_t *value, u_int16_t len, |
955 |
|
|
void *arg) |
956 |
|
|
{ |
957 |
|
|
int val0, val1; |
958 |
|
|
|
959 |
|
|
struct attr_validation_state *avs = |
960 |
|
|
(struct attr_validation_state *)arg; |
961 |
|
|
|
962 |
|
|
LOG_DBG((LOG_SA, 95, "sa_validate_xf_attrs: phase %d mode %d type %d " |
963 |
|
|
"len %d", avs->phase, avs->mode, type, len)); |
964 |
|
|
|
965 |
|
|
/* Make sure the phase and type are valid. */ |
966 |
|
|
if (avs->phase == 1) { |
967 |
|
|
if (type < IKE_ATTR_ENCRYPTION_ALGORITHM || |
968 |
|
|
type > IKE_ATTR_BLOCK_SIZE) |
969 |
|
|
return 1; |
970 |
|
|
} else if (avs->phase == 2) { |
971 |
|
|
if (type < IPSEC_ATTR_SA_LIFE_TYPE || |
972 |
|
|
type > IPSEC_ATTR_ECN_TUNNEL) |
973 |
|
|
return 1; |
974 |
|
|
} else |
975 |
|
|
return 1; |
976 |
|
|
|
977 |
|
|
if (avs->mode == 0) { /* Load attrs. */ |
978 |
|
|
avs->attrp[type] = value; |
979 |
|
|
avs->len[type] = len; |
980 |
|
|
return 0; |
981 |
|
|
} |
982 |
|
|
/* Checking for a missing attribute is an immediate failure. */ |
983 |
|
|
if (!avs->attrp[type]) |
984 |
|
|
return 1; |
985 |
|
|
|
986 |
|
|
/* Match the loaded attribute against this one, mark it as checked. */ |
987 |
|
|
avs->checked[type]++; |
988 |
|
|
switch (len) { |
989 |
|
|
case 2: |
990 |
|
|
val0 = (int)decode_16(value); |
991 |
|
|
break; |
992 |
|
|
case 4: |
993 |
|
|
val0 = (int)decode_32(value); |
994 |
|
|
break; |
995 |
|
|
default: |
996 |
|
|
return 1; |
997 |
|
|
} |
998 |
|
|
switch (avs->len[type]) { |
999 |
|
|
case 2: |
1000 |
|
|
val1 = (int)decode_16(avs->attrp[type]); |
1001 |
|
|
break; |
1002 |
|
|
case 4: |
1003 |
|
|
val1 = (int)decode_32(avs->attrp[type]); |
1004 |
|
|
break; |
1005 |
|
|
default: |
1006 |
|
|
return 1; |
1007 |
|
|
} |
1008 |
|
|
/* Return 0 when the values are equal. */ |
1009 |
|
|
return (val0 != val1); |
1010 |
|
|
} |
1011 |
|
|
|
1012 |
|
|
/* |
1013 |
|
|
* This function is used to validate the returned proposal (protection suite) |
1014 |
|
|
* we get from the responder against a proposal we sent. Only run as initiator. |
1015 |
|
|
* We return 0 if a match is found (in any transform of this proposal), 1 |
1016 |
|
|
* otherwise. Also see note in sa_add_transform() below. |
1017 |
|
|
*/ |
1018 |
|
|
static int |
1019 |
|
|
sa_validate_proto_xf(struct proto *match, struct payload *xf, int phase) |
1020 |
|
|
{ |
1021 |
|
|
struct attr_validation_state *avs; |
1022 |
|
|
struct proto_attr *pa; |
1023 |
|
|
int found = 0; |
1024 |
|
|
size_t i; |
1025 |
|
|
u_int8_t xf_id; |
1026 |
|
|
|
1027 |
|
|
if (!match->xf_cnt) |
1028 |
|
|
return 0; |
1029 |
|
|
|
1030 |
|
|
if (match->proto != GET_ISAKMP_PROP_PROTO(xf->context->p)) { |
1031 |
|
|
LOG_DBG((LOG_SA, 70, "sa_validate_proto_xf: proto %p (#%d) " |
1032 |
|
|
"protocol mismatch", match, match->no)); |
1033 |
|
|
return 1; |
1034 |
|
|
} |
1035 |
|
|
avs = calloc(1, sizeof *avs); |
1036 |
|
|
if (!avs) { |
1037 |
|
|
log_error("sa_validate_proto_xf: calloc (1, %lu)", |
1038 |
|
|
(unsigned long)sizeof *avs); |
1039 |
|
|
return 1; |
1040 |
|
|
} |
1041 |
|
|
avs->phase = phase; |
1042 |
|
|
|
1043 |
|
|
/* Load the "proposal candidate" attribute set. */ |
1044 |
|
|
(void)attribute_map(xf->p + ISAKMP_TRANSFORM_SA_ATTRS_OFF, |
1045 |
|
|
GET_ISAKMP_GEN_LENGTH(xf->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF, |
1046 |
|
|
sa_validate_xf_attrs, avs); |
1047 |
|
|
xf_id = GET_ISAKMP_TRANSFORM_ID(xf->p); |
1048 |
|
|
|
1049 |
|
|
/* Check against the transforms we suggested. */ |
1050 |
|
|
avs->mode++; |
1051 |
|
|
for (pa = TAILQ_FIRST(&match->xfs); pa && !found; |
1052 |
|
|
pa = TAILQ_NEXT(pa, next)) { |
1053 |
|
|
if (xf_id != GET_ISAKMP_TRANSFORM_ID(pa->attrs)) |
1054 |
|
|
continue; |
1055 |
|
|
|
1056 |
|
|
bzero(avs->checked, sizeof avs->checked); |
1057 |
|
|
if (attribute_map(pa->attrs + ISAKMP_TRANSFORM_SA_ATTRS_OFF, |
1058 |
|
|
pa->len - ISAKMP_TRANSFORM_SA_ATTRS_OFF, |
1059 |
|
|
sa_validate_xf_attrs, avs) == 0) |
1060 |
|
|
found++; |
1061 |
|
|
|
1062 |
|
|
LOG_DBG((LOG_SA, 80, "sa_validate_proto_xf: attr_map " |
1063 |
|
|
"xf %p proto %p pa %p found %d", xf, match, pa, found)); |
1064 |
|
|
|
1065 |
|
|
if (!found) |
1066 |
|
|
continue; |
1067 |
|
|
|
1068 |
|
|
/* |
1069 |
|
|
* Require all attributes present and checked. XXX perhaps |
1070 |
|
|
* not? |
1071 |
|
|
*/ |
1072 |
|
|
for (i = 0; i < sizeof avs->checked; i++) |
1073 |
|
|
if (avs->attrp[i] && !avs->checked[i]) |
1074 |
|
|
found = 0; |
1075 |
|
|
|
1076 |
|
|
LOG_DBG((LOG_SA, 80, "sa_validate_proto_xf: req_attr " |
1077 |
|
|
"xf %p proto %p pa %p found %d", xf, match, pa, found)); |
1078 |
|
|
} |
1079 |
|
|
free(avs); |
1080 |
|
|
return found ? 0 : 1; |
1081 |
|
|
} |
1082 |
|
|
|
1083 |
|
|
/* |
1084 |
|
|
* Register the chosen transform XF into SA. As a side effect set PROTOP |
1085 |
|
|
* to point at the corresponding proto structure. INITIATOR is true if we |
1086 |
|
|
* are the initiator. |
1087 |
|
|
*/ |
1088 |
|
|
int |
1089 |
|
|
sa_add_transform(struct sa *sa, struct payload *xf, int initiator, |
1090 |
|
|
struct proto **protop) |
1091 |
|
|
{ |
1092 |
|
|
struct proto *proto; |
1093 |
|
|
struct payload *prop = xf->context; |
1094 |
|
|
|
1095 |
|
|
*protop = 0; |
1096 |
|
|
if (!initiator) { |
1097 |
|
|
proto = calloc(1, sizeof *proto); |
1098 |
|
|
if (!proto) |
1099 |
|
|
log_error("sa_add_transform: calloc (1, %lu) failed", |
1100 |
|
|
(unsigned long)sizeof *proto); |
1101 |
|
|
} else { |
1102 |
|
|
/* |
1103 |
|
|
* RFC 2408, section 4.2 states the responder SHOULD use the |
1104 |
|
|
* proposal number from the initiator (i.e us), in it's |
1105 |
|
|
* selected proposal to make this lookup easier. Most vendors |
1106 |
|
|
* follow this. One noted exception is the CiscoPIX (and |
1107 |
|
|
* perhaps other Cisco products). |
1108 |
|
|
* |
1109 |
|
|
* We start by matching on the proposal number, as before. |
1110 |
|
|
*/ |
1111 |
|
|
for (proto = TAILQ_FIRST(&sa->protos); |
1112 |
|
|
proto && proto->no != GET_ISAKMP_PROP_NO(prop->p); |
1113 |
|
|
proto = TAILQ_NEXT(proto, link)) |
1114 |
|
|
; |
1115 |
|
|
/* |
1116 |
|
|
* If we did not find a match, search through all proposals |
1117 |
|
|
* and xforms. |
1118 |
|
|
*/ |
1119 |
|
|
if (!proto || sa_validate_proto_xf(proto, xf, sa->phase) != 0) |
1120 |
|
|
for (proto = TAILQ_FIRST(&sa->protos); |
1121 |
|
|
proto && sa_validate_proto_xf(proto, xf, sa->phase) != 0; |
1122 |
|
|
proto = TAILQ_NEXT(proto, link)) |
1123 |
|
|
; |
1124 |
|
|
} |
1125 |
|
|
if (!proto) |
1126 |
|
|
return -1; |
1127 |
|
|
*protop = proto; |
1128 |
|
|
|
1129 |
|
|
/* Allocate DOI-specific part. */ |
1130 |
|
|
if (!initiator) { |
1131 |
|
|
proto->data = calloc(1, sa->doi->proto_size); |
1132 |
|
|
if (!proto->data) { |
1133 |
|
|
log_error("sa_add_transform: calloc (1, %lu) failed", |
1134 |
|
|
(unsigned long)sa->doi->proto_size); |
1135 |
|
|
goto cleanup; |
1136 |
|
|
} |
1137 |
|
|
} |
1138 |
|
|
proto->no = GET_ISAKMP_PROP_NO(prop->p); |
1139 |
|
|
proto->proto = GET_ISAKMP_PROP_PROTO(prop->p); |
1140 |
|
|
proto->spi_sz[0] = GET_ISAKMP_PROP_SPI_SZ(prop->p); |
1141 |
|
|
if (proto->spi_sz[0]) { |
1142 |
|
|
proto->spi[0] = malloc(proto->spi_sz[0]); |
1143 |
|
|
if (!proto->spi[0]) |
1144 |
|
|
goto cleanup; |
1145 |
|
|
memcpy(proto->spi[0], prop->p + ISAKMP_PROP_SPI_OFF, |
1146 |
|
|
proto->spi_sz[0]); |
1147 |
|
|
} |
1148 |
|
|
proto->chosen = xf; |
1149 |
|
|
proto->sa = sa; |
1150 |
|
|
proto->id = GET_ISAKMP_TRANSFORM_ID(xf->p); |
1151 |
|
|
if (!initiator) |
1152 |
|
|
TAILQ_INSERT_TAIL(&sa->protos, proto, link); |
1153 |
|
|
|
1154 |
|
|
/* Let the DOI get at proto for initializing its own data. */ |
1155 |
|
|
if (sa->doi->proto_init) |
1156 |
|
|
sa->doi->proto_init(proto, 0); |
1157 |
|
|
|
1158 |
|
|
LOG_DBG((LOG_SA, 80, |
1159 |
|
|
"sa_add_transform: " |
1160 |
|
|
"proto %p no %d proto %d chosen %p sa %p id %d", |
1161 |
|
|
proto, proto->no, proto->proto, proto->chosen, proto->sa, |
1162 |
|
|
proto->id)); |
1163 |
|
|
|
1164 |
|
|
return 0; |
1165 |
|
|
|
1166 |
|
|
cleanup: |
1167 |
|
|
if (!initiator) { |
1168 |
|
|
free(proto->data); |
1169 |
|
|
free(proto); |
1170 |
|
|
} |
1171 |
|
|
*protop = 0; |
1172 |
|
|
return -1; |
1173 |
|
|
} |
1174 |
|
|
|
1175 |
|
|
/* Delete an SA. Tell the peer if NOTIFY is set. */ |
1176 |
|
|
void |
1177 |
|
|
sa_delete(struct sa *sa, int notify) |
1178 |
|
|
{ |
1179 |
|
|
if (notify) |
1180 |
|
|
message_send_delete(sa); |
1181 |
|
|
sa_free(sa); |
1182 |
|
|
} |
1183 |
|
|
|
1184 |
|
|
|
1185 |
|
|
/* Teardown all SAs. */ |
1186 |
|
|
void |
1187 |
|
|
sa_teardown_all(void) |
1188 |
|
|
{ |
1189 |
|
|
int i; |
1190 |
|
|
struct sa *sa, *next = 0; |
1191 |
|
|
|
1192 |
|
|
LOG_DBG((LOG_SA, 70, "sa_teardown_all:")); |
1193 |
|
|
/* Get Phase 2 SAs. */ |
1194 |
|
|
for (i = 0; i <= bucket_mask; i++) |
1195 |
|
|
for (sa = LIST_FIRST(&sa_tab[i]); sa; sa = next) { |
1196 |
|
|
next = LIST_NEXT(sa, link); |
1197 |
|
|
if (sa->phase == 2) { |
1198 |
|
|
/* |
1199 |
|
|
* Teardown the phase 2 SAs by name, similar |
1200 |
|
|
* to ui_teardown. |
1201 |
|
|
*/ |
1202 |
|
|
LOG_DBG((LOG_SA, 70, |
1203 |
|
|
"sa_teardown_all: tearing down SA %s", |
1204 |
|
|
sa->name ? sa->name : "<unnamed>")); |
1205 |
|
|
if (sa->name) |
1206 |
|
|
connection_teardown(sa->name); |
1207 |
|
|
sa_delete(sa, 1); |
1208 |
|
|
} |
1209 |
|
|
} |
1210 |
|
|
} |
1211 |
|
|
|
1212 |
|
|
/* |
1213 |
|
|
* This function will get called when we are closing in on the death time of SA |
1214 |
|
|
*/ |
1215 |
|
|
static void |
1216 |
|
|
sa_soft_expire(void *v_sa) |
1217 |
|
|
{ |
1218 |
|
|
struct sa *sa = v_sa; |
1219 |
|
|
|
1220 |
|
|
sa->soft_death = 0; |
1221 |
|
|
sa_release(sa); |
1222 |
|
|
|
1223 |
|
|
if ((sa->flags & (SA_FLAG_STAYALIVE | SA_FLAG_REPLACED)) == |
1224 |
|
|
SA_FLAG_STAYALIVE) |
1225 |
|
|
exchange_establish(sa->name, 0, 0, 1); |
1226 |
|
|
else |
1227 |
|
|
/* |
1228 |
|
|
* Start to watch the use of this SA, so a renegotiation can |
1229 |
|
|
* happen as soon as it is shown to be alive. |
1230 |
|
|
*/ |
1231 |
|
|
sa->flags |= SA_FLAG_FADING; |
1232 |
|
|
} |
1233 |
|
|
|
1234 |
|
|
/* SA has passed its best before date. */ |
1235 |
|
|
static void |
1236 |
|
|
sa_hard_expire(void *v_sa) |
1237 |
|
|
{ |
1238 |
|
|
struct sa *sa = v_sa; |
1239 |
|
|
|
1240 |
|
|
sa->death = 0; |
1241 |
|
|
sa_release(sa); |
1242 |
|
|
|
1243 |
|
|
if ((sa->flags & (SA_FLAG_STAYALIVE | SA_FLAG_REPLACED)) == |
1244 |
|
|
SA_FLAG_STAYALIVE) |
1245 |
|
|
exchange_establish(sa->name, 0, 0, 1); |
1246 |
|
|
|
1247 |
|
|
sa_delete(sa, 1); |
1248 |
|
|
} |
1249 |
|
|
|
1250 |
|
|
void |
1251 |
|
|
sa_reinit(void) |
1252 |
|
|
{ |
1253 |
|
|
struct sa *sa; |
1254 |
|
|
char *tag; |
1255 |
|
|
int i; |
1256 |
|
|
|
1257 |
|
|
/* For now; only do this if we have the proper tag configured. */ |
1258 |
|
|
tag = conf_get_str("General", "Renegotiate-on-HUP"); |
1259 |
|
|
if (!tag) |
1260 |
|
|
return; |
1261 |
|
|
|
1262 |
|
|
LOG_DBG((LOG_SA, 30, "sa_reinit: renegotiating active connections")); |
1263 |
|
|
|
1264 |
|
|
/* |
1265 |
|
|
* Get phase 2 SAs. Soft expire those without active exchanges. Do |
1266 |
|
|
* not touch a phase 2 SA where the soft expiration is not set, ie. |
1267 |
|
|
* the SA is not yet established. |
1268 |
|
|
*/ |
1269 |
|
|
for (i = 0; i <= bucket_mask; i++) |
1270 |
|
|
for (sa = LIST_FIRST(&sa_tab[i]); sa; sa = LIST_NEXT(sa, link)) |
1271 |
|
|
if (sa->phase == 2) |
1272 |
|
|
if (exchange_lookup_by_name(sa->name, |
1273 |
|
|
sa->phase) == 0 && sa->soft_death) { |
1274 |
|
|
timer_remove_event(sa->soft_death); |
1275 |
|
|
sa_soft_expire(sa); |
1276 |
|
|
} |
1277 |
|
|
} |
1278 |
|
|
|
1279 |
|
|
/* |
1280 |
|
|
* Get an SA attribute's flag value out of textual description. |
1281 |
|
|
*/ |
1282 |
|
|
int |
1283 |
|
|
sa_flag(char *attr) |
1284 |
|
|
{ |
1285 |
|
|
static struct sa_flag_map { |
1286 |
|
|
char *name; |
1287 |
|
|
int flag; |
1288 |
|
|
} sa_flag_map[] = { |
1289 |
|
|
{ |
1290 |
|
|
"active-only", SA_FLAG_ACTIVE_ONLY |
1291 |
|
|
}, |
1292 |
|
|
|
1293 |
|
|
/* |
1294 |
|
|
* Below this point are flags that are internal to the |
1295 |
|
|
* implementation. |
1296 |
|
|
*/ |
1297 |
|
|
{ |
1298 |
|
|
"__ondemand", SA_FLAG_ONDEMAND |
1299 |
|
|
}, |
1300 |
|
|
{ |
1301 |
|
|
"ikecfg", SA_FLAG_IKECFG |
1302 |
|
|
}, |
1303 |
|
|
}; |
1304 |
|
|
size_t i; |
1305 |
|
|
|
1306 |
|
|
for (i = 0; i < sizeof sa_flag_map / sizeof sa_flag_map[0]; i++) |
1307 |
|
|
if (strcasecmp(attr, sa_flag_map[i].name) == 0) |
1308 |
|
|
return sa_flag_map[i].flag; |
1309 |
|
|
log_print("sa_flag: attribute \"%s\" unknown", attr); |
1310 |
|
|
return 0; |
1311 |
|
|
} |
1312 |
|
|
|
1313 |
|
|
/* Mark SA as replaced. */ |
1314 |
|
|
void |
1315 |
|
|
sa_mark_replaced(struct sa *sa) |
1316 |
|
|
{ |
1317 |
|
|
LOG_DBG((LOG_SA, 60, "sa_mark_replaced: SA %p (%s) marked as replaced", |
1318 |
|
|
sa, sa->name ? sa->name : "unnamed")); |
1319 |
|
|
if (sa->dpd_event) { |
1320 |
|
|
timer_remove_event(sa->dpd_event); |
1321 |
|
|
sa->dpd_event = 0; |
1322 |
|
|
} |
1323 |
|
|
sa->flags |= SA_FLAG_REPLACED; |
1324 |
|
|
} |
1325 |
|
|
|
1326 |
|
|
/* Replace SA */ |
1327 |
|
|
void |
1328 |
|
|
sa_replace(struct sa *sa, struct sa *new_sa) |
1329 |
|
|
{ |
1330 |
|
|
LOG_DBG((LOG_SA, 60, "sa_replace: SA %p (%s) is replaced by SA %p (%s)", |
1331 |
|
|
sa, sa->name ? sa->name : "unnamed", |
1332 |
|
|
new_sa, new_sa->name ? new_sa->name : "unnamed")); |
1333 |
|
|
sa_mark_replaced(sa); |
1334 |
|
|
if (new_sa->flags & SA_FLAG_REPLACED) { |
1335 |
|
|
/* enable the dpd */ |
1336 |
|
|
if ((new_sa->flags & SA_FLAG_DPD) == SA_FLAG_DPD) |
1337 |
|
|
dpd_start(new_sa); |
1338 |
|
|
new_sa->flags &= ~SA_FLAG_REPLACED; |
1339 |
|
|
} |
1340 |
|
|
} |
1341 |
|
|
|
1342 |
|
|
/* |
1343 |
|
|
* Setup expiration timers for SA. This is used for ISAKMP SAs, but also |
1344 |
|
|
* possible to use for application SAs if the application does not deal |
1345 |
|
|
* with expirations itself. An example is the Linux FreeS/WAN KLIPS IPsec |
1346 |
|
|
* stack. |
1347 |
|
|
*/ |
1348 |
|
|
int |
1349 |
|
|
sa_setup_expirations(struct sa *sa) |
1350 |
|
|
{ |
1351 |
|
|
struct timeval expiration; |
1352 |
|
|
u_int64_t seconds = sa->seconds; |
1353 |
|
|
|
1354 |
|
|
/* |
1355 |
|
|
* Set the soft timeout to a random percentage between 85 & 95 of |
1356 |
|
|
* the negotiated lifetime to break strictly synchronized |
1357 |
|
|
* renegotiations. This works better when the randomization is on the |
1358 |
|
|
* order of processing plus network-roundtrip times, or larger. |
1359 |
|
|
* I.e. it depends on configuration and negotiated lifetimes. |
1360 |
|
|
* It is not good to do the decrease on the hard timeout, because then |
1361 |
|
|
* we may drop our SA before our peer. |
1362 |
|
|
* XXX Better scheme to come? |
1363 |
|
|
*/ |
1364 |
|
|
if (!sa->soft_death) { |
1365 |
|
|
gettimeofday(&expiration, 0); |
1366 |
|
|
/* |
1367 |
|
|
* XXX This should probably be configuration controlled |
1368 |
|
|
* somehow. |
1369 |
|
|
*/ |
1370 |
|
|
seconds = sa->seconds * (850 + arc4random_uniform(100)) / 1000; |
1371 |
|
|
LOG_DBG((LOG_TIMER, 95, |
1372 |
|
|
"sa_setup_expirations: SA %p soft timeout in %llu seconds", |
1373 |
|
|
sa, seconds)); |
1374 |
|
|
expiration.tv_sec += seconds; |
1375 |
|
|
sa->soft_death = timer_add_event("sa_soft_expire", |
1376 |
|
|
sa_soft_expire, sa, &expiration); |
1377 |
|
|
if (!sa->soft_death) { |
1378 |
|
|
/* If we don't give up we might start leaking... */ |
1379 |
|
|
sa_delete(sa, 1); |
1380 |
|
|
return -1; |
1381 |
|
|
} |
1382 |
|
|
sa_reference(sa); |
1383 |
|
|
} |
1384 |
|
|
if (!sa->death) { |
1385 |
|
|
gettimeofday(&expiration, 0); |
1386 |
|
|
LOG_DBG((LOG_TIMER, 95, |
1387 |
|
|
"sa_setup_expirations: SA %p hard timeout in %llu seconds", |
1388 |
|
|
sa, sa->seconds)); |
1389 |
|
|
expiration.tv_sec += sa->seconds; |
1390 |
|
|
sa->death = timer_add_event("sa_hard_expire", sa_hard_expire, |
1391 |
|
|
sa, &expiration); |
1392 |
|
|
if (!sa->death) { |
1393 |
|
|
/* If we don't give up we might start leaking... */ |
1394 |
|
|
sa_delete(sa, 1); |
1395 |
|
|
return -1; |
1396 |
|
|
} |
1397 |
|
|
sa_reference(sa); |
1398 |
|
|
} |
1399 |
|
|
return 0; |
1400 |
|
|
} |