| File: | src/sbin/isakmpd/sa.c |
| Warning: | line 820, column 9 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: sa.c,v 1.124 2017/12/05 20:31:45 jca 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_BITS6 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_BITS16 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)struct sa_list { struct sa *lh_first; } *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_BITS6) - 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])do { ((&sa_tab[i])->lh_first) = ((void *)0); } while ( 0); | |||
| 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])do { ((&sa_tab[i])->lh_first) = ((void *)0); } while ( 0); | |||
| 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_tab[i])->lh_first); sa; sa = LIST_NEXT(sa, link)((sa)->link.le_next)) | |||
| 132 | if (check(sa, arg)) { | |||
| 133 | LOG_DBG((LOG_SA, 90, "sa_find: return SA %p",log_debug (LOG_SA, 90, "sa_find: return SA %p", sa) | |||
| 134 | sa))log_debug (LOG_SA, 90, "sa_find: return SA %p", sa); | |||
| 135 | return sa; | |||
| 136 | } | |||
| 137 | LOG_DBG((LOG_SA, 90, "sa_find: no SA matched query"))log_debug (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_LEN8) == 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_REPLACED0x08); | |||
| 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_INET2: | |||
| 204 | return (memcmp(&((struct sockaddr_in *)sa1)->sin_addr, | |||
| 205 | &((struct sockaddr_in *)sa2)->sin_addr, | |||
| 206 | sizeof(struct in_addr))); | |||
| 207 | case AF_INET624: | |||
| 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_LOCAL1: | |||
| 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_READY0x01) == 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_INET2: | |||
| 246 | return ((struct sockaddr_in *)dst)->sin_port == ((struct sockaddr_in *)addr->addr)->sin_port; | |||
| 247 | case AF_INET624: | |||
| 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_READY0x01)) | |||
| 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)((src)->sa_len)) && | |||
| 276 | memcmp(dst, arg->dst, SA_LEN(dst)((dst)->sa_len))) | |||
| 277 | return 0; | |||
| 278 | ||||
| 279 | /* match icookie+rcookie against spi */ | |||
| 280 | if (memcmp(sa->cookies, arg->spi, ISAKMP_HDR_COOKIES_LEN(8 + 8)) == 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(8 + 8); 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_LEN4; 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)do { if (((sa)->link.le_next = (&sa_tab[bucket])->lh_first ) != ((void *)0)) (&sa_tab[bucket])->lh_first->link .le_prev = &(sa)->link.le_next; (&sa_tab[bucket])-> lh_first = (sa); (sa)->link.le_prev = &(&sa_tab[bucket ])->lh_first; } while (0); | |||
| 347 | sa_reference(sa); | |||
| 348 | LOG_DBG((LOG_SA, 70, "sa_enter: SA %p added to SA list", sa))log_debug (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_OFF0, | |||
| 360 | phase2 ? msg + ISAKMP_HDR_MESSAGE_ID_OFF20 : 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(8 + 8); 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_LEN4; 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])((&sa_tab[bucket])->lh_first); | |||
| 395 | sa && (memcmp(cookies, sa->cookies, ISAKMP_HDR_COOKIES_LEN(8 + 8)) != 0 || | |||
| 396 | (message_id && memcmp(message_id, sa->message_id, | |||
| 397 | ISAKMP_HDR_MESSAGE_ID_LEN4) != 0) || | |||
| 398 | (!message_id && !zero_test(sa->message_id, ISAKMP_HDR_MESSAGE_ID_LEN4))); | |||
| 399 | sa = LIST_NEXT(sa, link)((sa)->link.le_next)) | |||
| 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(8 + 8)); | |||
| 426 | memcpy(sa->message_id, exchange->message_id, | |||
| 427 | ISAKMP_HDR_MESSAGE_ID_LEN4); | |||
| 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)do { (&sa->protos)->tqh_first = ((void *)0); (& sa->protos)->tqh_last = &(&sa->protos)->tqh_first ; } while (0); | |||
| 445 | ||||
| 446 | sa_enter(sa); | |||
| 447 | TAILQ_INSERT_TAIL(&exchange->sa_list, sa, next)do { (sa)->next.tqe_next = ((void *)0); (sa)->next.tqe_prev = (&exchange->sa_list)->tqh_last; *(&exchange-> sa_list)->tqh_last = (sa); (&exchange->sa_list)-> tqh_last = &(sa)->next.tqe_next; } while (0); | |||
| 448 | sa_reference(sa); | |||
| 449 | ||||
| 450 | LOG_DBG((LOG_SA, 60,log_debug (LOG_SA, 60, "sa_create: sa %p phase %d added to exchange %p (%s)" , sa, sa->phase, exchange, exchange->name ? exchange-> name : "<unnamed>") | |||
| 451 | "sa_create: sa %p phase %d added to exchange %p (%s)", sa,log_debug (LOG_SA, 60, "sa_create: sa %p phase %d added to exchange %p (%s)" , sa, sa->phase, exchange, exchange->name ? exchange-> name : "<unnamed>") | |||
| 452 | sa->phase, exchange,log_debug (LOG_SA, 60, "sa_create: sa %p phase %d added to exchange %p (%s)" , sa, sa->phase, exchange, exchange->name ? exchange-> name : "<unnamed>") | |||
| 453 | exchange->name ? exchange->name : "<unnamed>"))log_debug (LOG_SA, 60, "sa_create: sa %p phase %d added to exchange %p (%s)" , sa, sa->phase, exchange, 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,log_debug (cls, level, "%s: %p %s phase %d doi %d flags 0x%x" , header, sa, sa->name ? sa->name : "<unnamed>", sa ->phase, sa->doi->id, sa->flags) | |||
| 469 | sa, sa->name ? sa->name : "<unnamed>", sa->phase, sa->doi->id,log_debug (cls, level, "%s: %p %s phase %d doi %d flags 0x%x" , header, sa, sa->name ? sa->name : "<unnamed>", sa ->phase, sa->doi->id, sa->flags) | |||
| 470 | sa->flags))log_debug (cls, level, "%s: %p %s phase %d doi %d flags 0x%x" , header, sa, sa->name ? sa->name : "<unnamed>", sa ->phase, sa->doi->id, sa->flags); | |||
| 471 | LOG_DBG((cls, level, "%s: icookie %08x%08x rcookie %08x%08x", header,log_debug (cls, level, "%s: icookie %08x%08x rcookie %08x%08x" , header, decode_32(sa->cookies), decode_32(sa->cookies + 4), decode_32(sa->cookies + 8), decode_32(sa->cookies + 12)) | |||
| 472 | decode_32(sa->cookies), decode_32(sa->cookies + 4),log_debug (cls, level, "%s: icookie %08x%08x rcookie %08x%08x" , header, decode_32(sa->cookies), decode_32(sa->cookies + 4), decode_32(sa->cookies + 8), decode_32(sa->cookies + 12)) | |||
| 473 | decode_32(sa->cookies + 8), decode_32(sa->cookies + 12)))log_debug (cls, level, "%s: icookie %08x%08x rcookie %08x%08x" , header, decode_32(sa->cookies), decode_32(sa->cookies + 4), decode_32(sa->cookies + 8), decode_32(sa->cookies + 12)); | |||
| 474 | LOG_DBG((cls, level, "%s: msgid %08x refcnt %d", header,log_debug (cls, level, "%s: msgid %08x refcnt %d", header, decode_32 (sa->message_id), sa->refcnt) | |||
| 475 | decode_32(sa->message_id), sa->refcnt))log_debug (cls, level, "%s: msgid %08x refcnt %d", header, decode_32 (sa->message_id), sa->refcnt); | |||
| 476 | LOG_DBG((cls, level, "%s: life secs %llu kb %llu", header, sa->seconds,log_debug (cls, level, "%s: life secs %llu kb %llu", header, sa ->seconds, sa->kilobytes) | |||
| 477 | sa->kilobytes))log_debug (cls, level, "%s: life secs %llu kb %llu", header, sa ->seconds, sa->kilobytes); | |||
| 478 | for (proto = TAILQ_FIRST(&sa->protos)((&sa->protos)->tqh_first); proto; | |||
| 479 | proto = TAILQ_NEXT(proto, link)((proto)->link.tqe_next)) { | |||
| 480 | LOG_DBG((cls, level, "%s: suite %d proto %d", header,log_debug (cls, level, "%s: suite %d proto %d", header, proto ->no, proto->proto) | |||
| 481 | proto->no, proto->proto))log_debug (cls, level, "%s: suite %d proto %d", header, proto ->no, proto->proto); | |||
| 482 | LOG_DBG((cls, level,log_debug (cls, level, "%s: spi_sz[0] %d spi[0] %p spi_sz[1] %d spi[1] %p" , header, proto->spi_sz[0], proto->spi[0], proto->spi_sz [1], proto->spi[1]) | |||
| 483 | "%s: spi_sz[0] %d spi[0] %p spi_sz[1] %d spi[1] %p",log_debug (cls, level, "%s: spi_sz[0] %d spi[0] %p spi_sz[1] %d spi[1] %p" , header, proto->spi_sz[0], proto->spi[0], proto->spi_sz [1], proto->spi[1]) | |||
| 484 | header, proto->spi_sz[0], proto->spi[0], proto->spi_sz[1],log_debug (cls, level, "%s: spi_sz[0] %d spi[0] %p spi_sz[1] %d spi[1] %p" , header, proto->spi_sz[0], proto->spi[0], proto->spi_sz [1], proto->spi[1]) | |||
| 485 | proto->spi[1]))log_debug (cls, level, "%s: spi_sz[0] %d spi[0] %p spi_sz[1] %d spi[1] %p" , header, proto->spi_sz[0], proto->spi[0], proto->spi_sz [1], proto->spi[1]); | |||
| 486 | LOG_DBG((cls, level, "%s: %s, %s", header,log_debug (cls, level, "%s: %s, %s", header, !sa->doi ? "<nodoi>" : sa->doi->decode_ids("initiator id: %s, responder id: %s" , sa->id_i, sa->id_i_len, sa->id_r, sa->id_r_len, 0), !sa->transport ? "<no transport>" : sa->transport ->vtbl->decode_ids(sa->transport)) | |||
| 487 | !sa->doi ? "<nodoi>" :log_debug (cls, level, "%s: %s, %s", header, !sa->doi ? "<nodoi>" : sa->doi->decode_ids("initiator id: %s, responder id: %s" , sa->id_i, sa->id_i_len, sa->id_r, sa->id_r_len, 0), !sa->transport ? "<no transport>" : sa->transport ->vtbl->decode_ids(sa->transport)) | |||
| 488 | sa->doi->decode_ids("initiator id: %s, responder id: %s",log_debug (cls, level, "%s: %s, %s", header, !sa->doi ? "<nodoi>" : sa->doi->decode_ids("initiator id: %s, responder id: %s" , sa->id_i, sa->id_i_len, sa->id_r, sa->id_r_len, 0), !sa->transport ? "<no transport>" : sa->transport ->vtbl->decode_ids(sa->transport)) | |||
| 489 | sa->id_i, sa->id_i_len,log_debug (cls, level, "%s: %s, %s", header, !sa->doi ? "<nodoi>" : sa->doi->decode_ids("initiator id: %s, responder id: %s" , sa->id_i, sa->id_i_len, sa->id_r, sa->id_r_len, 0), !sa->transport ? "<no transport>" : sa->transport ->vtbl->decode_ids(sa->transport)) | |||
| 490 | sa->id_r, sa->id_r_len, 0),log_debug (cls, level, "%s: %s, %s", header, !sa->doi ? "<nodoi>" : sa->doi->decode_ids("initiator id: %s, responder id: %s" , sa->id_i, sa->id_i_len, sa->id_r, sa->id_r_len, 0), !sa->transport ? "<no transport>" : sa->transport ->vtbl->decode_ids(sa->transport)) | |||
| 491 | !sa->transport ? "<no transport>" :log_debug (cls, level, "%s: %s, %s", header, !sa->doi ? "<nodoi>" : sa->doi->decode_ids("initiator id: %s, responder id: %s" , sa->id_i, sa->id_i_len, sa->id_r, sa->id_r_len, 0), !sa->transport ? "<no transport>" : sa->transport ->vtbl->decode_ids(sa->transport)) | |||
| 492 | sa->transport->vtbl->decode_ids(sa->transport)))log_debug (cls, level, "%s: %s, %s", header, !sa->doi ? "<nodoi>" : sa->doi->decode_ids("initiator id: %s, responder id: %s" , sa->id_i, sa->id_i_len, sa->id_r, sa->id_r_len, 0), !sa->transport ? "<no transport>" : 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,log_debug_buf (cls, level, spi_header, proto->spi[i], proto ->spi_sz[i]) | |||
| 498 | proto->spi[i], proto->spi_sz[i]))log_debug_buf (cls, level, spi_header, 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) (2 * 32 + 9) | |||
| 510 | char s[SBUFSZ(2 * 32 + 9)]; | |||
| 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_ESP3: | |||
| 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_3DES3: | |||
| 554 | fprintf(fd, "3DES\n"); | |||
| 555 | break; | |||
| 556 | ||||
| 557 | case IPSEC_ESP_AES12: | |||
| 558 | fprintf(fd, "AES (CBC)\n"); | |||
| 559 | break; | |||
| 560 | ||||
| 561 | case IPSEC_ESP_AES_CTR13: | |||
| 562 | fprintf(fd, "AES (CTR)\n"); | |||
| 563 | break; | |||
| 564 | ||||
| 565 | case IPSEC_ESP_AES_GCM_1620: | |||
| 566 | fprintf(fd, "AES (GCM)\n"); | |||
| 567 | break; | |||
| 568 | ||||
| 569 | case IPSEC_ESP_AES_GMAC23: | |||
| 570 | fprintf(fd, "AES (GMAC)\n"); | |||
| 571 | break; | |||
| 572 | ||||
| 573 | case IPSEC_ESP_CAST6: | |||
| 574 | fprintf(fd, "Cast-128\n"); | |||
| 575 | break; | |||
| 576 | ||||
| 577 | case IPSEC_ESP_BLOWFISH7: | |||
| 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_MD51: | |||
| 595 | fprintf(fd, "HMAC-MD5\n"); | |||
| 596 | break; | |||
| 597 | ||||
| 598 | case IPSEC_AUTH_HMAC_SHA2: | |||
| 599 | fprintf(fd, "HMAC-SHA1\n"); | |||
| 600 | break; | |||
| 601 | ||||
| 602 | case IPSEC_AUTH_HMAC_RIPEMD8: | |||
| 603 | fprintf(fd, "HMAC-RIPEMD-160\n"); | |||
| 604 | break; | |||
| 605 | ||||
| 606 | case IPSEC_AUTH_HMAC_SHA2_2565: | |||
| 607 | fprintf(fd, "HMAC-SHA2-256\n"); | |||
| 608 | break; | |||
| 609 | ||||
| 610 | case IPSEC_AUTH_HMAC_SHA2_3846: | |||
| 611 | fprintf(fd, "HMAC-SHA2-384\n"); | |||
| 612 | break; | |||
| 613 | ||||
| 614 | case IPSEC_AUTH_HMAC_SHA2_5127: | |||
| 615 | fprintf(fd, "HMAC-SHA2-512\n"); | |||
| 616 | break; | |||
| 617 | ||||
| 618 | case IPSEC_AUTH_DES_MAC3: | |||
| 619 | case IPSEC_AUTH_KPDK4: | |||
| 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_AH2: | |||
| 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_MD52: | |||
| 638 | fprintf(fd, "HMAC-MD5\n"); | |||
| 639 | break; | |||
| 640 | ||||
| 641 | case IPSEC_AH_SHA3: | |||
| 642 | fprintf(fd, "HMAC-SHA1\n"); | |||
| 643 | break; | |||
| 644 | ||||
| 645 | case IPSEC_AH_RIPEMD8: | |||
| 646 | fprintf(fd, "HMAC-RIPEMD-160\n"); | |||
| 647 | break; | |||
| 648 | ||||
| 649 | case IPSEC_AH_SHA2_2565: | |||
| 650 | fprintf(fd, "HMAC-SHA2-256\n"); | |||
| 651 | break; | |||
| 652 | ||||
| 653 | case IPSEC_AH_SHA2_3846: | |||
| 654 | fprintf(fd, "HMAC-SHA2-384\n"); | |||
| 655 | break; | |||
| 656 | ||||
| 657 | case IPSEC_AH_SHA2_5127: | |||
| 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)((&sa->protos)->tqh_first); proto; | |||
| 725 | proto = TAILQ_NEXT(proto, link)((proto)->link.tqe_next)) { | |||
| 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_tab[i])->lh_first); sa; sa = LIST_NEXT(sa, link)((sa)->link.le_next)) | |||
| 749 | sa_dump(LOG_REPORT-2, 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((void *)0) ? "<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_tab[i])->lh_first); sa; sa = LIST_NEXT(sa, link)((sa)->link.le_next)) | |||
| 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)do { if (((proto)->link.tqe_next) != ((void *)0)) (proto)-> link.tqe_next->link.tqe_prev = (proto)->link.tqe_prev; else (&sa->protos)->tqh_last = (proto)->link.tqe_prev ; *(proto)->link.tqe_prev = (proto)->link.tqe_next; ; ; } while (0); | |||
| 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)((&proto->xfs)->tqh_first)) != NULL((void *)0)) { | |||
| 820 | free(pa->attrs); | |||
| ||||
| 821 | TAILQ_REMOVE(&proto->xfs, pa, next)do { if (((pa)->next.tqe_next) != ((void *)0)) (pa)->next .tqe_next->next.tqe_prev = (pa)->next.tqe_prev; else (& proto->xfs)->tqh_last = (pa)->next.tqe_prev; *(pa)-> next.tqe_prev = (pa)->next.tqe_next; ; ; } while (0); | |||
| 822 | free(pa); | |||
| 823 | } | |||
| 824 | ||||
| 825 | LOG_DBG((LOG_SA, 90, "proto_free: freeing %p", proto))log_debug (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)do { if ((sa)->link.le_next != ((void *)0)) (sa)->link. le_next->link.le_prev = (sa)->link.le_prev; *(sa)->link .le_prev = (sa)->link.le_next; ; ; } while (0); | |||
| 855 | LOG_DBG((LOG_SA, 70, "sa_remove: SA %p removed from SA list", sa))log_debug (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",log_debug (LOG_SA, 80, "sa_reference: SA %p now has %d references" , sa, sa->refcnt) | |||
| 865 | sa, sa->refcnt))log_debug (LOG_SA, 80, "sa_reference: SA %p now has %d references" , 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",log_debug (LOG_SA, 80, "sa_release: SA %p had %d references", sa, sa->refcnt) | |||
| 876 | sa, sa->refcnt))log_debug (LOG_SA, 80, "sa_release: SA %p had %d references", sa, sa->refcnt); | |||
| 877 | ||||
| 878 | if (--sa->refcnt) | |||
| 879 | return; | |||
| 880 | ||||
| 881 | LOG_DBG((LOG_SA, 60, "sa_release: freeing SA %p", sa))log_debug (LOG_SA, 60, "sa_release: freeing SA %p", sa); | |||
| 882 | ||||
| 883 | while ((proto = TAILQ_FIRST(&sa->protos)((&sa->protos)->tqh_first)) != 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_PUBLIC0, | |||
| 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)((&msg->exchange->sa_list)->tqh_first); | |||
| 928 | ||||
| 929 | sa_remove(sa); | |||
| 930 | GET_ISAKMP_HDR_RCOOKIE(msg->iov[0].iov_base,field_get_raw (isakmp_hdr_fld + 1, msg->iov[0].iov_base, sa ->cookies + 8) | |||
| 931 | sa->cookies + ISAKMP_HDR_ICOOKIE_LEN)field_get_raw (isakmp_hdr_fld + 1, msg->iov[0].iov_base, sa ->cookies + 8); | |||
| 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(17 + 1) (IKE_ATTR_BLOCK_SIZE17 + 1) /* XXX Should be dynamic. */ | |||
| 943 | ||||
| 944 | struct attr_validation_state { | |||
| 945 | u_int8_t *attrp[ATTRS_SIZE(17 + 1)]; | |||
| 946 | u_int8_t checked[ATTRS_SIZE(17 + 1)]; | |||
| 947 | u_int16_t len[ATTRS_SIZE(17 + 1)]; | |||
| 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 "log_debug (LOG_SA, 95, "sa_validate_xf_attrs: phase %d mode %d type %d " "len %d", avs->phase, avs->mode, type, len) | |||
| 963 | "len %d", avs->phase, avs->mode, type, len))log_debug (LOG_SA, 95, "sa_validate_xf_attrs: phase %d mode %d type %d " "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_ALGORITHM1 || | |||
| 968 | type > IKE_ATTR_BLOCK_SIZE17) | |||
| 969 | return 1; | |||
| 970 | } else if (avs->phase == 2) { | |||
| 971 | if (type < IPSEC_ATTR_SA_LIFE_TYPE1 || | |||
| 972 | type > IPSEC_ATTR_ECN_TUNNEL10) | |||
| 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)field_get_num (isakmp_prop_fld + 1, xf->context->p)) { | |||
| 1031 | LOG_DBG((LOG_SA, 70, "sa_validate_proto_xf: proto %p (#%d) "log_debug (LOG_SA, 70, "sa_validate_proto_xf: proto %p (#%d) " "protocol mismatch", match, match->no) | |||
| 1032 | "protocol mismatch", match, match->no))log_debug (LOG_SA, 70, "sa_validate_proto_xf: proto %p (#%d) " "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_OFF8, | |||
| 1045 | GET_ISAKMP_GEN_LENGTH(xf->p)field_get_num (isakmp_gen_fld + 2, xf->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF8, | |||
| 1046 | sa_validate_xf_attrs, avs); | |||
| 1047 | xf_id = GET_ISAKMP_TRANSFORM_ID(xf->p)field_get_num (isakmp_transform_fld + 1, xf->p); | |||
| 1048 | ||||
| 1049 | /* Check against the transforms we suggested. */ | |||
| 1050 | avs->mode++; | |||
| 1051 | for (pa = TAILQ_FIRST(&match->xfs)((&match->xfs)->tqh_first); pa && !found; | |||
| 1052 | pa = TAILQ_NEXT(pa, next)((pa)->next.tqe_next)) { | |||
| 1053 | if (xf_id != GET_ISAKMP_TRANSFORM_ID(pa->attrs)field_get_num (isakmp_transform_fld + 1, pa->attrs)) | |||
| 1054 | continue; | |||
| 1055 | ||||
| 1056 | bzero(avs->checked, sizeof avs->checked); | |||
| 1057 | if (attribute_map(pa->attrs + ISAKMP_TRANSFORM_SA_ATTRS_OFF8, | |||
| 1058 | pa->len - ISAKMP_TRANSFORM_SA_ATTRS_OFF8, | |||
| 1059 | sa_validate_xf_attrs, avs) == 0) | |||
| 1060 | found++; | |||
| 1061 | ||||
| 1062 | LOG_DBG((LOG_SA, 80, "sa_validate_proto_xf: attr_map "log_debug (LOG_SA, 80, "sa_validate_proto_xf: attr_map " "xf %p proto %p pa %p found %d" , xf, match, pa, found) | |||
| 1063 | "xf %p proto %p pa %p found %d", xf, match, pa, found))log_debug (LOG_SA, 80, "sa_validate_proto_xf: attr_map " "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 "log_debug (LOG_SA, 80, "sa_validate_proto_xf: req_attr " "xf %p proto %p pa %p found %d" , xf, match, pa, found) | |||
| 1077 | "xf %p proto %p pa %p found %d", xf, match, pa, found))log_debug (LOG_SA, 80, "sa_validate_proto_xf: req_attr " "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)((&sa->protos)->tqh_first); | |||
| 1112 | proto && proto->no != GET_ISAKMP_PROP_NO(prop->p)field_get_num (isakmp_prop_fld + 0, prop->p); | |||
| 1113 | proto = TAILQ_NEXT(proto, link)((proto)->link.tqe_next)) | |||
| 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)((&sa->protos)->tqh_first); | |||
| 1121 | proto && sa_validate_proto_xf(proto, xf, sa->phase) != 0; | |||
| 1122 | proto = TAILQ_NEXT(proto, link)((proto)->link.tqe_next)) | |||
| 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)field_get_num (isakmp_prop_fld + 0, prop->p); | |||
| 1139 | proto->proto = GET_ISAKMP_PROP_PROTO(prop->p)field_get_num (isakmp_prop_fld + 1, prop->p); | |||
| 1140 | proto->spi_sz[0] = GET_ISAKMP_PROP_SPI_SZ(prop->p)field_get_num (isakmp_prop_fld + 2, 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_OFF8, | |||
| 1146 | proto->spi_sz[0]); | |||
| 1147 | } | |||
| 1148 | proto->chosen = xf; | |||
| 1149 | proto->sa = sa; | |||
| 1150 | proto->id = GET_ISAKMP_TRANSFORM_ID(xf->p)field_get_num (isakmp_transform_fld + 1, xf->p); | |||
| 1151 | if (!initiator) | |||
| 1152 | TAILQ_INSERT_TAIL(&sa->protos, proto, link)do { (proto)->link.tqe_next = ((void *)0); (proto)->link .tqe_prev = (&sa->protos)->tqh_last; *(&sa-> protos)->tqh_last = (proto); (&sa->protos)->tqh_last = &(proto)->link.tqe_next; } while (0); | |||
| 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,log_debug (LOG_SA, 80, "sa_add_transform: " "proto %p no %d proto %d chosen %p sa %p id %d" , proto, proto->no, proto->proto, proto->chosen, proto ->sa, proto->id) | |||
| 1159 | "sa_add_transform: "log_debug (LOG_SA, 80, "sa_add_transform: " "proto %p no %d proto %d chosen %p sa %p id %d" , proto, proto->no, proto->proto, proto->chosen, proto ->sa, proto->id) | |||
| 1160 | "proto %p no %d proto %d chosen %p sa %p id %d",log_debug (LOG_SA, 80, "sa_add_transform: " "proto %p no %d proto %d chosen %p sa %p id %d" , proto, proto->no, proto->proto, proto->chosen, proto ->sa, proto->id) | |||
| 1161 | proto, proto->no, proto->proto, proto->chosen, proto->sa,log_debug (LOG_SA, 80, "sa_add_transform: " "proto %p no %d proto %d chosen %p sa %p id %d" , proto, proto->no, proto->proto, proto->chosen, proto ->sa, proto->id) | |||
| 1162 | proto->id))log_debug (LOG_SA, 80, "sa_add_transform: " "proto %p no %d proto %d chosen %p sa %p id %d" , proto, proto->no, proto->proto, proto->chosen, proto ->sa, 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:"))log_debug (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_tab[i])->lh_first); sa; sa = next) { | |||
| 1196 | next = LIST_NEXT(sa, link)((sa)->link.le_next); | |||
| 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,log_debug (LOG_SA, 70, "sa_teardown_all: tearing down SA %s", sa->name ? sa->name : "<unnamed>") | |||
| 1203 | "sa_teardown_all: tearing down SA %s",log_debug (LOG_SA, 70, "sa_teardown_all: tearing down SA %s", sa->name ? sa->name : "<unnamed>") | |||
| 1204 | sa->name ? sa->name : "<unnamed>"))log_debug (LOG_SA, 70, "sa_teardown_all: tearing down SA %s", 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_STAYALIVE0x02 | SA_FLAG_REPLACED0x08)) == | |||
| 1224 | SA_FLAG_STAYALIVE0x02) | |||
| 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_FADING0x10; | |||
| 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_STAYALIVE0x02 | SA_FLAG_REPLACED0x08)) == | |||
| 1244 | SA_FLAG_STAYALIVE0x02) | |||
| 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"))log_debug (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_tab[i])->lh_first); sa; sa = LIST_NEXT(sa, link)((sa)->link.le_next)) | |||
| 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_ONLY0x20 | |||
| 1291 | }, | |||
| 1292 | ||||
| 1293 | /* | |||
| 1294 | * Below this point are flags that are internal to the | |||
| 1295 | * implementation. | |||
| 1296 | */ | |||
| 1297 | { | |||
| 1298 | "__ondemand", SA_FLAG_ONDEMAND0x04 | |||
| 1299 | }, | |||
| 1300 | { | |||
| 1301 | "ikecfg", SA_FLAG_IKECFG0x40 | |||
| 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",log_debug (LOG_SA, 60, "sa_mark_replaced: SA %p (%s) marked as replaced" , sa, sa->name ? sa->name : "unnamed") | |||
| 1318 | sa, sa->name ? sa->name : "unnamed"))log_debug (LOG_SA, 60, "sa_mark_replaced: SA %p (%s) marked as replaced" , 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_REPLACED0x08; | |||
| 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)",log_debug (LOG_SA, 60, "sa_replace: SA %p (%s) is replaced by SA %p (%s)" , sa, sa->name ? sa->name : "unnamed", new_sa, new_sa-> name ? new_sa->name : "unnamed") | |||
| 1331 | sa, sa->name ? sa->name : "unnamed",log_debug (LOG_SA, 60, "sa_replace: SA %p (%s) is replaced by SA %p (%s)" , sa, sa->name ? sa->name : "unnamed", new_sa, new_sa-> name ? new_sa->name : "unnamed") | |||
| 1332 | new_sa, new_sa->name ? new_sa->name : "unnamed"))log_debug (LOG_SA, 60, "sa_replace: SA %p (%s) is replaced by SA %p (%s)" , sa, sa->name ? sa->name : "unnamed", new_sa, new_sa-> name ? new_sa->name : "unnamed"); | |||
| 1333 | sa_mark_replaced(sa); | |||
| 1334 | if (new_sa->flags & SA_FLAG_REPLACED0x08) { | |||
| 1335 | /* enable the dpd */ | |||
| 1336 | if ((new_sa->flags & SA_FLAG_DPD0x80) == SA_FLAG_DPD0x80) | |||
| 1337 | dpd_start(new_sa); | |||
| 1338 | new_sa->flags &= ~SA_FLAG_REPLACED0x08; | |||
| 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 timespec 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 | clock_gettime(CLOCK_MONOTONIC3, &expiration); | |||
| 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,log_debug (LOG_TIMER, 95, "sa_setup_expirations: SA %p soft timeout in %llu seconds" , sa, seconds) | |||
| 1372 | "sa_setup_expirations: SA %p soft timeout in %llu seconds",log_debug (LOG_TIMER, 95, "sa_setup_expirations: SA %p soft timeout in %llu seconds" , sa, seconds) | |||
| 1373 | sa, seconds))log_debug (LOG_TIMER, 95, "sa_setup_expirations: SA %p soft timeout in %llu seconds" , 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 | clock_gettime(CLOCK_MONOTONIC3, &expiration); | |||
| 1386 | LOG_DBG((LOG_TIMER, 95,log_debug (LOG_TIMER, 95, "sa_setup_expirations: SA %p hard timeout in %llu seconds" , sa, sa->seconds) | |||
| 1387 | "sa_setup_expirations: SA %p hard timeout in %llu seconds",log_debug (LOG_TIMER, 95, "sa_setup_expirations: SA %p hard timeout in %llu seconds" , sa, sa->seconds) | |||
| 1388 | sa, sa->seconds))log_debug (LOG_TIMER, 95, "sa_setup_expirations: SA %p hard timeout in %llu seconds" , 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 | } |