diff options
author | Othmar Gsenger <otti@anytun.org> | 2008-05-25 09:50:42 +0000 |
---|---|---|
committer | Othmar Gsenger <otti@anytun.org> | 2008-05-25 09:50:42 +0000 |
commit | 71da41451212389bea25d67bc5da696b6d194bff (patch) | |
tree | a3b20decbd8bc9e47640af5fa4b39f731477955a /keyexchange/isakmpd-20041012/ipsec.c | |
parent | improved presentation again (diff) |
moved keyexchange to http://anytun.org/svn/keyexchange
Diffstat (limited to 'keyexchange/isakmpd-20041012/ipsec.c')
-rw-r--r-- | keyexchange/isakmpd-20041012/ipsec.c | 2523 |
1 files changed, 0 insertions, 2523 deletions
diff --git a/keyexchange/isakmpd-20041012/ipsec.c b/keyexchange/isakmpd-20041012/ipsec.c deleted file mode 100644 index 46cb8d9..0000000 --- a/keyexchange/isakmpd-20041012/ipsec.c +++ /dev/null @@ -1,2523 +0,0 @@ -/* $OpenBSD: ipsec.c,v 1.104 2004/09/17 13:53:08 ho Exp $ */ -/* $EOM: ipsec.c,v 1.143 2000/12/11 23:57:42 niklas Exp $ */ - -/* - * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved. - * Copyright (c) 2001 Angelos D. Keromytis. All rights reserved. - * Copyright (c) 2001 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 <netdb.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <stdlib.h> -#include <string.h> - -#include "sysdep.h" - -#include "attribute.h" -#include "conf.h" -#include "constants.h" -#include "crypto.h" -#include "dh.h" -#include "doi.h" -#if defined (USE_DPD) -#include "dpd.h" -#endif -#include "exchange.h" -#include "hash.h" -#include "ike_aggressive.h" -#include "ike_auth.h" -#include "ike_main_mode.h" -#include "ike_quick_mode.h" -#include "ipsec.h" -#include "ipsec_doi.h" -#include "isakmp.h" -#include "isakmp_cfg.h" -#include "isakmp_fld.h" -#include "isakmp_num.h" -#include "log.h" -#include "math_group.h" -#include "message.h" -#if defined (USE_NAT_TRAVERSAL) -#include "nat_traversal.h" -#endif -#include "prf.h" -#include "sa.h" -#include "timer.h" -#include "transport.h" -#include "util.h" -#ifdef USE_X509 -#include "x509.h" -#endif - -extern int acquire_only; - -/* Backwards compatibility. */ -#ifndef NI_MAXHOST -#define NI_MAXHOST 1025 -#endif - -/* The replay window size used for all IPsec protocols if not overridden. */ -#define DEFAULT_REPLAY_WINDOW 16 - -struct ipsec_decode_arg { - struct message *msg; - struct sa *sa; - struct proto *proto; -}; - -/* These variables hold the contacted peers ADT state. */ -struct contact { - struct sockaddr *addr; - socklen_t len; -} *contacts = 0; -int contact_cnt = 0, contact_limit = 0; - -static int addr_cmp(const void *, const void *); -static int ipsec_add_contact(struct message *); -static int ipsec_contacted(struct message *); -#ifdef USE_DEBUG -static int ipsec_debug_attribute(u_int16_t, u_int8_t *, u_int16_t, - void *); -#endif -static void ipsec_delete_spi(struct sa *, struct proto *, int); -static int16_t *ipsec_exchange_script(u_int8_t); -static void ipsec_finalize_exchange(struct message *); -static void ipsec_free_exchange_data(void *); -static void ipsec_free_proto_data(void *); -static void ipsec_free_sa_data(void *); -static struct keystate *ipsec_get_keystate(struct message *); -static u_int8_t *ipsec_get_spi(size_t *, u_int8_t, struct message *); -static int ipsec_handle_leftover_payload(struct message *, u_int8_t, - struct payload *); -static int ipsec_informational_post_hook(struct message *); -static int ipsec_informational_pre_hook(struct message *); -static int ipsec_initiator(struct message *); -static void ipsec_proto_init(struct proto *, char *); -static int ipsec_responder(struct message *); -static void ipsec_setup_situation(u_int8_t *); -static int ipsec_set_network(u_int8_t *, u_int8_t *, struct ipsec_sa *); -static size_t ipsec_situation_size(void); -static u_int8_t ipsec_spi_size(u_int8_t); -static int ipsec_validate_attribute(u_int16_t, u_int8_t *, u_int16_t, - void *); -static int ipsec_validate_exchange(u_int8_t); -static int ipsec_validate_id_information(u_int8_t, u_int8_t *, u_int8_t *, - size_t, struct exchange *); -static int ipsec_validate_key_information(u_int8_t *, size_t); -static int ipsec_validate_notification(u_int16_t); -static int ipsec_validate_proto(u_int8_t); -static int ipsec_validate_situation(u_int8_t *, size_t *, size_t); -static int ipsec_validate_transform_id(u_int8_t, u_int8_t); - -static struct doi ipsec_doi = { - {0}, IPSEC_DOI_IPSEC, - sizeof(struct ipsec_exch), sizeof(struct ipsec_sa), - sizeof(struct ipsec_proto), -#ifdef USE_DEBUG - ipsec_debug_attribute, -#endif - ipsec_delete_spi, - ipsec_exchange_script, - ipsec_finalize_exchange, - ipsec_free_exchange_data, - ipsec_free_proto_data, - ipsec_free_sa_data, - ipsec_get_keystate, - ipsec_get_spi, - ipsec_handle_leftover_payload, - ipsec_informational_post_hook, - ipsec_informational_pre_hook, - ipsec_is_attribute_incompatible, - ipsec_proto_init, - ipsec_setup_situation, - ipsec_situation_size, - ipsec_spi_size, - ipsec_validate_attribute, - ipsec_validate_exchange, - ipsec_validate_id_information, - ipsec_validate_key_information, - ipsec_validate_notification, - ipsec_validate_proto, - ipsec_validate_situation, - ipsec_validate_transform_id, - ipsec_initiator, - ipsec_responder, - ipsec_decode_ids -}; - -int16_t script_quick_mode[] = { - ISAKMP_PAYLOAD_HASH, /* Initiator -> responder. */ - ISAKMP_PAYLOAD_SA, - ISAKMP_PAYLOAD_NONCE, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_HASH, /* Responder -> initiator. */ - ISAKMP_PAYLOAD_SA, - ISAKMP_PAYLOAD_NONCE, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_HASH, /* Initiator -> responder. */ - EXCHANGE_SCRIPT_END -}; - -int16_t script_new_group_mode[] = { - ISAKMP_PAYLOAD_HASH, /* Initiator -> responder. */ - ISAKMP_PAYLOAD_SA, - EXCHANGE_SCRIPT_SWITCH, - ISAKMP_PAYLOAD_HASH, /* Responder -> initiator. */ - ISAKMP_PAYLOAD_SA, - EXCHANGE_SCRIPT_END -}; - -struct dst_spi_proto_arg { - struct sockaddr *dst; - u_int32_t spi; - u_int8_t proto; -}; - -/* - * Check if SA matches what we are asking for through V_ARG. It has to - * be a finished phase 2 SA. - * if "proto" arg is 0, match any proto - */ -static int -ipsec_sa_check(struct sa *sa, void *v_arg) -{ - struct dst_spi_proto_arg *arg = v_arg; - struct proto *proto; - struct sockaddr *dst, *src; - int incoming; - - if (sa->phase != 2 || !(sa->flags & SA_FLAG_READY)) - return 0; - - sa->transport->vtbl->get_dst(sa->transport, &dst); - if (memcmp(sockaddr_addrdata(dst), sockaddr_addrdata(arg->dst), - sockaddr_addrlen(dst)) == 0) - incoming = 0; - else { - sa->transport->vtbl->get_src(sa->transport, &src); - if (memcmp(sockaddr_addrdata(src), sockaddr_addrdata(arg->dst), - sockaddr_addrlen(src)) == 0) - incoming = 1; - else - return 0; - } - - for (proto = TAILQ_FIRST(&sa->protos); proto; - proto = TAILQ_NEXT(proto, link)) - if ((arg->proto == 0 || proto->proto == arg->proto) && - memcmp(proto->spi[incoming], &arg->spi, sizeof arg->spi) - == 0) - return 1; - return 0; -} - -/* Find an SA with a "name" of DST, SPI & PROTO. */ -struct sa * -ipsec_sa_lookup(struct sockaddr *dst, u_int32_t spi, u_int8_t proto) -{ - struct dst_spi_proto_arg arg; - - arg.dst = dst; - arg.spi = spi; - arg.proto = proto; - return sa_find(ipsec_sa_check, &arg); -} - -/* - * Check if SA matches the flow of another SA in V_ARG. It has to - * be a finished non-replaced phase 2 SA. - * XXX At some point other selectors will matter here too. - */ -static int -ipsec_sa_check_flow(struct sa *sa, void *v_arg) -{ - struct sa *sa2 = v_arg; - struct ipsec_sa *isa = sa->data, *isa2 = sa2->data; - - if (sa == sa2 || sa->phase != 2 || - (sa->flags & (SA_FLAG_READY | SA_FLAG_REPLACED)) != SA_FLAG_READY) - return 0; - - if (isa->tproto != isa2->tproto || isa->sport != isa2->sport || - isa->dport != isa2->dport) - return 0; - - return isa->src_net->sa_family == isa2->src_net->sa_family && - memcmp(sockaddr_addrdata(isa->src_net), - sockaddr_addrdata(isa2->src_net), - sockaddr_addrlen(isa->src_net)) == 0 && - memcmp(sockaddr_addrdata(isa->src_mask), - sockaddr_addrdata(isa2->src_mask), - sockaddr_addrlen(isa->src_mask)) == 0 && - memcmp(sockaddr_addrdata(isa->dst_net), - sockaddr_addrdata(isa2->dst_net), - sockaddr_addrlen(isa->dst_net)) == 0 && - memcmp(sockaddr_addrdata(isa->dst_mask), - sockaddr_addrdata(isa2->dst_mask), - sockaddr_addrlen(isa->dst_mask)) == 0; -} - -/* - * Do IPsec DOI specific finalizations task for the exchange where MSG was - * the final message. - */ -static void -ipsec_finalize_exchange(struct message *msg) -{ - struct sa *isakmp_sa = msg->isakmp_sa; - struct ipsec_sa *isa; - struct exchange *exchange = msg->exchange; - struct ipsec_exch *ie = exchange->data; - struct sa *sa = 0, *old_sa; - struct proto *proto, *last_proto = 0; -#ifdef USE_DEBUG - char *addr1, *addr2, *mask1, *mask2; -#endif - - switch (exchange->phase) { - case 1: - switch (exchange->type) { - case ISAKMP_EXCH_ID_PROT: - case ISAKMP_EXCH_AGGRESSIVE: - isa = isakmp_sa->data; - isa->hash = ie->hash->type; - isa->prf_type = ie->prf_type; - isa->skeyid_len = ie->skeyid_len; - isa->skeyid_d = ie->skeyid_d; - isa->skeyid_a = ie->skeyid_a; - /* Prevents early free of SKEYID_*. */ - ie->skeyid_a = ie->skeyid_d = 0; - - /* - * If a lifetime was negotiated setup the expiration - * timers. - */ - if (isakmp_sa->seconds) - sa_setup_expirations(isakmp_sa); - -#if defined (USE_NAT_TRAVERSAL) - if (isakmp_sa->flags & SA_FLAG_NAT_T_KEEPALIVE) - nat_t_setup_keepalive(isakmp_sa); -#endif - break; - } - break; - - case 2: - switch (exchange->type) { - case IKE_EXCH_QUICK_MODE: - /* - * Tell the application(s) about the SPIs and key - * material. - */ - for (sa = TAILQ_FIRST(&exchange->sa_list); sa; - sa = TAILQ_NEXT(sa, next)) { - isa = sa->data; - - if (exchange->initiator) { - /* - * Initiator is source, responder is - * destination. - */ - if (ipsec_set_network(ie->id_ci, - ie->id_cr, isa)) { - log_print( - "ipsec_finalize_exchange: " - "ipsec_set_network " - "failed"); - return; - } - } else { - /* - * Responder is source, initiator is - * destination. - */ - if (ipsec_set_network(ie->id_cr, - ie->id_ci, isa)) { - log_print( - "ipsec_finalize_exchange: " - "ipsec_set_network " - "failed"); - return; - } - } - - for (proto = TAILQ_FIRST(&sa->protos), - last_proto = 0; proto; - proto = TAILQ_NEXT(proto, link)) { - if (sysdep_ipsec_set_spi(sa, proto, - 0, isakmp_sa) || - (last_proto && - sysdep_ipsec_group_spis(sa, - last_proto, proto, 0)) || - sysdep_ipsec_set_spi(sa, proto, - 1, isakmp_sa) || - (last_proto && - sysdep_ipsec_group_spis(sa, - last_proto, proto, 1))) - /* - * XXX Tear down this - * exchange. - */ - return; - last_proto = proto; - } - -#ifdef USE_DEBUG - if (sockaddr2text(isa->src_net, &addr1, 0)) - addr1 = 0; - if (sockaddr2text(isa->src_mask, &mask1, 0)) - mask1 = 0; - if (sockaddr2text(isa->dst_net, &addr2, 0)) - addr2 = 0; - if (sockaddr2text(isa->dst_mask, &mask2, 0)) - mask2 = 0; - - LOG_DBG((LOG_EXCHANGE, 50, - "ipsec_finalize_exchange: src %s %s " - "dst %s %s tproto %u sport %u dport %u", - addr1 ? addr1 : "<??\?>", - mask1 ? mask1 : "<??\?>", - addr2 ? addr2 : "<??\?>", - mask2 ? mask2 : "<??\?>", - isa->tproto, ntohs(isa->sport), - ntohs(isa->dport))); - - if (addr1) - free(addr1); - if (mask1) - free(mask1); - if (addr2) - free(addr2); - if (mask2) - free(mask2); - -#endif /* USE_DEBUG */ - - /* - * If this is not an SA acquired by the - * kernel, it needs to have a SPD entry - * (a.k.a. flow) set up. - */ - if (!(sa->flags & SA_FLAG_ONDEMAND || - conf_get_str("General", "Acquire-Only") - || acquire_only) - && sysdep_ipsec_enable_sa(sa, isakmp_sa)) - /* XXX Tear down this exchange. */ - return; - - /* - * Mark elder SAs with the same flow - * information as replaced. - */ - while ((old_sa = sa_find(ipsec_sa_check_flow, - sa)) != 0) - sa_mark_replaced(old_sa); - } - break; - } - } -} - -/* Set the client addresses in ISA from SRC_ID and DST_ID. */ -static int -ipsec_set_network(u_int8_t *src_id, u_int8_t *dst_id, struct ipsec_sa *isa) -{ - int id; - - /* Set source address/mask. */ - id = GET_ISAKMP_ID_TYPE(src_id); - switch (id) { - case IPSEC_ID_IPV4_ADDR: - case IPSEC_ID_IPV4_ADDR_SUBNET: - isa->src_net = (struct sockaddr *)calloc(1, - sizeof(struct sockaddr_in)); - if (!isa->src_net) - goto memfail; - isa->src_net->sa_family = AF_INET; -#ifndef USE_OLD_SOCKADDR - isa->src_net->sa_len = sizeof(struct sockaddr_in); -#endif - - isa->src_mask = (struct sockaddr *)calloc(1, - sizeof(struct sockaddr_in)); - if (!isa->src_mask) - goto memfail; - isa->src_mask->sa_family = AF_INET; -#ifndef USE_OLD_SOCKADDR - isa->src_mask->sa_len = sizeof(struct sockaddr_in); -#endif - break; - - case IPSEC_ID_IPV6_ADDR: - case IPSEC_ID_IPV6_ADDR_SUBNET: - isa->src_net = (struct sockaddr *)calloc(1, - sizeof(struct sockaddr_in6)); - if (!isa->src_net) - goto memfail; - isa->src_net->sa_family = AF_INET6; -#ifndef USE_OLD_SOCKADDR - isa->src_net->sa_len = sizeof(struct sockaddr_in6); -#endif - - isa->src_mask = (struct sockaddr *)calloc(1, - sizeof(struct sockaddr_in6)); - if (!isa->src_mask) - goto memfail; - isa->src_mask->sa_family = AF_INET6; -#ifndef USE_OLD_SOCKADDR - isa->src_mask->sa_len = sizeof(struct sockaddr_in6); -#endif - break; - - case IPSEC_ID_IPV4_RANGE: - case IPSEC_ID_IPV6_RANGE: - case IPSEC_ID_DER_ASN1_DN: - case IPSEC_ID_DER_ASN1_GN: - case IPSEC_ID_KEY_ID: - default: - log_print("ipsec_set_network: ID type %d (%s) not supported", - id, constant_name(ipsec_id_cst, id)); - return -1; - } - - /* Net */ - memcpy(sockaddr_addrdata(isa->src_net), src_id + ISAKMP_ID_DATA_OFF, - sockaddr_addrlen(isa->src_net)); - - /* Mask */ - switch (id) { - case IPSEC_ID_IPV4_ADDR: - case IPSEC_ID_IPV6_ADDR: - memset(sockaddr_addrdata(isa->src_mask), 0xff, - sockaddr_addrlen(isa->src_mask)); - break; - case IPSEC_ID_IPV4_ADDR_SUBNET: - case IPSEC_ID_IPV6_ADDR_SUBNET: - memcpy(sockaddr_addrdata(isa->src_mask), src_id + - ISAKMP_ID_DATA_OFF + sockaddr_addrlen(isa->src_net), - sockaddr_addrlen(isa->src_mask)); - break; - } - - memcpy(&isa->sport, - src_id + ISAKMP_ID_DOI_DATA_OFF + IPSEC_ID_PORT_OFF, - IPSEC_ID_PORT_LEN); - - /* Set destination address. */ - id = GET_ISAKMP_ID_TYPE(dst_id); - switch (id) { - case IPSEC_ID_IPV4_ADDR: - case IPSEC_ID_IPV4_ADDR_SUBNET: - isa->dst_net = (struct sockaddr *)calloc(1, - sizeof(struct sockaddr_in)); - if (!isa->dst_net) - goto memfail; - isa->dst_net->sa_family = AF_INET; -#ifndef USE_OLD_SOCKADDR - isa->dst_net->sa_len = sizeof(struct sockaddr_in); -#endif - - isa->dst_mask = (struct sockaddr *)calloc(1, - sizeof(struct sockaddr_in)); - if (!isa->dst_mask) - goto memfail; - isa->dst_mask->sa_family = AF_INET; -#ifndef USE_OLD_SOCKADDR - isa->dst_mask->sa_len = sizeof(struct sockaddr_in); -#endif - break; - - case IPSEC_ID_IPV6_ADDR: - case IPSEC_ID_IPV6_ADDR_SUBNET: - isa->dst_net = (struct sockaddr *)calloc(1, - sizeof(struct sockaddr_in6)); - if (!isa->dst_net) - goto memfail; - isa->dst_net->sa_family = AF_INET6; -#ifndef USE_OLD_SOCKADDR - isa->dst_net->sa_len = sizeof(struct sockaddr_in6); -#endif - - isa->dst_mask = (struct sockaddr *)calloc(1, - sizeof(struct sockaddr_in6)); - if (!isa->dst_mask) - goto memfail; - isa->dst_mask->sa_family = AF_INET6; -#ifndef USE_OLD_SOCKADDR - isa->dst_mask->sa_len = sizeof(struct sockaddr_in6); -#endif - break; - } - - /* Net */ - memcpy(sockaddr_addrdata(isa->dst_net), dst_id + ISAKMP_ID_DATA_OFF, - sockaddr_addrlen(isa->dst_net)); - - /* Mask */ - switch (id) { - case IPSEC_ID_IPV4_ADDR: - case IPSEC_ID_IPV6_ADDR: - memset(sockaddr_addrdata(isa->dst_mask), 0xff, - sockaddr_addrlen(isa->dst_mask)); - break; - case IPSEC_ID_IPV4_ADDR_SUBNET: - case IPSEC_ID_IPV6_ADDR_SUBNET: - memcpy(sockaddr_addrdata(isa->dst_mask), dst_id + - ISAKMP_ID_DATA_OFF + sockaddr_addrlen(isa->dst_net), - sockaddr_addrlen(isa->dst_mask)); - break; - } - - memcpy(&isa->tproto, dst_id + ISAKMP_ID_DOI_DATA_OFF + - IPSEC_ID_PROTO_OFF, IPSEC_ID_PROTO_LEN); - memcpy(&isa->dport, - dst_id + ISAKMP_ID_DOI_DATA_OFF + IPSEC_ID_PORT_OFF, - IPSEC_ID_PORT_LEN); - return 0; - -memfail: - log_error("ipsec_set_network: calloc () failed"); - return -1; -} - -/* Free the DOI-specific exchange data pointed to by VIE. */ -static void -ipsec_free_exchange_data(void *vie) -{ - struct ipsec_exch *ie = vie; -#ifdef USE_ISAKMP_CFG - struct isakmp_cfg_attr *attr; -#endif - - if (ie->sa_i_b) - free(ie->sa_i_b); - if (ie->id_ci) - free(ie->id_ci); - if (ie->id_cr) - free(ie->id_cr); - if (ie->g_xi) - free(ie->g_xi); - if (ie->g_xr) - free(ie->g_xr); - if (ie->g_xy) - free(ie->g_xy); - if (ie->skeyid) - free(ie->skeyid); - if (ie->skeyid_d) - free(ie->skeyid_d); - if (ie->skeyid_a) - free(ie->skeyid_a); - if (ie->skeyid_e) - free(ie->skeyid_e); - if (ie->hash_i) - free(ie->hash_i); - if (ie->hash_r) - free(ie->hash_r); - if (ie->group) - group_free(ie->group); -#ifdef USE_ISAKMP_CFG - for (attr = LIST_FIRST(&ie->attrs); attr; - attr = LIST_FIRST(&ie->attrs)) { - LIST_REMOVE(attr, link); - if (attr->length) - free(attr->value); - free(attr); - } -#endif -} - -/* Free the DOI-specific SA data pointed to by VISA. */ -static void -ipsec_free_sa_data(void *visa) -{ - struct ipsec_sa *isa = visa; - - if (isa->src_net) - free(isa->src_net); - if (isa->src_mask) - free(isa->src_mask); - if (isa->dst_net) - free(isa->dst_net); - if (isa->dst_mask) - free(isa->dst_mask); - if (isa->skeyid_a) - free(isa->skeyid_a); - if (isa->skeyid_d) - free(isa->skeyid_d); -} - -/* Free the DOI-specific protocol data of an SA pointed to by VIPROTO. */ -static void -ipsec_free_proto_data(void *viproto) -{ - struct ipsec_proto *iproto = viproto; - int i; - - for (i = 0; i < 2; i++) - if (iproto->keymat[i]) - free(iproto->keymat[i]); -} - -/* Return exchange script based on TYPE. */ -static int16_t * -ipsec_exchange_script(u_int8_t type) -{ - switch (type) { -#ifdef USE_ISAKMP_CFG - case ISAKMP_EXCH_TRANSACTION: - return script_transaction; -#endif - case IKE_EXCH_QUICK_MODE: - return script_quick_mode; - case IKE_EXCH_NEW_GROUP_MODE: - return script_new_group_mode; - } - return 0; -} - -/* Initialize this DOI, requires doi_init to already have been called. */ -void -ipsec_init(void) -{ - doi_register(&ipsec_doi); -} - -/* Given a message MSG, return a suitable IV (or rather keystate). */ -static struct keystate * -ipsec_get_keystate(struct message *msg) -{ - struct keystate *ks; - struct hash *hash; - - /* If we have already have an IV, use it. */ - if (msg->exchange && msg->exchange->keystate) { - ks = malloc(sizeof *ks); - if (!ks) { - log_error("ipsec_get_keystate: malloc (%lu) failed", - (unsigned long) sizeof *ks); - return 0; - } - memcpy(ks, msg->exchange->keystate, sizeof *ks); - return ks; - } - /* - * For phase 2 when no SA yet is setup we need to hash the IV used by - * the ISAKMP SA concatenated with the message ID, and use that as an - * IV for further cryptographic operations. - */ - if (!msg->isakmp_sa->keystate) { - log_print("ipsec_get_keystate: no keystate in ISAKMP SA %p", - msg->isakmp_sa); - return 0; - } - ks = crypto_clone_keystate(msg->isakmp_sa->keystate); - if (!ks) - return 0; - - hash = hash_get(((struct ipsec_sa *)msg->isakmp_sa->data)->hash); - hash->Init(hash->ctx); - LOG_DBG_BUF((LOG_CRYPTO, 80, "ipsec_get_keystate: final phase 1 IV", - ks->riv, ks->xf->blocksize)); - hash->Update(hash->ctx, ks->riv, ks->xf->blocksize); - LOG_DBG_BUF((LOG_CRYPTO, 80, "ipsec_get_keystate: message ID", - ((u_int8_t *) msg->iov[0].iov_base) + ISAKMP_HDR_MESSAGE_ID_OFF, - ISAKMP_HDR_MESSAGE_ID_LEN)); - hash->Update(hash->ctx, ((u_int8_t *) msg->iov[0].iov_base) + - ISAKMP_HDR_MESSAGE_ID_OFF, ISAKMP_HDR_MESSAGE_ID_LEN); - hash->Final(hash->digest, hash->ctx); - crypto_init_iv(ks, hash->digest, ks->xf->blocksize); - LOG_DBG_BUF((LOG_CRYPTO, 80, "ipsec_get_keystate: phase 2 IV", - hash->digest, ks->xf->blocksize)); - return ks; -} - -static void -ipsec_setup_situation(u_int8_t *buf) -{ - SET_IPSEC_SIT_SIT(buf + ISAKMP_SA_SIT_OFF, IPSEC_SIT_IDENTITY_ONLY); -} - -static size_t -ipsec_situation_size(void) -{ - return IPSEC_SIT_SIT_LEN; -} - -static u_int8_t -ipsec_spi_size(u_int8_t proto) -{ - return IPSEC_SPI_SIZE; -} - -static int -ipsec_validate_attribute(u_int16_t type, u_int8_t * value, u_int16_t len, - void *vmsg) -{ - struct message *msg = vmsg; - - if ((msg->exchange->phase == 1 - && (type < IKE_ATTR_ENCRYPTION_ALGORITHM - || type > IKE_ATTR_GROUP_ORDER)) - || (msg->exchange->phase == 2 - && (type < IPSEC_ATTR_SA_LIFE_TYPE - || type > IPSEC_ATTR_ECN_TUNNEL))) - return -1; - return 0; -} - -static int -ipsec_validate_exchange(u_int8_t exch) -{ - return exch != IKE_EXCH_QUICK_MODE && exch != IKE_EXCH_NEW_GROUP_MODE; -} - -static int -ipsec_validate_id_information(u_int8_t type, u_int8_t *extra, u_int8_t *buf, - size_t sz, struct exchange *exchange) -{ - u_int8_t proto = GET_IPSEC_ID_PROTO(extra); - u_int16_t port = GET_IPSEC_ID_PORT(extra); - - LOG_DBG((LOG_MESSAGE, 40, - "ipsec_validate_id_information: proto %d port %d type %d", - proto, port, type)); - if (type < IPSEC_ID_IPV4_ADDR || type > IPSEC_ID_KEY_ID) - return -1; - - switch (type) { - case IPSEC_ID_IPV4_ADDR: - LOG_DBG_BUF((LOG_MESSAGE, 40, - "ipsec_validate_id_information: IPv4", buf, - sizeof(struct in_addr))); - break; - - case IPSEC_ID_IPV6_ADDR: - LOG_DBG_BUF((LOG_MESSAGE, 40, - "ipsec_validate_id_information: IPv6", buf, - sizeof(struct in6_addr))); - break; - - case IPSEC_ID_IPV4_ADDR_SUBNET: - LOG_DBG_BUF((LOG_MESSAGE, 40, - "ipsec_validate_id_information: IPv4 network/netmask", - buf, 2 * sizeof(struct in_addr))); - break; - - case IPSEC_ID_IPV6_ADDR_SUBNET: - LOG_DBG_BUF((LOG_MESSAGE, 40, - "ipsec_validate_id_information: IPv6 network/netmask", - buf, 2 * sizeof(struct in6_addr))); - break; - - default: - break; - } - - if (exchange->phase == 1 - && (proto != IPPROTO_UDP || port != UDP_DEFAULT_PORT) - && (proto != 0 || port != 0)) { - /* - * XXX SSH's ISAKMP tester fails this test (proto 17 - port - * 0). - */ -#ifdef notyet - return -1; -#else - log_print("ipsec_validate_id_information: dubious ID " - "information accepted"); -#endif - } - /* XXX More checks? */ - - return 0; -} - -static int -ipsec_validate_key_information(u_int8_t *buf, size_t sz) -{ - /* XXX Not implemented yet. */ - return 0; -} - -static int -ipsec_validate_notification(u_int16_t type) -{ - return type < IPSEC_NOTIFY_RESPONDER_LIFETIME - || type > IPSEC_NOTIFY_INITIAL_CONTACT ? -1 : 0; -} - -static int -ipsec_validate_proto(u_int8_t proto) -{ - return proto < IPSEC_PROTO_IPSEC_AH - || proto > IPSEC_PROTO_IPCOMP ? -1 : 0; -} - -static int -ipsec_validate_situation(u_int8_t *buf, size_t *sz, size_t len) -{ - if (len < IPSEC_SIT_SIT_OFF + IPSEC_SIT_SIT_LEN) { - log_print("ipsec_validate_situation: payload too short: %u", - (unsigned int) len); - return -1; - } - /* Currently only "identity only" situations are supported. */ - if (GET_IPSEC_SIT_SIT(buf) != IPSEC_SIT_IDENTITY_ONLY) - return 1; - - *sz = IPSEC_SIT_SIT_LEN; - - return 0; -} - -static int -ipsec_validate_transform_id(u_int8_t proto, u_int8_t transform_id) -{ - switch (proto) { - /* - * As no unexpected protocols can occur, we just tie the - * default case to the first case, in orer to silence a GCC - * warning. - */ - default: - case ISAKMP_PROTO_ISAKMP: - return transform_id != IPSEC_TRANSFORM_KEY_IKE; - case IPSEC_PROTO_IPSEC_AH: - return transform_id < IPSEC_AH_MD5 - || transform_id > IPSEC_AH_DES ? -1 : 0; - case IPSEC_PROTO_IPSEC_ESP: - return transform_id < IPSEC_ESP_DES_IV64 - || (transform_id > IPSEC_ESP_AES_128_CTR - && transform_id < IPSEC_ESP_AES_MARS) - || transform_id > IPSEC_ESP_AES_TWOFISH ? -1 : 0; - case IPSEC_PROTO_IPCOMP: - return transform_id < IPSEC_IPCOMP_OUI - || transform_id > IPSEC_IPCOMP_V42BIS ? -1 : 0; - } -} - -static int -ipsec_initiator(struct message *msg) -{ - struct exchange *exchange = msg->exchange; - int (**script)(struct message *) = 0; - - /* Check that the SA is coherent with the IKE rules. */ - if (exchange->type != ISAKMP_EXCH_TRANSACTION - && ((exchange->phase == 1 && - exchange->type != ISAKMP_EXCH_ID_PROT && - exchange->type != ISAKMP_EXCH_AGGRESSIVE && - exchange->type != ISAKMP_EXCH_INFO) - || (exchange->phase == 2 && - exchange->type != IKE_EXCH_QUICK_MODE && - exchange->type != ISAKMP_EXCH_INFO))) { - log_print("ipsec_initiator: unsupported exchange type %d " - "in phase %d", exchange->type, exchange->phase); - return -1; - } - switch (exchange->type) { - case ISAKMP_EXCH_ID_PROT: - script = ike_main_mode_initiator; - break; -#ifdef USE_AGGRESSIVE - case ISAKMP_EXCH_AGGRESSIVE: - script = ike_aggressive_initiator; - break; -#endif -#ifdef USE_ISAKMP_CFG - case ISAKMP_EXCH_TRANSACTION: - script = isakmp_cfg_initiator; - break; -#endif - case ISAKMP_EXCH_INFO: - return message_send_info(msg); - case IKE_EXCH_QUICK_MODE: - script = ike_quick_mode_initiator; - break; - default: - log_print("ipsec_initiator: unsupported exchange type %d", - exchange->type); - return -1; - } - - /* Run the script code for this step. */ - if (script) - return script[exchange->step] (msg); - - return 0; -} - -/* - * delete all SA's from addr with the associated proto and SPI's - * - * spis[] is an array of SPIs of size 16-octet for proto ISAKMP - * or 4-octet otherwise. - */ -static void -ipsec_delete_spi_list(struct sockaddr *addr, u_int8_t proto, u_int8_t *spis, - int nspis, char *type) -{ - struct sa *sa; - int i; - - for (i = 0; i < nspis; i++) { - if (proto == ISAKMP_PROTO_ISAKMP) { - u_int8_t *spi = spis + i * ISAKMP_HDR_COOKIES_LEN; - - /* - * This really shouldn't happen in IPSEC DOI - * code, but Cisco VPN 3000 sends ISAKMP DELETE's - * this way. - */ - sa = sa_lookup_isakmp_sa(addr, spi); - } else { - u_int32_t spi = ((u_int32_t *)spis)[i]; - - sa = ipsec_sa_lookup(addr, spi, proto); - } - - if (sa == NULL) { - LOG_DBG((LOG_SA, 30, "ipsec_delete_spi_list: could " - "not locate SA (SPI %08x, proto %u)", - ((u_int32_t *)spis)[i], proto)); - continue; - } - /* Delete the SA and search for the next */ - LOG_DBG((LOG_SA, 30, "ipsec_delete_spi_list: " - "%s made us delete SA %p (%d references) for proto %d", - type, sa, sa->refcnt, proto)); - - sa_free(sa); - } -} - -/* - * deal with a NOTIFY of INVALID_SPI - */ -static void -ipsec_invalid_spi (struct message *msg, struct payload *p) -{ - struct sockaddr *dst; - int invspisz, off; - u_int32_t spi; - u_int16_t totsiz; - u_int8_t spisz; - - /* Any notification that make us do something should be protected */ - if(!TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH])) - { - LOG_DBG ((LOG_SA, 40, - "ipsec_invalid_spi: missing HASH payload in INVALID_SPI" - " notification")); - return; - } - - /* - * get the invalid spi out of the variable sized notification data - * field, which is after the variable sized SPI field [which specifies - * the receiving entity's phase-1 SPI, not the invalid spi] - */ - totsiz = GET_ISAKMP_GEN_LENGTH (p->p); - spisz = GET_ISAKMP_NOTIFY_SPI_SZ (p->p); - off = ISAKMP_NOTIFY_SPI_OFF + spisz; - invspisz = totsiz - off; - - if (invspisz != sizeof spi) - { - LOG_DBG ((LOG_SA, 40, - "ipsec_invalid_spi: SPI size %d in INVALID_SPI " - "payload unsupported", spisz)); - return; - } - memcpy (&spi, p->p + off, sizeof spi); - - msg->transport->vtbl->get_dst (msg->transport, &dst); - - /* delete matching SPI's from this peer */ - ipsec_delete_spi_list (dst, 0, (u_int8_t *)&spi, 1, "INVALID_SPI"); -} - -static int -ipsec_responder(struct message *msg) -{ - struct exchange *exchange = msg->exchange; - int (**script)(struct message *) = 0; - struct payload *p; - u_int16_t type; - - /* Check that a new exchange is coherent with the IKE rules. */ - if (exchange->step == 0 && exchange->type != ISAKMP_EXCH_TRANSACTION - && ((exchange->phase == 1 && - exchange->type != ISAKMP_EXCH_ID_PROT && - exchange->type != ISAKMP_EXCH_AGGRESSIVE && - exchange->type != ISAKMP_EXCH_INFO) - || (exchange->phase == 2 && - exchange->type != IKE_EXCH_QUICK_MODE && - exchange->type != ISAKMP_EXCH_INFO))) { - message_drop(msg, ISAKMP_NOTIFY_UNSUPPORTED_EXCHANGE_TYPE, - 0, 1, 0); - return -1; - } - LOG_DBG((LOG_MISC, 30, "ipsec_responder: phase %d exchange %d step %d", - exchange->phase, exchange->type, exchange->step)); - switch (exchange->type) { - case ISAKMP_EXCH_ID_PROT: - script = ike_main_mode_responder; - break; -#ifdef USE_AGGRESSIVE - case ISAKMP_EXCH_AGGRESSIVE: - script = ike_aggressive_responder; - break; -#endif -#ifdef USE_ISAKMP_CFG - case ISAKMP_EXCH_TRANSACTION: - script = isakmp_cfg_responder; - break; -#endif - case ISAKMP_EXCH_INFO: - for (p = payload_first(msg, ISAKMP_PAYLOAD_NOTIFY); p; - p = TAILQ_NEXT(p, link)) { - type = GET_ISAKMP_NOTIFY_MSG_TYPE(p->p); - LOG_DBG((LOG_EXCHANGE, 10, - "ipsec_responder: got NOTIFY of type %s", - constant_name(isakmp_notify_cst, type))); - - switch (type) { - case IPSEC_NOTIFY_INITIAL_CONTACT: - /* Handled by leftover logic. */ - break; - -#if defined (USE_DPD) - case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE: - case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE_ACK: - dpd_handle_notify(msg, p); - break; -#endif - - default: - p->flags |= PL_MARK; - break; - } - } - - /* - * If any DELETEs are in here, let the logic of leftover - * payloads deal with them. - */ - return 0; - - case IKE_EXCH_QUICK_MODE: - script = ike_quick_mode_responder; - break; - - default: - message_drop(msg, ISAKMP_NOTIFY_UNSUPPORTED_EXCHANGE_TYPE, - 0, 1, 0); - return -1; - } - - /* Run the script code for this step. */ - if (script) - return script[exchange->step] (msg); - - /* - * XXX So far we don't accept any proposals for exchanges we don't - * support. - */ - if (payload_first(msg, ISAKMP_PAYLOAD_SA)) { - message_drop(msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 1, 0); - return -1; - } - return 0; -} - -static enum hashes -from_ike_hash(u_int16_t hash) -{ - switch (hash) { - case IKE_HASH_MD5: - return HASH_MD5; - case IKE_HASH_SHA: - return HASH_SHA1; - } - return -1; -} - -static enum transform -from_ike_crypto(u_int16_t crypto) -{ - /* Coincidentally this is the null operation :-) */ - return crypto; -} - -/* - * Find out whether the attribute of type TYPE with a LEN length value - * pointed to by VALUE is incompatible with what we can handle. - * VMSG is a pointer to the current message. - */ -int -ipsec_is_attribute_incompatible(u_int16_t type, u_int8_t *value, u_int16_t len, - void *vmsg) -{ - struct message *msg = vmsg; - u_int16_t dv = decode_16(value); - - if (msg->exchange->phase == 1) { - switch (type) { - case IKE_ATTR_ENCRYPTION_ALGORITHM: - return !crypto_get(from_ike_crypto(dv)); - case IKE_ATTR_HASH_ALGORITHM: - return !hash_get(from_ike_hash(dv)); - case IKE_ATTR_AUTHENTICATION_METHOD: - return !ike_auth_get(dv); - case IKE_ATTR_GROUP_DESCRIPTION: - return (dv < IKE_GROUP_DESC_MODP_768 - || dv > IKE_GROUP_DESC_MODP_1536) - && (dv < IKE_GROUP_DESC_MODP_2048 - || dv > IKE_GROUP_DESC_MODP_8192); - case IKE_ATTR_GROUP_TYPE: - return 1; - case IKE_ATTR_GROUP_PRIME: - return 1; - case IKE_ATTR_GROUP_GENERATOR_1: - return 1; - case IKE_ATTR_GROUP_GENERATOR_2: - return 1; - case IKE_ATTR_GROUP_CURVE_A: - return 1; - case IKE_ATTR_GROUP_CURVE_B: - return 1; - case IKE_ATTR_LIFE_TYPE: - return dv < IKE_DURATION_SECONDS - || dv > IKE_DURATION_KILOBYTES; - case IKE_ATTR_LIFE_DURATION: - return len != 2 && len != 4; - case IKE_ATTR_PRF: - return 1; - case IKE_ATTR_KEY_LENGTH: - /* - * Our crypto routines only allows key-lengths which - * are multiples of an octet. - */ - return dv % 8 != 0; - case IKE_ATTR_FIELD_SIZE: - return 1; - case IKE_ATTR_GROUP_ORDER: - return 1; - } - } else { - switch (type) { - case IPSEC_ATTR_SA_LIFE_TYPE: - return dv < IPSEC_DURATION_SECONDS - || dv > IPSEC_DURATION_KILOBYTES; - case IPSEC_ATTR_SA_LIFE_DURATION: - return len != 2 && len != 4; - case IPSEC_ATTR_GROUP_DESCRIPTION: - return (dv < IKE_GROUP_DESC_MODP_768 - || dv > IKE_GROUP_DESC_MODP_1536) - && (dv < IKE_GROUP_DESC_MODP_2048 - || IKE_GROUP_DESC_MODP_8192 < dv); - case IPSEC_ATTR_ENCAPSULATION_MODE: -#if defined (USE_NAT_TRAVERSAL) - return dv != IPSEC_ENCAP_TUNNEL - && dv != IPSEC_ENCAP_TRANSPORT - && dv != IPSEC_ENCAP_UDP_ENCAP_TUNNEL - && dv != IPSEC_ENCAP_UDP_ENCAP_TRANSPORT - && dv != IPSEC_ENCAP_UDP_ENCAP_TUNNEL_DRAFT - && dv != IPSEC_ENCAP_UDP_ENCAP_TRANSPORT_DRAFT; -#else - return dv < IPSEC_ENCAP_TUNNEL - || dv > IPSEC_ENCAP_TRANSPORT; -#endif /* USE_NAT_TRAVERSAL */ - case IPSEC_ATTR_AUTHENTICATION_ALGORITHM: - return dv < IPSEC_AUTH_HMAC_MD5 - || dv > IPSEC_AUTH_HMAC_RIPEMD; - case IPSEC_ATTR_KEY_LENGTH: - /* - * XXX Blowfish needs '0'. Others appear to disregard - * this attr? - */ - return 0; - case IPSEC_ATTR_KEY_ROUNDS: - return 1; - case IPSEC_ATTR_COMPRESS_DICTIONARY_SIZE: - return 1; - case IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM: - return 1; - case IPSEC_ATTR_ECN_TUNNEL: - return 1; - } - } - /* XXX Silence gcc. */ - return 1; -} - -#ifdef USE_DEBUG -/* - * Log the attribute of TYPE with a LEN length value pointed to by VALUE - * in human-readable form. VMSG is a pointer to the current message. - */ -int -ipsec_debug_attribute(u_int16_t type, u_int8_t *value, u_int16_t len, - void *vmsg) -{ - struct message *msg = vmsg; - char val[20]; - - /* XXX Transient solution. */ - if (len == 2) - snprintf(val, sizeof val, "%d", decode_16(value)); - else if (len == 4) - snprintf(val, sizeof val, "%d", decode_32(value)); - else - snprintf(val, sizeof val, "unrepresentable"); - - LOG_DBG((LOG_MESSAGE, 50, "Attribute %s value %s", - constant_name(msg->exchange->phase == 1 ? ike_attr_cst : - ipsec_attr_cst, type), val)); - return 0; -} -#endif - -/* - * Decode the attribute of type TYPE with a LEN length value pointed to by - * VALUE. VIDA is a pointer to a context structure where we can find the - * current message, SA and protocol. - */ -int -ipsec_decode_attribute(u_int16_t type, u_int8_t *value, u_int16_t len, - void *vida) -{ - struct ipsec_decode_arg *ida = vida; - struct message *msg = ida->msg; - struct sa *sa = ida->sa; - struct ipsec_sa *isa = sa->data; - struct proto *proto = ida->proto; - struct ipsec_proto *iproto = proto->data; - struct exchange *exchange = msg->exchange; - struct ipsec_exch *ie = exchange->data; - static int lifetype = 0; - - if (exchange->phase == 1) { - switch (type) { - case IKE_ATTR_ENCRYPTION_ALGORITHM: - /* XXX Errors possible? */ - exchange->crypto = crypto_get(from_ike_crypto( - decode_16(value))); - break; - case IKE_ATTR_HASH_ALGORITHM: - /* XXX Errors possible? */ - ie->hash = hash_get(from_ike_hash(decode_16(value))); - break; - case IKE_ATTR_AUTHENTICATION_METHOD: - /* XXX Errors possible? */ - ie->ike_auth = ike_auth_get(decode_16(value)); - break; - case IKE_ATTR_GROUP_DESCRIPTION: - isa->group_desc = decode_16(value); - break; - case IKE_ATTR_GROUP_TYPE: - break; - case IKE_ATTR_GROUP_PRIME: - break; - case IKE_ATTR_GROUP_GENERATOR_1: - break; - case IKE_ATTR_GROUP_GENERATOR_2: - break; - case IKE_ATTR_GROUP_CURVE_A: - break; - case IKE_ATTR_GROUP_CURVE_B: - break; - case IKE_ATTR_LIFE_TYPE: - lifetype = decode_16(value); - return 0; - case IKE_ATTR_LIFE_DURATION: - switch (lifetype) { - case IKE_DURATION_SECONDS: - switch (len) { - case 2: - sa->seconds = decode_16(value); - break; - case 4: - sa->seconds = decode_32(value); - break; - default: - log_print("ipsec_decode_attribute: " - "unreasonable lifetime"); - } - break; - case IKE_DURATION_KILOBYTES: - switch (len) { - case 2: - sa->kilobytes = decode_16(value); - break; - case 4: - sa->kilobytes = decode_32(value); - break; - default: - log_print("ipsec_decode_attribute: " - "unreasonable lifetime"); - } - break; - default: - log_print("ipsec_decode_attribute: unknown " - "lifetime type"); - } - break; - case IKE_ATTR_PRF: - break; - case IKE_ATTR_KEY_LENGTH: - exchange->key_length = decode_16(value) / 8; - break; - case IKE_ATTR_FIELD_SIZE: - break; - case IKE_ATTR_GROUP_ORDER: - break; - } - } else { - switch (type) { - case IPSEC_ATTR_SA_LIFE_TYPE: - lifetype = decode_16(value); - return 0; - case IPSEC_ATTR_SA_LIFE_DURATION: - switch (lifetype) { - case IPSEC_DURATION_SECONDS: - switch (len) { - case 2: - sa->seconds = decode_16(value); - break; - case 4: - sa->seconds = decode_32(value); - break; - default: - log_print("ipsec_decode_attribute: " - "unreasonable lifetime"); - } - break; - case IPSEC_DURATION_KILOBYTES: - switch (len) { - case 2: - sa->kilobytes = decode_16(value); - break; - case 4: - sa->kilobytes = decode_32(value); - break; - default: - log_print("ipsec_decode_attribute: " - "unreasonable lifetime"); - } - break; - default: - log_print("ipsec_decode_attribute: unknown " - "lifetime type"); - } - break; - case IPSEC_ATTR_GROUP_DESCRIPTION: - isa->group_desc = decode_16(value); - break; - case IPSEC_ATTR_ENCAPSULATION_MODE: - /* - * XXX Multiple protocols must have same - * encapsulation mode, no? - */ - iproto->encap_mode = decode_16(value); - break; - case IPSEC_ATTR_AUTHENTICATION_ALGORITHM: - iproto->auth = decode_16(value); - break; - case IPSEC_ATTR_KEY_LENGTH: - iproto->keylen = decode_16(value); - break; - case IPSEC_ATTR_KEY_ROUNDS: - iproto->keyrounds = decode_16(value); - break; - case IPSEC_ATTR_COMPRESS_DICTIONARY_SIZE: - break; - case IPSEC_ATTR_COMPRESS_PRIVATE_ALGORITHM: - break; - case IPSEC_ATTR_ECN_TUNNEL: - break; - } - } - lifetype = 0; - return 0; -} - -/* - * Walk over the attributes of the transform payload found in BUF, and - * fill out the fields of the SA attached to MSG. Also mark the SA as - * processed. - */ -void -ipsec_decode_transform(struct message *msg, struct sa *sa, struct proto *proto, - u_int8_t *buf) -{ - struct ipsec_exch *ie = msg->exchange->data; - struct ipsec_decode_arg ida; - - LOG_DBG((LOG_MISC, 20, "ipsec_decode_transform: transform %d chosen", - GET_ISAKMP_TRANSFORM_NO(buf))); - - ida.msg = msg; - ida.sa = sa; - ida.proto = proto; - - /* The default IKE lifetime is 8 hours. */ - if (sa->phase == 1) - sa->seconds = 28800; - - /* Extract the attributes and stuff them into the SA. */ - attribute_map(buf + ISAKMP_TRANSFORM_SA_ATTRS_OFF, - GET_ISAKMP_GEN_LENGTH(buf) - ISAKMP_TRANSFORM_SA_ATTRS_OFF, - ipsec_decode_attribute, &ida); - - /* - * If no pseudo-random function was negotiated, it's HMAC. - * XXX As PRF_HMAC currently is zero, this is a no-op. - */ - if (!ie->prf_type) - ie->prf_type = PRF_HMAC; -} - -/* - * Delete the IPsec SA represented by the INCOMING direction in protocol PROTO - * of the IKE security association SA. - */ -static void -ipsec_delete_spi(struct sa *sa, struct proto *proto, int incoming) -{ - if (sa->phase == 1) - return; - /* XXX Error handling? Is it interesting? */ - sysdep_ipsec_delete_spi(sa, proto, incoming); -} - -/* - * Store BUF into the g^x entry of the exchange that message MSG belongs to. - * PEER is non-zero when the value is our peer's, and zero when it is ours. - */ -static int -ipsec_g_x(struct message *msg, int peer, u_int8_t *buf) -{ - struct exchange *exchange = msg->exchange; - struct ipsec_exch *ie = exchange->data; - u_int8_t **g_x; - int initiator = exchange->initiator ^ peer; - char header[32]; - - g_x = initiator ? &ie->g_xi : &ie->g_xr; - *g_x = malloc(ie->g_x_len); - if (!*g_x) { - log_error("ipsec_g_x: malloc (%lu) failed", - (unsigned long)ie->g_x_len); - return -1; - } - memcpy(*g_x, buf, ie->g_x_len); - snprintf(header, sizeof header, "ipsec_g_x: g^x%c", - initiator ? 'i' : 'r'); - LOG_DBG_BUF((LOG_MISC, 80, header, *g_x, ie->g_x_len)); - return 0; -} - -/* Generate our DH value. */ -int -ipsec_gen_g_x(struct message *msg) -{ - struct exchange *exchange = msg->exchange; - struct ipsec_exch *ie = exchange->data; - u_int8_t *buf; - - buf = malloc(ISAKMP_KE_SZ + ie->g_x_len); - if (!buf) { - log_error("ipsec_gen_g_x: malloc (%lu) failed", - ISAKMP_KE_SZ + (unsigned long)ie->g_x_len); - return -1; - } - if (message_add_payload(msg, ISAKMP_PAYLOAD_KEY_EXCH, buf, - ISAKMP_KE_SZ + ie->g_x_len, 1)) { - free(buf); - return -1; - } - if (dh_create_exchange(ie->group, buf + ISAKMP_KE_DATA_OFF)) { - log_print("ipsec_gen_g_x: dh_create_exchange failed"); - free(buf); - return -1; - } - return ipsec_g_x(msg, 0, buf + ISAKMP_KE_DATA_OFF); -} - -/* Save the peer's DH value. */ -int -ipsec_save_g_x(struct message *msg) -{ - struct exchange *exchange = msg->exchange; - struct ipsec_exch *ie = exchange->data; - struct payload *kep; - - kep = payload_first(msg, ISAKMP_PAYLOAD_KEY_EXCH); - kep->flags |= PL_MARK; - ie->g_x_len = GET_ISAKMP_GEN_LENGTH(kep->p) - ISAKMP_KE_DATA_OFF; - - /* Check that the given length matches the group's expectancy. */ - if (ie->g_x_len != (size_t) dh_getlen(ie->group)) { - /* XXX Is this a good notify type? */ - message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); - return -1; - } - return ipsec_g_x(msg, 1, kep->p + ISAKMP_KE_DATA_OFF); -} - -/* - * Get a SPI for PROTO and the transport MSG passed over. Store the - * size where SZ points. NB! A zero return is OK if *SZ is zero. - */ -static u_int8_t * -ipsec_get_spi(size_t *sz, u_int8_t proto, struct message *msg) -{ - struct sockaddr *dst, *src; - struct transport *transport = msg->transport; - - if (msg->exchange->phase == 1) { - *sz = 0; - return 0; - } else { - /* We are the destination in the SA we want a SPI for. */ - transport->vtbl->get_src(transport, &dst); - /* The peer is the source. */ - transport->vtbl->get_dst(transport, &src); - return sysdep_ipsec_get_spi(sz, proto, src, dst, - msg->exchange->seq); - } -} - -/* - * We have gotten a payload PAYLOAD of type TYPE, which did not get handled - * by the logic of the exchange MSG takes part in. Now is the time to deal - * with such a payload if we know how to, if we don't, return -1, otherwise - * 0. - */ -int -ipsec_handle_leftover_payload(struct message *msg, u_int8_t type, - struct payload *payload) -{ - u_int32_t spisz, nspis; - struct sockaddr *dst; - int reenter = 0; - u_int8_t *spis, proto; - struct sa *sa; - - switch (type) { - case ISAKMP_PAYLOAD_DELETE: - proto = GET_ISAKMP_DELETE_PROTO(payload->p); - nspis = GET_ISAKMP_DELETE_NSPIS(payload->p); - spisz = GET_ISAKMP_DELETE_SPI_SZ(payload->p); - - if (nspis == 0) { - LOG_DBG((LOG_SA, 60, "ipsec_handle_leftover_payload: " - "message specified zero SPIs, ignoring")); - return -1; - } - /* verify proper SPI size */ - if ((proto == ISAKMP_PROTO_ISAKMP && spisz != - ISAKMP_HDR_COOKIES_LEN) - || (proto != ISAKMP_PROTO_ISAKMP && spisz != - sizeof(u_int32_t))) { - log_print("ipsec_handle_leftover_payload: invalid SPI " - "size %d for proto %d in DELETE payload", - spisz, proto); - return -1; - } - spis = (u_int8_t *) malloc(nspis * spisz); - if (!spis) { - log_error("ipsec_handle_leftover_payload: malloc " - "(%d) failed", nspis * spisz); - return -1; - } - /* extract SPI and get dst address */ - memcpy(spis, payload->p + ISAKMP_DELETE_SPI_OFF, nspis * spisz); - msg->transport->vtbl->get_dst(msg->transport, &dst); - - ipsec_delete_spi_list(dst, proto, spis, nspis, "DELETE"); - - free(spis); - payload->flags |= PL_MARK; - return 0; - - case ISAKMP_PAYLOAD_NOTIFY: - switch (GET_ISAKMP_NOTIFY_MSG_TYPE(payload->p)) { - case IPSEC_NOTIFY_INITIAL_CONTACT: - /* - * Permit INITIAL-CONTACT if - * - this is not an AGGRESSIVE mode exchange - * - it is protected by an ISAKMP SA - * - * XXX Instead of the first condition above, we could - * XXX permit this only for phase 2. In the last - * XXX packet of main-mode, this payload, while - * XXX encrypted, is not part of the hash digest. As - * XXX we currently send our own INITIAL-CONTACTs at - * XXX this point, this too would need to be changed. - */ - if (msg->exchange->type == ISAKMP_EXCH_AGGRESSIVE) { - log_print("ipsec_handle_leftover_payload: got " - "INITIAL-CONTACT in AGGRESSIVE mode"); - return -1; - } - if ((msg->exchange->flags & EXCHANGE_FLAG_ENCRYPT) - == 0) { - log_print("ipsec_handle_leftover_payload: got " - "INITIAL-CONTACT without ISAKMP SA"); - return -1; - } - - if ((msg->flags & MSG_AUTHENTICATED) == 0) { - log_print("ipsec_handle_leftover_payload: " - "got unauthenticated INITIAL-CONTACT"); - return -1; - } - /* - * Find out who is sending this and then delete every - * SA that is ready. Exchanges will timeout - * themselves and then the non-ready SAs will - * disappear too. - */ - msg->transport->vtbl->get_dst(msg->transport, &dst); - while ((sa = sa_lookup_by_peer(dst, - sysdep_sa_len(dst))) != 0) { - /* - * Don't delete the current SA -- we received - * the notification over it, so it's obviously - * still active. We temporarily need to remove - * the SA from the list to avoid an endless - * loop, but keep a reference so it won't - * disappear meanwhile. - */ - if (sa == msg->isakmp_sa) { - sa_reference(sa); - sa_remove(sa); - reenter = 1; - continue; - } - LOG_DBG((LOG_SA, 30, - "ipsec_handle_leftover_payload: " - "INITIAL-CONTACT made us delete SA %p", - sa)); - sa_delete(sa, 0); - } - - if (reenter) { - sa_enter(msg->isakmp_sa); - sa_release(msg->isakmp_sa); - } - payload->flags |= PL_MARK; - return 0; - } - } - return -1; -} - -/* Return the encryption keylength in octets of the ESP protocol PROTO. */ -int -ipsec_esp_enckeylength(struct proto *proto) -{ - struct ipsec_proto *iproto = proto->data; - - /* Compute the keylength to use. */ - switch (proto->id) { - case IPSEC_ESP_DES: - case IPSEC_ESP_DES_IV32: - case IPSEC_ESP_DES_IV64: - return 8; - case IPSEC_ESP_3DES: - return 24; - case IPSEC_ESP_CAST: - if (!iproto->keylen) - return 16; - return iproto->keylen / 8; - case IPSEC_ESP_AES: - case IPSEC_ESP_AES_128_CTR: - if (!iproto->keylen) - return 16; - /* Fallthrough */ - default: - return iproto->keylen / 8; - } -} - -/* Return the authentication keylength in octets of the ESP protocol PROTO. */ -int -ipsec_esp_authkeylength(struct proto *proto) -{ - struct ipsec_proto *iproto = proto->data; - - switch (iproto->auth) { - case IPSEC_AUTH_HMAC_MD5: - return 16; - case IPSEC_AUTH_HMAC_SHA: - case IPSEC_AUTH_HMAC_RIPEMD: - return 20; - case IPSEC_AUTH_HMAC_SHA2_256: - return 32; - case IPSEC_AUTH_HMAC_SHA2_384: - return 48; - case IPSEC_AUTH_HMAC_SHA2_512: - return 64; - default: - return 0; - } -} - -/* Return the authentication keylength in octets of the AH protocol PROTO. */ -int -ipsec_ah_keylength(struct proto *proto) -{ - switch (proto->id) { - case IPSEC_AH_MD5: - return 16; - case IPSEC_AH_SHA: - case IPSEC_AH_RIPEMD: - return 20; - case IPSEC_AH_SHA2_256: - return 32; - case IPSEC_AH_SHA2_384: - return 48; - case IPSEC_AH_SHA2_512: - return 64; - default: - return -1; - } -} - -/* Return the total keymaterial length of the protocol PROTO. */ -int -ipsec_keymat_length(struct proto *proto) -{ - switch (proto->proto) { - case IPSEC_PROTO_IPSEC_ESP: - return ipsec_esp_enckeylength(proto) - + ipsec_esp_authkeylength(proto); - case IPSEC_PROTO_IPSEC_AH: - return ipsec_ah_keylength(proto); - default: - return -1; - } -} - -/* Helper function for ipsec_get_id(). */ -static int -ipsec_get_proto_port(char *section, u_int8_t *tproto, u_int16_t *port) -{ - struct protoent *pe = NULL; - struct servent *se; - char *pstr; - - pstr = conf_get_str(section, "Protocol"); - if (!pstr) { - *tproto = 0; - return 0; - } - *tproto = (u_int8_t)atoi(pstr); - if (!*tproto) { - pe = getprotobyname(pstr); - if (pe) - *tproto = pe->p_proto; - } - if (!*tproto) { - log_print("ipsec_get_proto_port: protocol \"%s\" unknown", - pstr); - return -1; - } - - pstr = conf_get_str(section, "Port"); - if (!pstr) - return 0; - *port = (u_int16_t)atoi(pstr); - if (!*port) { - se = getservbyname(pstr, - pe ? pe->p_name : (pstr ? pstr : NULL)); - if (se) - *port = se->s_port; - } - if (!*port) { - log_print("ipsec_get_proto_port: port \"%s\" unknown", - pstr); - return -1; - } - return 0; -} - -/* - * Out of a named section SECTION in the configuration file find out - * the network address and mask as well as the ID type. Put the info - * in the areas pointed to by ADDR, MASK, TPROTO, PORT, and ID respectively. - * Return 0 on success and -1 on failure. - */ -int -ipsec_get_id(char *section, int *id, struct sockaddr **addr, - struct sockaddr **mask, u_int8_t *tproto, u_int16_t *port) -{ - char *type, *address, *netmask; - - type = conf_get_str(section, "ID-type"); - if (!type) { - log_print("ipsec_get_id: section %s has no \"ID-type\" tag", - section); - return -1; - } - *id = constant_value(ipsec_id_cst, type); - switch (*id) { - case IPSEC_ID_IPV4_ADDR: - case IPSEC_ID_IPV6_ADDR: { - int ret; - - address = conf_get_str(section, "Address"); - if (!address) { - log_print("ipsec_get_id: section %s has no " - "\"Address\" tag", section); - return -1; - } - if (text2sockaddr(address, NULL, addr)) { - log_print("ipsec_get_id: invalid address %s in " - "section %s", address, section); - return -1; - } - ret = ipsec_get_proto_port(section, tproto, port); - if (ret < 0) - free(*addr); - - return ret; - } - -#ifdef notyet - case IPSEC_ID_FQDN: - return -1; - - case IPSEC_ID_USER_FQDN: - return -1; -#endif - - case IPSEC_ID_IPV4_ADDR_SUBNET: - case IPSEC_ID_IPV6_ADDR_SUBNET: { - int ret; - - address = conf_get_str(section, "Network"); - if (!address) { - log_print("ipsec_get_id: section %s has no " - "\"Network\" tag", section); - return -1; - } - if (text2sockaddr(address, NULL, addr)) { - log_print("ipsec_get_id: invalid section %s " - "network %s", section, address); - return -1; - } - netmask = conf_get_str(section, "Netmask"); - if (!netmask) { - log_print("ipsec_get_id: section %s has no " - "\"Netmask\" tag", section); - free(*addr); - return -1; - } - if (text2sockaddr(netmask, NULL, mask)) { - log_print("ipsec_id_build: invalid section %s " - "network %s", section, netmask); - free(*addr); - return -1; - } - ret = ipsec_get_proto_port(section, tproto, port); - if (ret < 0) { - free(*mask); - free(*addr); - } - return ret; - } - -#ifdef notyet - case IPSEC_ID_IPV4_RANGE: - return -1; - - case IPSEC_ID_IPV6_RANGE: - return -1; - - case IPSEC_ID_DER_ASN1_DN: - return -1; - - case IPSEC_ID_DER_ASN1_GN: - return -1; - - case IPSEC_ID_KEY_ID: - return -1; -#endif - - default: - log_print("ipsec_get_id: unknown ID type \"%s\" in " - "section %s", type, section); - return -1; - } - - return 0; -} - -/* - * XXX I rather want this function to return a status code, and fail if - * we cannot fit the information in the supplied buffer. - */ -static void -ipsec_decode_id(char *buf, size_t size, u_int8_t *id, size_t id_len, - int isakmpform) -{ - int id_type; - char *addr = 0, *mask = 0; - u_int32_t *idp; - - if (id) { - if (!isakmpform) { - /* - * Exchanges and SAs dont carry the IDs in ISAKMP - * form. - */ - id -= ISAKMP_GEN_SZ; - id_len += ISAKMP_GEN_SZ; - } - id_type = GET_ISAKMP_ID_TYPE(id); - idp = (u_int32_t *) (id + ISAKMP_ID_DATA_OFF); - switch (id_type) { - case IPSEC_ID_IPV4_ADDR: - util_ntoa(&addr, AF_INET, id + ISAKMP_ID_DATA_OFF); - snprintf(buf, size, "%08x: %s", - decode_32(id + ISAKMP_ID_DATA_OFF), addr); - break; - - case IPSEC_ID_IPV4_ADDR_SUBNET: - util_ntoa(&addr, AF_INET, id + ISAKMP_ID_DATA_OFF); - util_ntoa(&mask, AF_INET, id + ISAKMP_ID_DATA_OFF + 4); - snprintf(buf, size, "%08x/%08x: %s/%s", - decode_32(id + ISAKMP_ID_DATA_OFF), - decode_32(id + ISAKMP_ID_DATA_OFF + 4), addr, mask); - break; - - case IPSEC_ID_IPV6_ADDR: - util_ntoa(&addr, AF_INET6, id + ISAKMP_ID_DATA_OFF); - snprintf(buf, size, "%08x%08x%08x%08x: %s", *idp, - *(idp + 1), *(idp + 2), *(idp + 3), addr); - break; - - case IPSEC_ID_IPV6_ADDR_SUBNET: - util_ntoa(&addr, AF_INET6, id + ISAKMP_ID_DATA_OFF); - util_ntoa(&mask, AF_INET6, id + ISAKMP_ID_DATA_OFF + - sizeof(struct in6_addr)); - snprintf(buf, size, - "%08x%08x%08x%08x/%08x%08x%08x%08x: %s/%s", *idp, - *(idp + 1), *(idp + 2), *(idp + 3), *(idp + 4), - *(idp + 5), *(idp + 6), *(idp + 7), addr, mask); - break; - - case IPSEC_ID_FQDN: - case IPSEC_ID_USER_FQDN: - /* String is not NUL terminated, be careful */ - id_len -= ISAKMP_ID_DATA_OFF; - id_len = MIN(id_len, size - 1); - memcpy(buf, id + ISAKMP_ID_DATA_OFF, id_len); - buf[id_len] = '\0'; - break; - -#ifdef USE_X509 - case IPSEC_ID_DER_ASN1_DN: - addr = x509_DN_string(id + ISAKMP_ID_DATA_OFF, - id_len - ISAKMP_ID_DATA_OFF); - if (!addr) { - snprintf(buf, size, "unparsable ASN1 DN ID"); - return; - } - strlcpy(buf, addr, size); - break; -#endif - - default: - snprintf(buf, size, "<id type unknown: %x>", id_type); - break; - } - } else - snprintf(buf, size, "<no ipsec id>"); - if (addr) - free(addr); - if (mask) - free(mask); -} - -char * -ipsec_decode_ids(char *fmt, u_int8_t *id1, size_t id1_len, u_int8_t *id2, - size_t id2_len, int isakmpform) -{ - static char result[1024]; - char s_id1[256], s_id2[256]; - - ipsec_decode_id(s_id1, sizeof s_id1, id1, id1_len, isakmpform); - ipsec_decode_id(s_id2, sizeof s_id2, id2, id2_len, isakmpform); - - snprintf(result, sizeof result, fmt, s_id1, s_id2); - return result; -} - -/* - * Out of a named section SECTION in the configuration file build an - * ISAKMP ID payload. Ths payload size should be stashed in SZ. - * The caller is responsible for freeing the payload. - */ -u_int8_t * -ipsec_build_id(char *section, size_t *sz) -{ - struct sockaddr *addr, *mask; - u_int8_t *p; - int id, subnet = 0; - u_int8_t tproto = 0; - u_int16_t port = 0; - - if (ipsec_get_id(section, &id, &addr, &mask, &tproto, &port)) - return 0; - - if (id == IPSEC_ID_IPV4_ADDR_SUBNET || id == IPSEC_ID_IPV6_ADDR_SUBNET) - subnet = 1; - - *sz = ISAKMP_ID_SZ + sockaddr_addrlen(addr); - if (subnet) - *sz += sockaddr_addrlen(mask); - - p = malloc(*sz); - if (!p) { - log_print("ipsec_build_id: malloc(%lu) failed", - (unsigned long)*sz); - if (subnet) - free(mask); - free(addr); - return 0; - } - SET_ISAKMP_ID_TYPE(p, id); - SET_ISAKMP_ID_DOI_DATA(p, (unsigned char *)"\000\000\000"); - - memcpy(p + ISAKMP_ID_DATA_OFF, sockaddr_addrdata(addr), - sockaddr_addrlen(addr)); - if (subnet) - memcpy(p + ISAKMP_ID_DATA_OFF + sockaddr_addrlen(addr), - sockaddr_addrdata(mask), sockaddr_addrlen(mask)); - - SET_IPSEC_ID_PROTO(p + ISAKMP_ID_DOI_DATA_OFF, tproto); - SET_IPSEC_ID_PORT(p + ISAKMP_ID_DOI_DATA_OFF, port); - - if (subnet) - free(mask); - free(addr); - return p; -} - -/* - * copy an ISAKMPD id - */ -int -ipsec_clone_id(u_int8_t **did, size_t *did_len, u_int8_t *id, size_t id_len) -{ - if (*did) - free(*did); - - if (!id_len || !id) { - *did = 0; - *did_len = 0; - return 0; - } - *did = malloc(id_len); - if (!*did) { - *did_len = 0; - log_error("ipsec_clone_id: malloc(%lu) failed", - (unsigned long)id_len); - return -1; - } - *did_len = id_len; - memcpy(*did, id, id_len); - - return 0; -} - -/* - * IPsec-specific PROTO initializations. SECTION is only set if we are the - * initiator thus only usable there. - * XXX I want to fix this later. - */ -void -ipsec_proto_init(struct proto *proto, char *section) -{ - struct ipsec_proto *iproto = proto->data; - - if (proto->sa->phase == 2) - iproto->replay_window = section ? conf_get_num(section, - "ReplayWindow", DEFAULT_REPLAY_WINDOW) : - DEFAULT_REPLAY_WINDOW; -} - -/* - * Add a notification payload of type INITIAL CONTACT to MSG if this is - * the first contact we have made to our peer. - */ -int -ipsec_initial_contact(struct message *msg) -{ - u_int8_t *buf; - - if (ipsec_contacted(msg)) - return 0; - - buf = malloc(ISAKMP_NOTIFY_SZ + ISAKMP_HDR_COOKIES_LEN); - if (!buf) { - log_error("ike_phase_1_initial_contact: malloc (%d) failed", - ISAKMP_NOTIFY_SZ + ISAKMP_HDR_COOKIES_LEN); - return -1; - } - SET_ISAKMP_NOTIFY_DOI(buf, IPSEC_DOI_IPSEC); - SET_ISAKMP_NOTIFY_PROTO(buf, ISAKMP_PROTO_ISAKMP); - SET_ISAKMP_NOTIFY_SPI_SZ(buf, ISAKMP_HDR_COOKIES_LEN); - SET_ISAKMP_NOTIFY_MSG_TYPE(buf, IPSEC_NOTIFY_INITIAL_CONTACT); - memcpy(buf + ISAKMP_NOTIFY_SPI_OFF, msg->isakmp_sa->cookies, - ISAKMP_HDR_COOKIES_LEN); - if (message_add_payload(msg, ISAKMP_PAYLOAD_NOTIFY, buf, - ISAKMP_NOTIFY_SZ + ISAKMP_HDR_COOKIES_LEN, 1)) { - free(buf); - return -1; - } - return ipsec_add_contact(msg); -} - -/* - * Compare the two contacts pointed to by A and B. Return negative if - * *A < *B, 0 if they are equal, and positive if *A is the largest of them. - */ -static int -addr_cmp(const void *a, const void *b) -{ - const struct contact *x = a, *y = b; - int minlen = MIN(x->len, y->len); - int rv = memcmp(x->addr, y->addr, minlen); - - return rv ? rv : (x->len - y->len); -} - -/* - * Add the peer that MSG is bound to as an address we don't want to send - * INITIAL CONTACT too from now on. Do not call this function with a - * specific address duplicate times. We want fast lookup, speed of insertion - * is unimportant, if this is to scale. - */ -static int -ipsec_add_contact(struct message *msg) -{ - struct contact *new_contacts; - struct sockaddr *dst, *addr; - int cnt; - - if (contact_cnt == contact_limit) { - cnt = contact_limit ? 2 * contact_limit : 64; - new_contacts = realloc(contacts, cnt * sizeof contacts[0]); - if (!new_contacts) { - log_error("ipsec_add_contact: " - "realloc (%p, %lu) failed", contacts, - cnt * (unsigned long) sizeof contacts[0]); - return -1; - } - contact_limit = cnt; - contacts = new_contacts; - } - msg->transport->vtbl->get_dst(msg->transport, &dst); - addr = malloc(sysdep_sa_len(dst)); - if (!addr) { - log_error("ipsec_add_contact: malloc (%d) failed", - sysdep_sa_len(dst)); - return -1; - } - memcpy(addr, dst, sysdep_sa_len(dst)); - contacts[contact_cnt].addr = addr; - contacts[contact_cnt++].len = sysdep_sa_len(dst); - - /* - * XXX There are better algorithms for already mostly-sorted data like - * this, but only qsort is standard. I will someday do this inline. - */ - qsort(contacts, contact_cnt, sizeof *contacts, addr_cmp); - return 0; -} - -/* Return true if the recipient of MSG has already been contacted. */ -static int -ipsec_contacted(struct message *msg) -{ - struct contact contact; - - msg->transport->vtbl->get_dst(msg->transport, &contact.addr); - contact.len = sysdep_sa_len(contact.addr); - return contacts ? (bsearch(&contact, contacts, contact_cnt, - sizeof *contacts, addr_cmp) != 0) : 0; -} - -/* Add a HASH for to MSG. */ -u_int8_t * -ipsec_add_hash_payload(struct message *msg, size_t hashsize) -{ - u_int8_t *buf; - - buf = malloc(ISAKMP_HASH_SZ + hashsize); - if (!buf) { - log_error("ipsec_add_hash_payload: malloc (%lu) failed", - ISAKMP_HASH_SZ + (unsigned long) hashsize); - return 0; - } - if (message_add_payload(msg, ISAKMP_PAYLOAD_HASH, buf, - ISAKMP_HASH_SZ + hashsize, 1)) { - free(buf); - return 0; - } - return buf; -} - -/* Fill in the HASH payload of MSG. */ -int -ipsec_fill_in_hash(struct message *msg) -{ - struct exchange *exchange = msg->exchange; - struct sa *isakmp_sa = msg->isakmp_sa; - struct ipsec_sa *isa = isakmp_sa->data; - struct hash *hash = hash_get(isa->hash); - struct prf *prf; - struct payload *payload; - u_int8_t *buf; - u_int32_t i; - char header[80]; - - /* If no SKEYID_a, we need not do anything. */ - if (!isa->skeyid_a) - return 0; - - payload = payload_first(msg, ISAKMP_PAYLOAD_HASH); - if (!payload) { - log_print("ipsec_fill_in_hash: no HASH payload found"); - return -1; - } - buf = payload->p; - - /* Allocate the prf and start calculating our HASH(1). */ - LOG_DBG_BUF((LOG_MISC, 90, "ipsec_fill_in_hash: SKEYID_a", - isa->skeyid_a, isa->skeyid_len)); - prf = prf_alloc(isa->prf_type, hash->type, isa->skeyid_a, - isa->skeyid_len); - if (!prf) - return -1; - - prf->Init(prf->prfctx); - LOG_DBG_BUF((LOG_MISC, 90, "ipsec_fill_in_hash: message_id", - exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); - prf->Update(prf->prfctx, exchange->message_id, - ISAKMP_HDR_MESSAGE_ID_LEN); - - /* Loop over all payloads after HASH(1). */ - for (i = 2; i < msg->iovlen; i++) { - /* XXX Misleading payload type printouts. */ - snprintf(header, sizeof header, - "ipsec_fill_in_hash: payload %d after HASH(1)", i - 1); - LOG_DBG_BUF((LOG_MISC, 90, header, msg->iov[i].iov_base, - msg->iov[i].iov_len)); - prf->Update(prf->prfctx, msg->iov[i].iov_base, - msg->iov[i].iov_len); - } - prf->Final(buf + ISAKMP_HASH_DATA_OFF, prf->prfctx); - prf_free(prf); - LOG_DBG_BUF((LOG_MISC, 80, "ipsec_fill_in_hash: HASH(1)", buf + - ISAKMP_HASH_DATA_OFF, hash->hashsize)); - - return 0; -} - -/* Add a HASH payload to MSG, if we have an ISAKMP SA we're protected by. */ -static int -ipsec_informational_pre_hook(struct message *msg) -{ - struct sa *isakmp_sa = msg->isakmp_sa; - struct ipsec_sa *isa; - struct hash *hash; - - if (!isakmp_sa) - return 0; - isa = isakmp_sa->data; - hash = hash_get(isa->hash); - return ipsec_add_hash_payload(msg, hash->hashsize) == 0; -} - -/* - * Fill in the HASH payload in MSG, if we have an ISAKMP SA we're protected by. - */ -static int -ipsec_informational_post_hook(struct message *msg) -{ - if (!msg->isakmp_sa) - return 0; - return ipsec_fill_in_hash(msg); -} - -ssize_t -ipsec_id_size(char *section, u_int8_t *id) -{ - char *type, *data; - - type = conf_get_str(section, "ID-type"); - if (!type) { - log_print("ipsec_id_size: section %s has no \"ID-type\" tag", - section); - return -1; - } - *id = constant_value(ipsec_id_cst, type); - switch (*id) { - case IPSEC_ID_IPV4_ADDR: - return sizeof(struct in_addr); - case IPSEC_ID_IPV4_ADDR_SUBNET: - return 2 * sizeof(struct in_addr); - case IPSEC_ID_IPV6_ADDR: - return sizeof(struct in6_addr); - case IPSEC_ID_IPV6_ADDR_SUBNET: - return 2 * sizeof(struct in6_addr); - case IPSEC_ID_FQDN: - case IPSEC_ID_USER_FQDN: - case IPSEC_ID_KEY_ID: - case IPSEC_ID_DER_ASN1_DN: - case IPSEC_ID_DER_ASN1_GN: - data = conf_get_str(section, "Name"); - if (!data) { - log_print("ipsec_id_size: " - "section %s has no \"Name\" tag", section); - return -1; - } - return strlen(data); - } - log_print("ipsec_id_size: unrecognized/unsupported ID-type %d (%s)", - *id, type); - return -1; -} - -/* - * Generate a string version of the ID. - */ -char * -ipsec_id_string(u_int8_t *id, size_t id_len) -{ - char *buf = 0; - char *addrstr = 0; - size_t len, size; - - /* - * XXX Real ugly way of making the offsets correct. Be aware that id - * now will point before the actual buffer and cannot be dereferenced - * without an offset larger than or equal to ISAKM_GEN_SZ. - */ - id -= ISAKMP_GEN_SZ; - - /* This is the actual length of the ID data field. */ - id_len += ISAKMP_GEN_SZ - ISAKMP_ID_DATA_OFF; - - /* - * Conservative allocation. - * XXX I think the ASN1 DN case can be thought through to give a better - * estimate. - */ - size = MAX(sizeof "ipv6/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - sizeof "asn1_dn/" + id_len - ISAKMP_ID_DATA_OFF); - buf = malloc(size); - if (!buf) - /* XXX Log? */ - goto fail; - - switch (GET_ISAKMP_ID_TYPE(id)) { - case IPSEC_ID_IPV4_ADDR: - if (id_len < sizeof(struct in_addr)) - goto fail; - util_ntoa(&addrstr, AF_INET, id + ISAKMP_ID_DATA_OFF); - if (!addrstr) - goto fail; - snprintf(buf, size, "ipv4/%s", addrstr); - break; - - case IPSEC_ID_IPV6_ADDR: - if (id_len < sizeof(struct in6_addr)) - goto fail; - util_ntoa(&addrstr, AF_INET6, id + ISAKMP_ID_DATA_OFF); - if (!addrstr) - goto fail; - snprintf(buf, size, "ipv6/%s", addrstr); - break; - - case IPSEC_ID_FQDN: - case IPSEC_ID_USER_FQDN: - strlcpy(buf, - GET_ISAKMP_ID_TYPE(id) == IPSEC_ID_FQDN ? "fqdn/" : "ufqdn/", - size); - len = strlen(buf); - - memcpy(buf + len, id + ISAKMP_ID_DATA_OFF, id_len); - *(buf + len + id_len) = '\0'; - break; - -#ifdef USE_X509 - case IPSEC_ID_DER_ASN1_DN: - strlcpy(buf, "asn1_dn/", size); - len = strlen(buf); - addrstr = x509_DN_string(id + ISAKMP_ID_DATA_OFF, - id_len - ISAKMP_ID_DATA_OFF); - if (!addrstr) - goto fail; - if (size < len + strlen(addrstr) + 1) - goto fail; - strlcpy(buf + len, addrstr, size - len); - break; -#endif - - default: - /* Unknown type. */ - LOG_DBG((LOG_MISC, 10, - "ipsec_id_string: unknown identity type %d\n", - GET_ISAKMP_ID_TYPE(id))); - goto fail; - } - - if (addrstr) - free(addrstr); - return buf; - -fail: - if (buf) - free(buf); - if (addrstr) - free(addrstr); - return 0; -} |