diff options
author | Othmar Gsenger <otti@anytun.org> | 2007-12-08 20:59:57 +0000 |
---|---|---|
committer | Othmar Gsenger <otti@anytun.org> | 2007-12-08 20:59:57 +0000 |
commit | f84dc62cc602eacb0daee3e9918a68b711ba94f0 (patch) | |
tree | 1acbdabf30b2ece1da880386da6a4b7c002669c3 /keyexchange/isakmpd-20041012/exchange.c | |
parent | * added AuthTag class (diff) |
removed isakmpd
Diffstat (limited to 'keyexchange/isakmpd-20041012/exchange.c')
-rw-r--r-- | keyexchange/isakmpd-20041012/exchange.c | 1799 |
1 files changed, 0 insertions, 1799 deletions
diff --git a/keyexchange/isakmpd-20041012/exchange.c b/keyexchange/isakmpd-20041012/exchange.c deleted file mode 100644 index 1c4ef1f..0000000 --- a/keyexchange/isakmpd-20041012/exchange.c +++ /dev/null @@ -1,1799 +0,0 @@ -/* $OpenBSD: exchange.c,v 1.104 2004/09/17 13:53:08 ho Exp $ */ -/* $EOM: exchange.c,v 1.143 2000/12/04 00:02:25 angelos Exp $ */ - -/* - * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved. - * Copyright (c) 1999, 2001 Angelos D. Keromytis. All rights reserved. - * Copyright (c) 1999, 2000, 2002 Håkan Olsson. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * This code was written under funding by Ericsson Radio Systems. - */ - -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <stdlib.h> -#include <string.h> - -#include "sysdep.h" - -#include "cert.h" -#include "conf.h" -#include "connection.h" -#include "constants.h" -#include "cookie.h" -#include "crypto.h" -#include "doi.h" -#include "exchange.h" -#include "ipsec_num.h" -#include "isakmp.h" -#ifdef USE_ISAKMP_CFG -#include "isakmp_cfg.h" -#endif -#include "libcrypto.h" -#include "log.h" -#include "message.h" -#include "timer.h" -#include "transport.h" -#include "ipsec.h" -#include "sa.h" -#include "util.h" -#include "key.h" - -/* Initial number of bits from the cookies used as hash. */ -#define INITIAL_BUCKET_BITS 6 - -/* - * Don't try to use more bits than this as a hash. - * We only XOR 16 bits so going above that means changing the code below - * too. - */ -#define MAX_BUCKET_BITS 16 - -#ifdef USE_DEBUG -static void exchange_dump(char *, struct exchange *); -#endif -static void exchange_free_aux(void *); -#if 0 -static void exchange_resize(void); -#endif -static struct exchange *exchange_lookup_active(char *, int); - -static -LIST_HEAD(exchange_list, exchange) *exchange_tab; - -/* Works both as a maximum index and a mask. */ -static int bucket_mask; - -/* - * Validation scripts used to test messages for correct content of - * payloads depending on the exchange type. - */ -int16_t script_base[] = { - ISAKMP_PAYLOAD_SA, /* Initiator -> responder. */ - ISAKMP_PAYLOAD_NONCE, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_SA, /* Responder -> initiator. */ - ISAKMP_PAYLOAD_NONCE, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_KEY_EXCH, /* Initiator -> responder. */ - ISAKMP_PAYLOAD_ID, - EXCHANGE_SCRIPT_AUTH, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_KEY_EXCH, /* Responder -> initiator. */ - ISAKMP_PAYLOAD_ID, - EXCHANGE_SCRIPT_AUTH, - EXCHANGE_SCRIPT_END -}; - -int16_t script_identity_protection[] = { - ISAKMP_PAYLOAD_SA, /* Initiator -> responder. */ - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_SA, /* Responder -> initiator. */ - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_KEY_EXCH, /* Initiator -> responder. */ - ISAKMP_PAYLOAD_NONCE, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_KEY_EXCH, /* Responder -> initiator. */ - ISAKMP_PAYLOAD_NONCE, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_ID, /* Initiator -> responder. */ - EXCHANGE_SCRIPT_AUTH, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_ID, /* Responder -> initiator. */ - EXCHANGE_SCRIPT_AUTH, - EXCHANGE_SCRIPT_END -}; - -int16_t script_authentication_only[] = { - ISAKMP_PAYLOAD_SA, /* Initiator -> responder. */ - ISAKMP_PAYLOAD_NONCE, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_SA, /* Responder -> initiator. */ - ISAKMP_PAYLOAD_NONCE, - ISAKMP_PAYLOAD_ID, - EXCHANGE_SCRIPT_AUTH, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_ID, /* Initiator -> responder. */ - EXCHANGE_SCRIPT_AUTH, - EXCHANGE_SCRIPT_END -}; - -#ifdef USE_AGGRESSIVE -int16_t script_aggressive[] = { - ISAKMP_PAYLOAD_SA, /* Initiator -> responder. */ - ISAKMP_PAYLOAD_KEY_EXCH, - ISAKMP_PAYLOAD_NONCE, - ISAKMP_PAYLOAD_ID, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_SA, /* Responder -> initiator. */ - ISAKMP_PAYLOAD_KEY_EXCH, - ISAKMP_PAYLOAD_NONCE, - ISAKMP_PAYLOAD_ID, - EXCHANGE_SCRIPT_AUTH, - EXCHANGE_SCRIPT_SWITCH, - EXCHANGE_SCRIPT_AUTH, /* Initiator -> responder. */ - EXCHANGE_SCRIPT_END -}; -#endif /* USE_AGGRESSIVE */ - -int16_t script_informational[] = { - EXCHANGE_SCRIPT_INFO, /* Initiator -> responder. */ - EXCHANGE_SCRIPT_END -}; - -/* - * Check what exchange SA is negotiated with and return a suitable validation - * script. - */ -int16_t * -exchange_script(struct exchange *exchange) -{ - switch (exchange->type) { - case ISAKMP_EXCH_BASE: - return script_base; - case ISAKMP_EXCH_ID_PROT: - return script_identity_protection; - case ISAKMP_EXCH_AUTH_ONLY: - return script_authentication_only; -#ifdef USE_AGGRESSIVE - case ISAKMP_EXCH_AGGRESSIVE: - return script_aggressive; -#endif - case ISAKMP_EXCH_INFO: - return script_informational; -#ifdef USE_ISAKMP_CFG - case ISAKMP_EXCH_TRANSACTION: - return script_transaction; -#endif - default: - if (exchange->type >= ISAKMP_EXCH_DOI_MIN && - exchange->type <= ISAKMP_EXCH_DOI_MAX) - return exchange->doi->exchange_script(exchange->type); - } - return 0; -} - -/* - * Validate the message MSG's contents wrt what payloads the exchange type - * requires at this point in the dialogoue. Return -1 if the validation fails, - * 0 if it succeeds and the script is not finished and 1 if it's ready. - */ -static int -exchange_validate(struct message *msg) -{ - struct exchange *exchange = msg->exchange; - int16_t *pc = exchange->exch_pc; - - while (*pc != EXCHANGE_SCRIPT_END && *pc != EXCHANGE_SCRIPT_SWITCH) { - LOG_DBG((LOG_EXCHANGE, 90, - "exchange_validate: checking for required %s", - *pc >= ISAKMP_PAYLOAD_NONE - ? constant_name(isakmp_payload_cst, *pc) - : constant_name(exchange_script_cst, *pc))); - - /* Check for existence of the required payloads. */ - if ((*pc > 0 && !payload_first(msg, *pc)) - || (*pc == EXCHANGE_SCRIPT_AUTH - && !payload_first(msg, ISAKMP_PAYLOAD_HASH) - && !payload_first(msg, ISAKMP_PAYLOAD_SIG)) - || (*pc == EXCHANGE_SCRIPT_INFO - && ((!payload_first(msg, ISAKMP_PAYLOAD_NOTIFY) - && !payload_first(msg, ISAKMP_PAYLOAD_DELETE)) - || (payload_first(msg, ISAKMP_PAYLOAD_DELETE) - && !payload_first(msg, ISAKMP_PAYLOAD_HASH))))) { - /* Missing payload. */ - LOG_DBG((LOG_MESSAGE, 70, - "exchange_validate: msg %p requires missing %s", - msg, *pc >= ISAKMP_PAYLOAD_NONE - ? constant_name(isakmp_payload_cst, *pc) - : constant_name(exchange_script_cst, *pc))); - return -1; - } - pc++; - } - if (*pc == EXCHANGE_SCRIPT_END) - /* Cleanup. */ - return 1; - - return 0; -} - -/* Feed unhandled payloads to the DOI for handling. Help for exchange_run(). */ -static void -exchange_handle_leftover_payloads(struct message *msg) -{ - struct exchange *exchange = msg->exchange; - struct doi *doi = exchange->doi; - struct payload *p; - int i; - - for (i = ISAKMP_PAYLOAD_SA; i < payload_index_max; i++) { - if (i == ISAKMP_PAYLOAD_PROPOSAL || - i == ISAKMP_PAYLOAD_TRANSFORM) - continue; - for (p = payload_first(msg, i); p; - p = TAILQ_NEXT(p, link)) { - if (p->flags & PL_MARK) - continue; - if (!doi->handle_leftover_payload || - doi->handle_leftover_payload(msg, i, p)) - LOG_DBG((LOG_EXCHANGE, 10, - "exchange_run: unexpected payload %s", - constant_name(isakmp_payload_cst, i))); - } - } -} - -/* - * Run the exchange script from a point given by the "program counter" - * upto either the script's end or a transmittal of a message. If we are - * at the point of a reception of a message, that message should be handed - * in here in the MSG argument. Otherwise we are the initiator and should - * expect MSG to be a half-cooked message without payloads. - */ -void -exchange_run(struct message *msg) -{ - struct exchange *exchange = msg->exchange; - struct doi *doi = exchange->doi; - int (*handler)(struct message *) = exchange->initiator ? - doi->initiator : doi->responder; - int done = 0; - - while (!done) { - /* - * It's our turn if we're either the initiator on an even step, - * or the responder on an odd step of the dialogue. - */ - if (exchange->initiator ^ (exchange->step % 2)) { - done = 1; - if (exchange->step) - msg = message_alloc_reply(msg); - message_setup_header(msg, exchange->type, 0, - exchange->message_id); - if (handler(msg)) { - /* - * This can happen when transient starvation - * of memory occurs. - * XXX The peer's retransmit ought to - * kick-start this exchange again. If he's - * stopped retransmitting he's likely dropped - * the SA at his side so we need to do that - * too, i.e. implement automatic SA teardown - * after a certain amount of inactivity. - */ - log_print("exchange_run: doi->%s (%p) failed", - exchange->initiator ? "initiator" : - "responder", msg); - message_free(msg); - return; - } - switch (exchange_validate(msg)) { - case 1: - /* - * The last message of a multi-message - * exchange should not be retransmitted other - * than "on-demand", i.e. if we see - * retransmits of the last message of the peer - * later. - */ - msg->flags |= MSG_LAST; - if (exchange->step > 0) { - if (exchange->last_sent) - message_free(exchange->last_sent); - exchange->last_sent = msg; - } - /* - * After we physically have sent our last - * message we need to do SA-specific - * finalization, like telling our application - * the SA is ready to be used, or issuing a - * CONNECTED notify if we set the COMMIT bit. - */ - message_register_post_send(msg, - exchange_finalize); - - /* Fallthrough. */ - - case 0: - /* XXX error handling. */ - message_send(msg); - break; - - default: - log_print("exchange_run: exchange_validate " - "failed, DOI error"); - exchange_free(exchange); - message_free(msg); - return; - } - } else { - done = exchange_validate(msg); - switch (done) { - case 0: - case 1: - /* Feed the message to the DOI. */ - if (handler(msg)) { - /* - * Trust the peer to retransmit. - * XXX We have to implement SA aging - * with automatic teardown. - */ - message_free(msg); - return; - } - /* - * Go over the yet unhandled payloads and feed - * them to DOI for handling. - */ - exchange_handle_leftover_payloads(msg); - - /* - * We have advanced the state. If we have - * been processing an incoming message, record - * that message as the one to do duplication - * tests against. - */ - if (exchange->last_received) - message_free(exchange->last_received); - exchange->last_received = msg; - if (exchange->flags & EXCHANGE_FLAG_ENCRYPT) - crypto_update_iv(exchange->keystate); - - if (done) { - exchange_finalize(msg); - return; - } - break; - - case -1: - log_print("exchange_run: exchange_validate " - "failed"); - /* - * XXX Is this the best error notification - * type? - */ - message_drop(msg, - ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1); - return; - } - } - - LOG_DBG((LOG_EXCHANGE, 40, - "exchange_run: exchange %p finished step %d, advancing...", - exchange, exchange->step)); - exchange->step++; - while (*exchange->exch_pc != EXCHANGE_SCRIPT_SWITCH - && *exchange->exch_pc != EXCHANGE_SCRIPT_END) - exchange->exch_pc++; - exchange->exch_pc++; - } -} - -void -exchange_init(void) -{ - int i; - - bucket_mask = (1 << INITIAL_BUCKET_BITS) - 1; - exchange_tab = malloc((bucket_mask + 1) * - sizeof(struct exchange_list)); - if (!exchange_tab) - log_fatal("exchange_init: out of memory"); - for (i = 0; i <= bucket_mask; i++) - LIST_INIT(&exchange_tab[i]); -} - -#if 0 -/* XXX Currently unused. */ -static void -exchange_resize(void) -{ - struct exchange_list *new_tab; - int new_mask = (bucket_mask + 1) * 2 - 1; - int i; - - new_tab = realloc(exchange_tab, - (new_mask + 1) * sizeof(struct exchange_list)); - if (!new_tab) - return; - for (i = bucket_mask + 1; i <= new_mask; i++) - LIST_INIT(&new_tab[i]); - bucket_mask = new_mask; - /* XXX Rehash existing entries. */ -} -#endif - -/* Lookup a phase 1 exchange out of just the initiator cookie. */ -struct exchange * -exchange_lookup_from_icookie(u_int8_t * cookie) -{ - struct exchange *exchange; - int i; - - for (i = 0; i <= bucket_mask; i++) - for (exchange = LIST_FIRST(&exchange_tab[i]); exchange; - exchange = LIST_NEXT(exchange, link)) - if (memcmp(exchange->cookies, cookie, - ISAKMP_HDR_ICOOKIE_LEN) == 0 && - exchange->phase == 1) - return exchange; - return 0; -} - -/* Lookup an exchange out of the name and phase. */ -struct exchange * -exchange_lookup_by_name(char *name, int phase) -{ - struct exchange *exchange; - int i; - - /* If we search for nothing, we will find nothing. */ - if (!name) - return 0; - - for (i = 0; i <= bucket_mask; i++) - for (exchange = LIST_FIRST(&exchange_tab[i]); exchange; - exchange = LIST_NEXT(exchange, link)) { - LOG_DBG((LOG_EXCHANGE, 90, - "exchange_lookup_by_name: %s == %s && %d == %d?", - name, exchange->name ? exchange->name : - "<unnamed>", phase, exchange->phase)); - - /* - * Match by name, but don't select finished exchanges, - * i.e where MSG_LAST are set in last_sent msg. - */ - if (exchange->name && - strcasecmp(exchange->name, name) == 0 && - exchange->phase == phase && - (!exchange->last_sent || - (exchange->last_sent->flags & MSG_LAST) == 0)) - return exchange; - } - return 0; -} - -/* Lookup an exchange out of the name, phase and step > 1. */ -static struct exchange * -exchange_lookup_active(char *name, int phase) -{ - struct exchange *exchange; - int i; - - /* XXX Almost identical to exchange_lookup_by_name. */ - - if (!name) - return 0; - - for (i = 0; i <= bucket_mask; i++) - for (exchange = LIST_FIRST(&exchange_tab[i]); exchange; - exchange = LIST_NEXT(exchange, link)) { - LOG_DBG((LOG_EXCHANGE, 90, - "exchange_lookup_active: %s == %s && %d == %d?", - name, exchange->name ? exchange->name : - "<unnamed>", phase, exchange->phase)); - if (exchange->name && - strcasecmp(exchange->name, name) == 0 && - exchange->phase == phase) { - if (exchange->step > 1) - return exchange; - else - LOG_DBG((LOG_EXCHANGE, 80, - "exchange_lookup_active: avoided " - "early (pre-step 1) exchange %p", - exchange)); - } - } - return 0; -} - -static void -exchange_enter(struct exchange *exchange) -{ - u_int16_t bucket = 0; - u_int8_t *cp; - int i; - - /* XXX We might resize if we are crossing a certain threshold */ - - for (i = 0; i < ISAKMP_HDR_COOKIES_LEN; i += 2) { - cp = exchange->cookies + i; - /* Doing it this way avoids alignment problems. */ - bucket ^= cp[0] | cp[1] << 8; - } - for (i = 0; i < ISAKMP_HDR_MESSAGE_ID_LEN; i += 2) { - cp = exchange->message_id + i; - /* Doing it this way avoids alignment problems. */ - bucket ^= cp[0] | cp[1] << 8; - } - bucket &= bucket_mask; - LIST_INSERT_HEAD(&exchange_tab[bucket], exchange, link); -} - -/* - * Lookup the exchange given by the header fields MSG. PHASE2 is false when - * looking for phase 1 exchanges and true otherwise. - */ -struct exchange * -exchange_lookup(u_int8_t *msg, int phase2) -{ - struct exchange *exchange; - u_int16_t bucket = 0; - u_int8_t *cp; - int i; - - /* - * We use the cookies to get bits to use as an index into exchange_tab, - * as at least one (our cookie) is a good hash, xoring all the bits, - * 16 at a time, and then masking, should do. Doing it this way means - * we can validate cookies very fast thus delimiting the effects of - * "Denial of service"-attacks using packet flooding. - */ - for (i = 0; i < ISAKMP_HDR_COOKIES_LEN; i += 2) { - cp = msg + ISAKMP_HDR_COOKIES_OFF + i; - /* Doing it this way avoids alignment problems. */ - bucket ^= cp[0] | cp[1] << 8; - } - if (phase2) - for (i = 0; i < ISAKMP_HDR_MESSAGE_ID_LEN; i += 2) { - cp = msg + ISAKMP_HDR_MESSAGE_ID_OFF + i; - /* Doing it this way avoids alignment problems. */ - bucket ^= cp[0] | cp[1] << 8; - } - bucket &= bucket_mask; - for (exchange = LIST_FIRST(&exchange_tab[bucket]); - exchange && (memcmp(msg + ISAKMP_HDR_COOKIES_OFF, - exchange->cookies, ISAKMP_HDR_COOKIES_LEN) != 0 || - (phase2 && memcmp(msg + ISAKMP_HDR_MESSAGE_ID_OFF, - exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN) != 0) || - (!phase2 && !zero_test(msg + ISAKMP_HDR_MESSAGE_ID_OFF, - ISAKMP_HDR_MESSAGE_ID_LEN))); - exchange = LIST_NEXT(exchange, link)) - ; - - return exchange; -} - -/* - * Create a phase PHASE exchange where INITIATOR denotes our role. DOI - * is the domain of interpretation identifier and TYPE tells what exchange - * type to use per either the DOI document or the ISAKMP spec proper. - * NSA tells how many SAs we should pre-allocate, and should be zero - * when we have the responder role. - */ -static struct exchange * -exchange_create(int phase, int initiator, int doi, int type) -{ - struct exchange *exchange; - struct timeval expiration; - int delta; - - /* - * We want the exchange zeroed for exchange_free to be able to find - * out what fields have been filled-in. - */ - exchange = calloc(1, sizeof *exchange); - if (!exchange) { - log_error("exchange_create: calloc (1, %lu) failed", - (unsigned long)sizeof *exchange); - return 0; - } - exchange->phase = phase; - exchange->step = 0; - exchange->initiator = initiator; - memset(exchange->cookies, 0, ISAKMP_HDR_COOKIES_LEN); - memset(exchange->message_id, 0, ISAKMP_HDR_MESSAGE_ID_LEN); - exchange->doi = doi_lookup(doi); - exchange->type = type; - exchange->policy_id = -1; - exchange->exch_pc = exchange_script(exchange); - exchange->last_sent = exchange->last_received = 0; - TAILQ_INIT(&exchange->sa_list); - TAILQ_INIT(&exchange->aca_list); - - /* Allocate the DOI-specific structure and initialize it to zeroes. */ - if (exchange->doi->exchange_size) { - exchange->data = calloc(1, exchange->doi->exchange_size); - if (!exchange->data) { - log_error("exchange_create: calloc (1, %lu) failed", - (unsigned long)exchange->doi->exchange_size); - exchange_free(exchange); - return 0; - } - } - gettimeofday(&expiration, 0); - delta = conf_get_num("General", "Exchange-max-time", - EXCHANGE_MAX_TIME); - expiration.tv_sec += delta; - exchange->death = timer_add_event("exchange_free_aux", - exchange_free_aux, exchange, &expiration); - if (!exchange->death) { - /* If we don't give up we might start leaking... */ - exchange_free_aux(exchange); - return 0; - } - return exchange; -} - -struct exchange_finalization_node { - void (*first)(struct exchange *, void *, int); - void *first_arg; - void (*second)(struct exchange *, void *, int); - void *second_arg; -}; - -/* Run the finalization functions of ARG. */ -static void -exchange_run_finalizations(struct exchange *exchange, void *arg, int fail) -{ - struct exchange_finalization_node *node = arg; - - node->first(exchange, node->first_arg, fail); - node->second(exchange, node->second_arg, fail); - free(node); -} - -/* - * Add a finalization function FINALIZE with argument ARG to the tail - * of the finalization function list of EXCHANGE. - */ -static void -exchange_add_finalization(struct exchange *exchange, - void (*finalize)(struct exchange *, void *, int), void *arg) -{ - struct exchange_finalization_node *node; - - if (!finalize) - return; - - if (!exchange->finalize) { - exchange->finalize = finalize; - exchange->finalize_arg = arg; - return; - } - node = malloc(sizeof *node); - if (!node) { - log_error("exchange_add_finalization: malloc (%lu) failed", - (unsigned long)sizeof *node); - free(arg); - return; - } - node->first = exchange->finalize; - node->first_arg = exchange->finalize_arg; - node->second = finalize; - node->second_arg = arg; - exchange->finalize = exchange_run_finalizations; - exchange->finalize_arg = node; -} - -#ifdef USE_ISAKMP_CFG -static void -exchange_establish_transaction(struct exchange *exchange, void *arg, int fail) -{ - /* Establish a TRANSACTION exchange. */ - struct exchange_finalization_node *node = - (struct exchange_finalization_node *)arg; - struct sa *isakmp_sa = sa_lookup_by_name((char *) node->second_arg, 1); - - if (isakmp_sa && !fail) - exchange_establish_p2(isakmp_sa, ISAKMP_EXCH_TRANSACTION, 0, 0, - node->first, node->first_arg); - - free(node); -} -#endif /* USE_ISAKMP_CFG */ - -/* Establish a phase 1 exchange. */ -void -exchange_establish_p1(struct transport *t, u_int8_t type, u_int32_t doi, - char *name, void *args, void (*finalize)(struct exchange *, void *, int), - void *arg) -{ - struct exchange *exchange; - struct message *msg; -#ifdef USE_ISAKMP_CFG - struct conf_list *flags; - struct conf_list_node *flag; -#endif - char *tag = 0; - char *str; - - if (name) { - /* If no exchange type given, fetch from the configuration. */ - if (type == 0) { - /* - * XXX Similar code can be found in - * exchange_setup_p1. Share? - */ - - /* Find out our phase 1 mode. */ - tag = conf_get_str(name, "Configuration"); - if (!tag) { - /* Use default setting. */ - tag = CONF_DFLT_TAG_PHASE1_CONFIG; - } - /* Figure out the DOI. XXX Factor out? */ - str = conf_get_str(tag, "DOI"); - if (!str || strcasecmp(str, "IPSEC") == 0) - doi = IPSEC_DOI_IPSEC; - else if (strcasecmp(str, "ISAKMP") == 0) - doi = ISAKMP_DOI_ISAKMP; - else { - log_print("exchange_establish_p1: " - "DOI \"%s\" unsupported", str); - return; - } - - /* What exchange type do we want? */ - str = conf_get_str(tag, "EXCHANGE_TYPE"); - if (!str) { - log_print("exchange_establish_p1: " - "no \"EXCHANGE_TYPE\" tag in [%s] section", - tag); - return; - } - type = constant_value(isakmp_exch_cst, str); - if (!type) { - log_print("exchange_setup_p1: " - "unknown exchange type %s", str); - return; - } - } - } - exchange = exchange_create(1, 1, doi, type); - if (!exchange) { - /* XXX Do something here? */ - return; - } - if (name) { - exchange->name = strdup(name); - if (!exchange->name) { - log_error("exchange_establish_p1: " - "strdup (\"%s\") failed", name); - exchange_free(exchange); - return; - } - } - exchange->policy = name ? conf_get_str(name, "Configuration") : 0; - if (!exchange->policy && name) - exchange->policy = CONF_DFLT_TAG_PHASE1_CONFIG; - -#ifdef USE_ISAKMP_CFG - if (name && (flags = conf_get_list(name, "Flags")) != NULL) { - for (flag = TAILQ_FIRST(&flags->fields); flag; - flag = TAILQ_NEXT(flag, link)) - if (strcasecmp(flag->field, "ikecfg") == 0) { - struct exchange_finalization_node *node; - - node = calloc(1, (unsigned long)sizeof *node); - if (!node) { - log_print("exchange_establish_p1: " - "calloc (1, %lu) failed", - (unsigned long)sizeof(*node)); - exchange_free(exchange); - return; - } - /* - * Insert this finalization inbetween - * the original. - */ - node->first = finalize; - node->first_arg = arg; - node->second_arg = name; - exchange_add_finalization(exchange, - exchange_establish_transaction, - node); - finalize = 0; - } - conf_free_list(flags); - } -#endif /* USE_ISAKMP_CFG */ - - exchange_add_finalization(exchange, finalize, arg); - cookie_gen(t, exchange, exchange->cookies, ISAKMP_HDR_ICOOKIE_LEN); - exchange_enter(exchange); -#ifdef USE_DEBUG - exchange_dump("exchange_establish_p1", exchange); -#endif - - msg = message_alloc(t, 0, ISAKMP_HDR_SZ); - if (!msg) { - log_print("exchange_establish_p1: message_alloc () failed"); - exchange_free(exchange); - return; - } - msg->exchange = exchange; - - /* Do not create SA for an information or transaction exchange. */ - if (exchange->type != ISAKMP_EXCH_INFO - && exchange->type != ISAKMP_EXCH_TRANSACTION) { - /* - * Don't install a transport into this SA as it will be an - * INADDR_ANY address in the local end, which is not good at - * all. Let the reply packet install the transport instead. - */ - sa_create(exchange, 0); - msg->isakmp_sa = TAILQ_FIRST(&exchange->sa_list); - if (!msg->isakmp_sa) { - /* XXX Do something more here? */ - message_free(msg); - exchange_free(exchange); - return; - } - sa_reference(msg->isakmp_sa); - } - msg->extra = args; - - exchange_run(msg); -} - -/* Establish a phase 2 exchange. XXX With just one SA for now. */ -void -exchange_establish_p2(struct sa *isakmp_sa, u_int8_t type, char *name, - void *args, void (*finalize)(struct exchange *, void *, int), void *arg) -{ - struct exchange *exchange; - struct message *msg; - u_int32_t doi = ISAKMP_DOI_ISAKMP; - u_int32_t seq = 0; - int i; - char *tag, *str; - - if (isakmp_sa) - doi = isakmp_sa->doi->id; - - if (name) { - /* Find out our phase 2 modes. */ - tag = conf_get_str(name, "Configuration"); - if (!tag) { - log_print("exchange_establish_p2: " - "no configuration for peer \"%s\"", name); - return; - } - seq = (u_int32_t)conf_get_num(name, "Acquire-ID", 0); - - /* Figure out the DOI. */ - str = conf_get_str(tag, "DOI"); - if (!str || strcasecmp(str, "IPSEC") == 0) - doi = IPSEC_DOI_IPSEC; - else if (strcasecmp(str, "ISAKMP") == 0) - doi = ISAKMP_DOI_ISAKMP; - else { - log_print("exchange_establish_p2: " - "DOI \"%s\" unsupported", str); - return; - } - - /* What exchange type do we want? */ - if (!type) { - str = conf_get_str(tag, "EXCHANGE_TYPE"); - if (!str) { - log_print("exchange_establish_p2: " - "no \"EXCHANGE_TYPE\" tag in [%s] section", - tag); - return; - } - /* XXX IKE dependent. */ - type = constant_value(ike_exch_cst, str); - if (!type) { - log_print("exchange_establish_p2: unknown " - "exchange type %s", str); - return; - } - } - } - exchange = exchange_create(2, 1, doi, type); - if (!exchange) { - /* XXX Do something here? */ - return; - } - if (name) { - exchange->name = strdup(name); - if (!exchange->name) { - log_error("exchange_establish_p2: " - "strdup (\"%s\") failed", name); - exchange_free(exchange); - return; - } - } - exchange->policy = name ? conf_get_str(name, "Configuration") : 0; - exchange->finalize = finalize; - exchange->finalize_arg = arg; - exchange->seq = seq; - memcpy(exchange->cookies, isakmp_sa->cookies, ISAKMP_HDR_COOKIES_LEN); - getrandom(exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); - exchange->flags |= EXCHANGE_FLAG_ENCRYPT; -#if defined (USE_NAT_TRAVERSAL) - if (isakmp_sa->flags & SA_FLAG_NAT_T_ENABLE) - exchange->flags |= EXCHANGE_FLAG_NAT_T_ENABLE; - if (isakmp_sa->flags & SA_FLAG_NAT_T_KEEPALIVE) - exchange->flags |= EXCHANGE_FLAG_NAT_T_KEEPALIVE; -#endif - exchange_enter(exchange); -#ifdef USE_DEBUG - exchange_dump("exchange_establish_p2", exchange); -#endif - - /* - * Do not create SA's for informational exchanges. - * XXX How to handle new group mode? - */ - if (exchange->type != ISAKMP_EXCH_INFO && - exchange->type != ISAKMP_EXCH_TRANSACTION) { - /* XXX Number of SAs should come from the args structure. */ - for (i = 0; i < 1; i++) - if (sa_create(exchange, isakmp_sa->transport)) { - exchange_free(exchange); - return; - } - } - msg = message_alloc(isakmp_sa->transport, 0, ISAKMP_HDR_SZ); - msg->isakmp_sa = isakmp_sa; - sa_reference(isakmp_sa); - - msg->extra = args; - - /* This needs to be done late or else get_keystate won't work right. */ - msg->exchange = exchange; - - exchange_run(msg); -} - -/* Out of an incoming phase 1 message, setup an exchange. */ -struct exchange * -exchange_setup_p1(struct message *msg, u_int32_t doi) -{ - struct transport *t = msg->transport; - struct exchange *exchange; - struct sockaddr *dst; -#ifdef USE_ISAKMP_CFG - struct conf_list *flags; - struct conf_list_node *flag; -#endif - char *name = 0, *policy = 0, *str; - u_int32_t want_doi; - u_int8_t type; - - /* XXX Similar code can be found in exchange_establish_p1. Share? */ - - /* - * Unless this is an informational exchange, look up our policy for - * this peer. - */ - type = GET_ISAKMP_HDR_EXCH_TYPE(msg->iov[0].iov_base); - if (type != ISAKMP_EXCH_INFO) { - /* - * Find out our inbound phase 1 mode. - */ - t->vtbl->get_dst(t, &dst); - if (sockaddr2text(dst, &str, 0) == -1) - return 0; - name = conf_get_str("Phase 1", str); - free(str); - if (name) { - /* - * If another phase 1 exchange is ongoing don't bother - * returning the call. However, we will need to - * continue responding if our phase 1 exchange is - * still waiting for step 1 (i.e still half-open). - */ - if (exchange_lookup_active(name, 1)) - return 0; - } else { - name = conf_get_str("Phase 1", "Default"); - if (!name) { - log_print("exchange_setup_p1: no \"Default\" " - "tag in [Phase 1] section"); - return 0; - } - } - - policy = conf_get_str(name, "Configuration"); - if (!policy) - policy = CONF_DFLT_TAG_PHASE1_CONFIG; - - /* Figure out the DOI. */ - str = conf_get_str(policy, "DOI"); - if (!str || strcasecmp(str, "IPSEC") == 0) - want_doi = IPSEC_DOI_IPSEC; - else if (strcasecmp(str, "ISAKMP") == 0) - want_doi = ISAKMP_DOI_ISAKMP; - else { - log_print("exchange_setup_p1: " - "DOI \"%s\" unsupported", str); - return 0; - } - if (want_doi != doi) { - /* XXX Should I tell what DOI I got? */ - log_print("exchange_setup_p1: expected %s DOI", str); - return 0; - } - /* What exchange type do we want? */ - str = conf_get_str(policy, "EXCHANGE_TYPE"); - if (!str) { - log_print("exchange_setup_p1: no \"EXCHANGE_TYPE\" " - "tag in [%s] section", policy); - return 0; - } - type = constant_value(isakmp_exch_cst, str); - if (!type) { - log_print("exchange_setup_p1: " - "unknown exchange type %s", str); - return 0; - } - if (type != GET_ISAKMP_HDR_EXCH_TYPE(msg->iov[0].iov_base)) { - log_print("exchange_setup_p1: " - "expected exchange type %s got %s", str, - constant_name(isakmp_exch_cst, - GET_ISAKMP_HDR_EXCH_TYPE(msg->iov[0].iov_base))); - return 0; - } - } - exchange = exchange_create(1, 0, doi, type); - if (!exchange) - return 0; - - exchange->name = name ? strdup(name) : 0; - if (name && !exchange->name) { - log_error("exchange_setup_p1: strdup (\"%s\") failed", name); - exchange_free(exchange); - return 0; - } - exchange->policy = policy; - -#ifdef USE_ISAKMP_CFG - if (name && (flags = conf_get_list(name, "Flags")) != NULL) { - for (flag = TAILQ_FIRST(&flags->fields); flag; - flag = TAILQ_NEXT(flag, link)) - if (strcasecmp(flag->field, "ikecfg") == 0) { - struct exchange_finalization_node *node; - - node = calloc(1, (unsigned long)sizeof *node); - if (!node) { - log_print("exchange_establish_p1: " - "calloc (1, %lu) failed", - (unsigned long)sizeof(*node)); - exchange_free(exchange); - return 0; - } - /* - * Insert this finalization inbetween - * the original. - */ - node->first = 0; - node->first_arg = 0; - node->second_arg = name; - exchange_add_finalization(exchange, - exchange_establish_transaction, - node); - } - conf_free_list(flags); - } -#endif - - cookie_gen(msg->transport, exchange, exchange->cookies + - ISAKMP_HDR_ICOOKIE_LEN, ISAKMP_HDR_RCOOKIE_LEN); - GET_ISAKMP_HDR_ICOOKIE(msg->iov[0].iov_base, exchange->cookies); - exchange_enter(exchange); -#ifdef USE_DEBUG - exchange_dump("exchange_setup_p1", exchange); -#endif - return exchange; -} - -/* Out of an incoming phase 2 message, setup an exchange. */ -struct exchange * -exchange_setup_p2(struct message *msg, u_int8_t doi) -{ - struct exchange *exchange; - u_int8_t *buf = msg->iov[0].iov_base; - - exchange = exchange_create(2, 0, doi, GET_ISAKMP_HDR_EXCH_TYPE(buf)); - if (!exchange) - return 0; - GET_ISAKMP_HDR_ICOOKIE(buf, exchange->cookies); - GET_ISAKMP_HDR_RCOOKIE(buf, - exchange->cookies + ISAKMP_HDR_ICOOKIE_LEN); - GET_ISAKMP_HDR_MESSAGE_ID(buf, exchange->message_id); -#if defined (USE_NAT_TRAVERSAL) - if (msg->isakmp_sa->flags & SA_FLAG_NAT_T_ENABLE) - exchange->flags |= EXCHANGE_FLAG_NAT_T_ENABLE; - if (msg->isakmp_sa->flags & SA_FLAG_NAT_T_KEEPALIVE) - exchange->flags |= EXCHANGE_FLAG_NAT_T_KEEPALIVE; -#endif - exchange_enter(exchange); -#ifdef USE_DEBUG - exchange_dump("exchange_setup_p2", exchange); -#endif - return exchange; -} - -/* Dump interesting data about an exchange. */ -static void -exchange_dump_real(char *header, struct exchange *exchange, int class, - int level) -{ - struct sa *sa; - char buf[LOG_SIZE]; - /* Don't risk overflowing the final log buffer. */ - size_t bufsize_max = LOG_SIZE - strlen(header) - 32; - - LOG_DBG((class, level, - "%s: %p %s %s policy %s phase %d doi %d exchange %d step %d", - header, exchange, exchange->name ? exchange->name : "<unnamed>", - exchange->policy ? exchange->policy : "<no policy>", - exchange->initiator ? "initiator" : "responder", exchange->phase, - exchange->doi->id, exchange->type, exchange->step)); - LOG_DBG((class, level, "%s: icookie %08x%08x rcookie %08x%08x", header, - decode_32(exchange->cookies), decode_32(exchange->cookies + 4), - decode_32(exchange->cookies + 8), - decode_32(exchange->cookies + 12))); - - /* Include phase 2 SA list for this exchange */ - if (exchange->phase == 2) { - snprintf(buf, bufsize_max, "sa_list "); - for (sa = TAILQ_FIRST(&exchange->sa_list); - sa && strlen(buf) < bufsize_max; sa = TAILQ_NEXT(sa, next)) - snprintf(buf + strlen(buf), bufsize_max - strlen(buf), - "%p ", sa); - if (sa) - strlcat(buf, "...", bufsize_max); - } else - buf[0] = '\0'; - - LOG_DBG((class, level, "%s: msgid %08x %s", header, - decode_32(exchange->message_id), buf)); -} - -#ifdef USE_DEBUG -static void -exchange_dump(char *header, struct exchange *exchange) -{ - exchange_dump_real(header, exchange, LOG_EXCHANGE, 10); -} -#endif - -void -exchange_report(void) -{ - struct exchange *exchange; - int i; - - for (i = 0; i <= bucket_mask; i++) - for (exchange = LIST_FIRST(&exchange_tab[i]); exchange; - exchange = LIST_NEXT(exchange, link)) - exchange_dump_real("exchange_report", exchange, - LOG_REPORT, 0); -} - -/* - * Release all resources this exchange is using *except* for the "death" - * event. When removing an exchange from the expiration handler that event - * will be dealt with therein instead. - */ -static void -exchange_free_aux(void *v_exch) -{ - struct exchange *exchange = v_exch; - struct sa *sa, *next_sa; - struct cert_handler *handler; - - LOG_DBG((LOG_EXCHANGE, 80, "exchange_free_aux: freeing exchange %p", - exchange)); - - if (exchange->last_received) - message_free(exchange->last_received); - if (exchange->last_sent) - message_free(exchange->last_sent); - if (exchange->in_transit && - exchange->in_transit != exchange->last_sent) - message_free(exchange->in_transit); - if (exchange->nonce_i) - free(exchange->nonce_i); - if (exchange->nonce_r) - free(exchange->nonce_r); - if (exchange->id_i) - free(exchange->id_i); - if (exchange->id_r) - free(exchange->id_r); - if (exchange->keystate) - free(exchange->keystate); - if (exchange->doi && exchange->doi->free_exchange_data) - exchange->doi->free_exchange_data(exchange->data); - if (exchange->data) - free(exchange->data); - if (exchange->name) - free(exchange->name); - if (exchange->recv_cert) { - handler = cert_get(exchange->recv_certtype); - if (handler) - handler->cert_free(exchange->recv_cert); - } - if (exchange->sent_cert) { - handler = cert_get(exchange->sent_certtype); - if (handler) - handler->cert_free(exchange->sent_cert); - } - if (exchange->recv_key) - key_free(exchange->recv_keytype, ISAKMP_KEYTYPE_PUBLIC, - exchange->recv_key); - if (exchange->keynote_key) - free(exchange->keynote_key); /* This is just a string */ - -#if defined (POLICY) || defined (KEYNOTE) - if (exchange->policy_id != -1) - kn_close(exchange->policy_id); -#endif - - exchange_free_aca_list(exchange); - LIST_REMOVE(exchange, link); - - /* Tell potential finalize routine we never got there. */ - if (exchange->finalize) - exchange->finalize(exchange, exchange->finalize_arg, 1); - - /* Remove any SAs that have not been disassociated from us. */ - for (sa = TAILQ_FIRST(&exchange->sa_list); sa; sa = next_sa) { - next_sa = TAILQ_NEXT(sa, next); - /* One for the reference in exchange->sa_list. */ - sa_release(sa); - /* And two more for the expiration and SA linked list. */ - sa_free(sa); - } - - free(exchange); -} - -/* Release all resources this exchange is using. */ -void -exchange_free(struct exchange *exchange) -{ - if (exchange->death) - timer_remove_event(exchange->death); - exchange_free_aux(exchange); -} - -/* - * Upgrade the phase 1 exchange and its ISAKMP SA with the rcookie of our - * peer (found in his recently sent message MSG). - */ -void -exchange_upgrade_p1(struct message *msg) -{ - struct exchange *exchange = msg->exchange; - - LIST_REMOVE(exchange, link); - GET_ISAKMP_HDR_RCOOKIE(msg->iov[0].iov_base, exchange->cookies + - ISAKMP_HDR_ICOOKIE_LEN); - exchange_enter(exchange); - sa_isakmp_upgrade(msg); -} - -static int -exchange_check_old_sa(struct sa *sa, void *v_arg) -{ - struct sa *new_sa = v_arg; - char res1[1024]; - - if (sa == new_sa || !sa->name || !(sa->flags & SA_FLAG_READY) || - (sa->flags & SA_FLAG_REPLACED)) - return 0; - - if (sa->phase != new_sa->phase || new_sa->name == 0 || - strcasecmp(sa->name, new_sa->name)) - return 0; - - if (sa->initiator) - strlcpy(res1, ipsec_decode_ids("%s %s", sa->id_i, sa->id_i_len, - sa->id_r, sa->id_r_len, 0), sizeof res1); - else - strlcpy(res1, ipsec_decode_ids("%s %s", sa->id_r, sa->id_r_len, - sa->id_i, sa->id_i_len, 0), sizeof res1); - - LOG_DBG((LOG_EXCHANGE, 30, - "checking whether new SA replaces existing SA with IDs %s", res1)); - - if (new_sa->initiator) - return strcasecmp(res1, ipsec_decode_ids("%s %s", new_sa->id_i, - new_sa->id_i_len, new_sa->id_r, new_sa->id_r_len, 0)) == 0; - else - return strcasecmp(res1, ipsec_decode_ids("%s %s", new_sa->id_r, - new_sa->id_r_len, new_sa->id_i, new_sa->id_i_len, 0)) == 0; -} - -void -exchange_finalize(struct message *msg) -{ - struct exchange *exchange = msg->exchange; - struct sa *sa, *old_sa; - struct proto *proto; - struct conf_list *attrs; - struct conf_list_node *attr; - struct cert_handler *handler; - int i; - char *id_doi, *id_trp; - -#ifdef USE_DEBUG - exchange_dump("exchange_finalize", exchange); -#endif - - /* Copy the ID from phase 1 to exchange or phase 2 SA. */ - if (msg->isakmp_sa) { - if (exchange->id_i && exchange->id_r) { - ipsec_clone_id(&msg->isakmp_sa->id_i, - &msg->isakmp_sa->id_i_len, exchange->id_i, - exchange->id_i_len); - ipsec_clone_id(&msg->isakmp_sa->id_r, - &msg->isakmp_sa->id_r_len, exchange->id_r, - exchange->id_r_len); - } else if (msg->isakmp_sa->id_i && msg->isakmp_sa->id_r) { - ipsec_clone_id(&exchange->id_i, &exchange->id_i_len, - msg->isakmp_sa->id_i, msg->isakmp_sa->id_i_len); - ipsec_clone_id(&exchange->id_r, &exchange->id_r_len, - msg->isakmp_sa->id_r, msg->isakmp_sa->id_r_len); - } - } - /* - * Walk over all the SAs and noting them as ready. If we set the - * COMMIT bit, tell the peer each SA is connected. - * - * XXX The decision should really be based on if a SA was installed - * successfully. - */ - for (sa = TAILQ_FIRST(&exchange->sa_list); sa; - sa = TAILQ_NEXT(sa, next)) { - /* Move over the name to the SA. */ - sa->name = exchange->name ? strdup(exchange->name) : 0; - - if (exchange->flags & EXCHANGE_FLAG_I_COMMITTED) { - for (proto = TAILQ_FIRST(&sa->protos); proto; - proto = TAILQ_NEXT(proto, link)) - for (i = 0; i < 2; i++) - message_send_notification(exchange->last_received, - msg->isakmp_sa, - ISAKMP_NOTIFY_STATUS_CONNECTED, - proto, i); - } - /* - * Locate any old SAs and mark them replaced - * (SA_FLAG_REPLACED). - */ - sa->initiator = exchange->initiator; - while ((old_sa = sa_find(exchange_check_old_sa, sa)) != 0) - sa_mark_replaced(old_sa); - - /* Setup the SA flags. */ - sa->flags |= SA_FLAG_READY; - if (exchange->name) { - attrs = conf_get_list(exchange->name, "Flags"); - if (attrs) { - for (attr = TAILQ_FIRST(&attrs->fields); attr; - attr = TAILQ_NEXT(attr, link)) - sa->flags |= sa_flag(attr->field); - conf_free_list(attrs); - } - /* 'Connections' should stay alive. */ - if (connection_exist(exchange->name)) { - sa->flags |= SA_FLAG_STAYALIVE; - - /* - * ISAKMP SA of this connection should also - * stay alive. - */ - if (exchange->phase == 2 && msg->isakmp_sa) - msg->isakmp_sa->flags |= - SA_FLAG_STAYALIVE; - } - } - sa->seq = exchange->seq; - sa->exch_type = exchange->type; - } - - /* - * If this was an phase 1 SA negotiation, save the keystate in the - * ISAKMP SA structure for future initialization of phase 2 exchanges' - * keystates. Also save the Phase 1 ID and authentication - * information. - */ - if (exchange->phase == 1 && msg->isakmp_sa) { - msg->isakmp_sa->keystate = exchange->keystate; - exchange->keystate = 0; - - msg->isakmp_sa->recv_certtype = exchange->recv_certtype; - msg->isakmp_sa->sent_certtype = exchange->sent_certtype; - msg->isakmp_sa->recv_keytype = exchange->recv_keytype; - msg->isakmp_sa->recv_key = exchange->recv_key; - msg->isakmp_sa->keynote_key = exchange->keynote_key; - /* Reset. */ - exchange->recv_key = 0; - exchange->keynote_key = 0; - msg->isakmp_sa->policy_id = exchange->policy_id; - exchange->policy_id = -1; - msg->isakmp_sa->initiator = exchange->initiator; - - if (exchange->recv_certtype && exchange->recv_cert) { - handler = cert_get(exchange->recv_certtype); - if (handler) - msg->isakmp_sa->recv_cert = - handler->cert_dup(exchange->recv_cert); - } - if (exchange->sent_certtype) { - handler = cert_get(exchange->sent_certtype); - if (handler) - msg->isakmp_sa->sent_cert = - handler->cert_dup(exchange->sent_cert); - } - if (exchange->doi) - id_doi = exchange->doi->decode_ids( - "initiator id %s, responder id %s", - exchange->id_i, exchange->id_i_len, - exchange->id_r, exchange->id_r_len, 0); - else - id_doi = "<no doi>"; - - if (msg->isakmp_sa->transport) - id_trp = - msg->isakmp_sa->transport->vtbl->decode_ids(msg->isakmp_sa->transport); - else - id_trp = "<no transport>"; - -#if defined (USE_NAT_TRAVERSAL) - if (exchange->flags & EXCHANGE_FLAG_NAT_T_ENABLE) - msg->isakmp_sa->flags |= SA_FLAG_NAT_T_ENABLE; - if (exchange->flags & EXCHANGE_FLAG_NAT_T_KEEPALIVE) - msg->isakmp_sa->flags |= SA_FLAG_NAT_T_KEEPALIVE; -#endif - - LOG_DBG((LOG_EXCHANGE, 10, - "exchange_finalize: phase 1 done: %s, %s", id_doi, - id_trp)); - - log_verbose("isakmpd: phase 1 done: %s, %s", id_doi, id_trp); - } - exchange->doi->finalize_exchange(msg); - if (exchange->finalize) - exchange->finalize(exchange, exchange->finalize_arg, 0); - exchange->finalize = 0; - - /* - * There is no reason to keep the SAs connected to us anymore, in fact - * it can hurt us if we have short lifetimes on the SAs and we try - * to call exchange_report, where the SA list will be walked and - * references to freed SAs can occur. - */ - while (TAILQ_FIRST(&exchange->sa_list)) { - sa = TAILQ_FIRST(&exchange->sa_list); - - if (exchange->id_i && exchange->id_r) { - ipsec_clone_id(&sa->id_i, &sa->id_i_len, - exchange->id_i, exchange->id_i_len); - ipsec_clone_id(&sa->id_r, &sa->id_r_len, - exchange->id_r, exchange->id_r_len); - } - TAILQ_REMOVE(&exchange->sa_list, sa, next); - sa_release(sa); - } - - /* If we have nothing to retransmit we can safely remove ourselves. */ - if (!exchange->last_sent) - exchange_free(exchange); -} - -/* Stash a nonce into the exchange data. */ -static int -exchange_nonce(struct exchange *exchange, int peer, size_t nonce_sz, - u_int8_t *buf) -{ - u_int8_t **nonce; - size_t *nonce_len; - int initiator = exchange->initiator ^ peer; - char header[32]; - - nonce = initiator ? &exchange->nonce_i : &exchange->nonce_r; - nonce_len = - initiator ? &exchange->nonce_i_len : &exchange->nonce_r_len; - *nonce_len = nonce_sz; - *nonce = malloc(nonce_sz); - if (!*nonce) { - log_error("exchange_nonce: malloc (%lu) failed", - (unsigned long)nonce_sz); - return -1; - } - memcpy(*nonce, buf, nonce_sz); - snprintf(header, sizeof header, "exchange_nonce: NONCE_%c", - initiator ? 'i' : 'r'); - LOG_DBG_BUF((LOG_EXCHANGE, 80, header, *nonce, nonce_sz)); - return 0; -} - -/* Generate our NONCE. */ -int -exchange_gen_nonce(struct message *msg, size_t nonce_sz) -{ - struct exchange *exchange = msg->exchange; - u_int8_t *buf; - - buf = malloc(ISAKMP_NONCE_SZ + nonce_sz); - if (!buf) { - log_error("exchange_gen_nonce: malloc (%lu) failed", - ISAKMP_NONCE_SZ + (unsigned long)nonce_sz); - return -1; - } - getrandom(buf + ISAKMP_NONCE_DATA_OFF, nonce_sz); - if (message_add_payload(msg, ISAKMP_PAYLOAD_NONCE, buf, - ISAKMP_NONCE_SZ + nonce_sz, 1)) { - free(buf); - return -1; - } - return exchange_nonce(exchange, 0, nonce_sz, - buf + ISAKMP_NONCE_DATA_OFF); -} - -/* Save the peer's NONCE. */ -int -exchange_save_nonce(struct message *msg) -{ - struct payload *noncep; - struct exchange *exchange = msg->exchange; - - noncep = payload_first(msg, ISAKMP_PAYLOAD_NONCE); - noncep->flags |= PL_MARK; - return exchange_nonce(exchange, 1, GET_ISAKMP_GEN_LENGTH(noncep->p) - - ISAKMP_NONCE_DATA_OFF, noncep->p + ISAKMP_NONCE_DATA_OFF); -} - -/* Save the peer's CERT REQuests. */ -int -exchange_save_certreq(struct message *msg) -{ - struct payload *cp = payload_first(msg, ISAKMP_PAYLOAD_CERT_REQ); - struct exchange *exchange = msg->exchange; - struct certreq_aca *aca; - - for (; cp; cp = TAILQ_NEXT(cp, link)) { - cp->flags |= PL_MARK; - aca = certreq_decode(GET_ISAKMP_CERTREQ_TYPE(cp->p), cp->p + - ISAKMP_CERTREQ_AUTHORITY_OFF, GET_ISAKMP_GEN_LENGTH(cp->p) - - ISAKMP_CERTREQ_AUTHORITY_OFF); - if (aca) - TAILQ_INSERT_TAIL(&exchange->aca_list, aca, link); - } - - return 0; -} - -/* Free the list of pending CERTREQs. */ -void -exchange_free_aca_list(struct exchange *exchange) -{ - struct certreq_aca *aca; - - for (aca = TAILQ_FIRST(&exchange->aca_list); aca; - aca = TAILQ_FIRST(&exchange->aca_list)) { - if (aca->data) { - if (aca->handler) - aca->handler->free_aca(aca->data); - free(aca->data); - } - TAILQ_REMOVE(&exchange->aca_list, aca, link); - free(aca); - } -} - -/* Obtain certificates from acceptable certification authority. */ -int -exchange_add_certs(struct message *msg) -{ - struct exchange *exchange = msg->exchange; - struct certreq_aca *aca; - u_int8_t *cert = 0, *new_cert = 0; - u_int32_t certlen; - u_int8_t *id; - size_t id_len; - - id = exchange->initiator ? exchange->id_r : exchange->id_i; - id_len = exchange->initiator ? exchange->id_r_len : exchange->id_i_len; - - /* - * Without IDs we cannot handle this yet. Keep the aca_list around for - * a later step/retry to see if we got the ID by then. - * Note: A 'return -1' breaks X509-auth interop in the responder case - * with some IPsec clients that send CERTREQs early (such as - * the SSH Sentinel). - */ - if (!id) - return 0; - - for (aca = TAILQ_FIRST(&exchange->aca_list); aca; - aca = TAILQ_NEXT(aca, link)) { - /* XXX? If we can not satisfy a CERTREQ we drop the message. */ - if (!aca->handler->cert_obtain(id, id_len, aca->data, &cert, - &certlen)) { - log_print("exchange_add_certs: could not obtain cert " - "for a type %d cert request", aca->id); - if (cert) - free(cert); - return -1; - } - new_cert = realloc(cert, ISAKMP_CERT_SZ + certlen); - if (!new_cert) { - log_error("exchange_add_certs: realloc (%p, %d) " - "failed", cert, ISAKMP_CERT_SZ + certlen); - if (cert) - free(cert); - return -1; - } - cert = new_cert; - memmove(cert + ISAKMP_CERT_DATA_OFF, cert, certlen); - SET_ISAKMP_CERT_ENCODING(cert, aca->id); - if (message_add_payload(msg, ISAKMP_PAYLOAD_CERT, cert, - ISAKMP_CERT_SZ + certlen, 1)) { - free(cert); - return -1; - } - } - - /* We dont need the CERT REQs any more, they are answered. */ - exchange_free_aca_list(exchange); - - return 0; -} - -static void -exchange_establish_finalize(struct exchange *exchange, void *arg, int fail) -{ - char *name = arg; - - LOG_DBG((LOG_EXCHANGE, 20, "exchange_establish_finalize: " - "finalizing exchange %p with arg %p (%s) & fail = %d", - exchange, arg, name ? name : "<unnamed>", fail)); - - if (!fail) - exchange_establish(name, 0, 0); - free(name); -} - -/* - * Establish an exchange named NAME, and record the FINALIZE function - * taking ARG as an argument to be run after the exchange is ready. - */ -void -exchange_establish(char *name, void (*finalize)(struct exchange *, void *, - int), void *arg) -{ - struct transport *transport; - struct sa *isakmp_sa; - struct exchange *exchange; - int phase; - char *trpt, *peer; - - phase = conf_get_num(name, "Phase", 0); - - /* - * First of all, never try to establish anything if another exchange - * of the same kind is running. - */ - exchange = exchange_lookup_by_name(name, phase); - if (exchange) { - LOG_DBG((LOG_EXCHANGE, 40, - "exchange_establish: %s exchange already exists as %p", - name, exchange)); - exchange_add_finalization(exchange, finalize, arg); - return; - } - switch (phase) { - case 1: - trpt = conf_get_str(name, "Transport"); - if (!trpt) { - /* Phase 1 transport defaults to "udp". */ - trpt = ISAKMP_DEFAULT_TRANSPORT; - } - transport = transport_create(trpt, name); - if (!transport) { - log_print("exchange_establish: transport \"%s\" for " - "peer \"%s\" could not be created", trpt, name); - return; - } - exchange_establish_p1(transport, 0, 0, name, 0, finalize, arg); - break; - - case 2: - peer = conf_get_str(name, "ISAKMP-peer"); - if (!peer) { - log_print("exchange_establish: No ISAKMP-peer given " - "for \"%s\"", name); - return; - } - isakmp_sa = sa_lookup_by_name(peer, 1); - if (!isakmp_sa) { - name = strdup(name); - if (!name) { - log_error("exchange_establish: " - "strdup (\"%s\") failed", name); - return; - } - if (conf_get_num(peer, "Phase", 0) != 1) { - log_print("exchange_establish: " - "[%s]:ISAKMP-peer's (%s) phase is not 1", - name, peer); - return; - } - /* - * XXX We're losing information here (what the - * original finalize routine was. As a result, if an - * exchange does not manage to get through, there may - * be application-specific information that won't get - * cleaned up, since no error signalling will be done. - * This is the case with dynamic SAs and PFKEY. - */ - exchange_establish(peer, exchange_establish_finalize, - name); - exchange = exchange_lookup_by_name(peer, 1); - /* - * If the exchange was correctly initialized, add the - * original finalization routine; otherwise, call it - * directly. - */ - if (exchange) - exchange_add_finalization(exchange, finalize, - arg); - else - finalize(0, arg, 1); /* Indicate failure */ - return; - } else - exchange_establish_p2(isakmp_sa, 0, name, 0, finalize, - arg); - break; - - default: - log_print("exchange_establish: " - "peer \"%s\" does not have a correct phase (%d)", - name, phase); - break; - } -} |