diff options
Diffstat (limited to 'keyexchange/isakmpd-20041012/ike_phase_1.c')
-rw-r--r-- | keyexchange/isakmpd-20041012/ike_phase_1.c | 1396 |
1 files changed, 1396 insertions, 0 deletions
diff --git a/keyexchange/isakmpd-20041012/ike_phase_1.c b/keyexchange/isakmpd-20041012/ike_phase_1.c new file mode 100644 index 0000000..7f72a25 --- /dev/null +++ b/keyexchange/isakmpd-20041012/ike_phase_1.c @@ -0,0 +1,1396 @@ +/* $OpenBSD: ike_phase_1.c,v 1.56 2004/08/08 19:11:06 deraadt Exp $ */ +/* $EOM: ike_phase_1.c,v 1.31 2000/12/11 23:47:56 niklas Exp $ */ + +/* + * Copyright (c) 1999, 2000 Niklas Hallqvist. All rights reserved. + * Copyright (c) 1999, 2000 Angelos D. Keromytis. All rights reserved. + * Copyright (c) 2001, 2004 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 <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" +#ifdef USE_DPD +#include "dpd.h" +#endif +#include "exchange.h" +#include "hash.h" +#include "ike_auth.h" +#include "ike_phase_1.h" +#include "ipsec.h" +#include "ipsec_doi.h" +#include "isakmp.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 "transport.h" +#include "util.h" + +static int attribute_unacceptable(u_int16_t, u_int8_t *, u_int16_t, + void *); +static int ike_phase_1_validate_prop(struct exchange *, struct sa *, + struct sa *); + +/* Offer a set of transforms to the responder in the MSG message. */ +int +ike_phase_1_initiator_send_SA(struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + u_int8_t *proposal = 0, *sa_buf = 0, *saved_nextp, *attr; + u_int8_t **transform = 0; + size_t transforms_len = 0, proposal_len, sa_len; + size_t *transform_len = 0; + struct conf_list *conf, *life_conf; + struct conf_list_node *xf, *life; + int value, update_nextp; + size_t i; + struct payload *p; + struct proto *proto; + struct proto_attr *pa; + int group_desc = -1, new_group_desc; + + /* Get the list of transforms. */ + conf = conf_get_list(exchange->policy, "Transforms"); + if (!conf) + return -1; + + transform = calloc(conf->cnt, sizeof *transform); + if (!transform) { + log_error("ike_phase_1_initiator_send_SA: calloc (%lu, %lu) " + "failed", (u_long)conf->cnt, (u_long) sizeof *transform); + goto bail_out; + } + transform_len = calloc(conf->cnt, sizeof *transform_len); + if (!transform_len) { + log_error("ike_phase_1_initiator_send_SA: calloc (%lu, %lu) " + "failed", (u_long)conf->cnt, + (u_long) sizeof *transform_len); + goto bail_out; + } + for (xf = TAILQ_FIRST(&conf->fields), i = 0; i < conf->cnt; + i++, xf = TAILQ_NEXT(xf, link)) { + /* XXX The sizing needs to be dynamic. */ + transform[i] = malloc(ISAKMP_TRANSFORM_SA_ATTRS_OFF + + 16 * ISAKMP_ATTR_VALUE_OFF); + if (!transform[i]) { + log_error("ike_phase_1_initiator_send_SA: malloc (%d) " + "failed", ISAKMP_TRANSFORM_SA_ATTRS_OFF + + 16 * ISAKMP_ATTR_VALUE_OFF); + goto bail_out; + } + SET_ISAKMP_TRANSFORM_NO(transform[i], i); + SET_ISAKMP_TRANSFORM_ID(transform[i], IPSEC_TRANSFORM_KEY_IKE); + SET_ISAKMP_TRANSFORM_RESERVED(transform[i], 0); + + attr = transform[i] + ISAKMP_TRANSFORM_SA_ATTRS_OFF; + + if (attribute_set_constant(xf->field, "ENCRYPTION_ALGORITHM", + ike_encrypt_cst, IKE_ATTR_ENCRYPTION_ALGORITHM, &attr)) + goto bail_out; + + if (attribute_set_constant(xf->field, "HASH_ALGORITHM", + ike_hash_cst, IKE_ATTR_HASH_ALGORITHM, &attr)) + goto bail_out; + + if (attribute_set_constant(xf->field, "AUTHENTICATION_METHOD", + ike_auth_cst, IKE_ATTR_AUTHENTICATION_METHOD, &attr)) + goto bail_out; + + if (attribute_set_constant(xf->field, "GROUP_DESCRIPTION", + ike_group_desc_cst, IKE_ATTR_GROUP_DESCRIPTION, &attr)) { + /* + * If no group description exists, try looking for + * a user-defined one. + */ + if (attribute_set_constant(xf->field, "GROUP_TYPE", + ike_group_cst, IKE_ATTR_GROUP_TYPE, &attr)) + goto bail_out; + +#if 0 + if (attribute_set_bignum(xf->field, "GROUP_PRIME", + IKE_ATTR_GROUP_PRIME, &attr)) + goto bail_out; + + if (attribute_set_bignum(xf->field, + "GROUP_GENERATOR_2", IKE_ATTR_GROUP_GENERATOR_2, + &attr)) + goto bail_out; + + if (attribute_set_bignum(xf->field, + "GROUP_GENERATOR_2", IKE_ATTR_GROUP_GENERATOR_2, + &attr)) + goto bail_out; + + if (attribute_set_bignum(xf->field, "GROUP_CURVE_A", + IKE_ATTR_GROUP_CURVE_A, &attr)) + goto bail_out; + + if (attribute_set_bignum(xf->field, "GROUP_CURVE_B", + IKE_ATTR_GROUP_CURVE_B, &attr)) + goto bail_out; +#endif + } + /* + * Life durations are special, we should be able to specify + * several, one per type. + */ + life_conf = conf_get_list(xf->field, "Life"); + if (life_conf) { + for (life = TAILQ_FIRST(&life_conf->fields); life; + life = TAILQ_NEXT(life, link)) { + attribute_set_constant(life->field, + "LIFE_TYPE", ike_duration_cst, + IKE_ATTR_LIFE_TYPE, &attr); + + /* + * XXX Deals with 16 and 32 bit lifetimes + * only + */ + value = conf_get_num(life->field, + "LIFE_DURATION", 0); + if (value) { + if (value <= 0xffff) + attr = attribute_set_basic( + attr, + IKE_ATTR_LIFE_DURATION, + value); + else { + value = htonl(value); + attr = attribute_set_var(attr, + IKE_ATTR_LIFE_DURATION, + (u_int8_t *)&value, + sizeof value); + } + } + } + conf_free_list(life_conf); + } + attribute_set_constant(xf->field, "PRF", ike_prf_cst, + IKE_ATTR_PRF, &attr); + + value = conf_get_num(xf->field, "KEY_LENGTH", 0); + if (value) + attr = attribute_set_basic(attr, IKE_ATTR_KEY_LENGTH, + value); + + value = conf_get_num(xf->field, "FIELD_SIZE", 0); + if (value) + attr = attribute_set_basic(attr, IKE_ATTR_FIELD_SIZE, + value); + + value = conf_get_num(xf->field, "GROUP_ORDER", 0); + if (value) + attr = attribute_set_basic(attr, IKE_ATTR_GROUP_ORDER, + value); + + /* Record the real transform size. */ + transforms_len += transform_len[i] = attr - transform[i]; + + /* XXX I don't like exchange-specific stuff in here. */ + if (exchange->type == ISAKMP_EXCH_AGGRESSIVE) { + /* + * Make sure that if a group description is specified, + * it is specified for all transforms equally. + */ + attr = (u_int8_t *) conf_get_str(xf->field, + "GROUP_DESCRIPTION"); + new_group_desc = + attr ? constant_value(ike_group_desc_cst, + (char *) attr) : 0; + if (group_desc == -1) + group_desc = new_group_desc; + else if (group_desc != new_group_desc) { + log_print("ike_phase_1_initiator_send_SA: " + "differing group descriptions in a " + "proposal"); + goto bail_out; + } + } + /* + * We need to check that we actually support our + * configuration. + */ + if (attribute_map(transform[i] + ISAKMP_TRANSFORM_SA_ATTRS_OFF, + transform_len[i] - ISAKMP_TRANSFORM_SA_ATTRS_OFF, + exchange->doi->is_attribute_incompatible, msg)) { + log_print("ike_phase_1_initiator_send_SA: " + "section [%s] has unsupported attribute(s)", + xf->field); + goto bail_out; + } + } + + /* XXX I don't like exchange-specific stuff in here. */ + if (exchange->type == ISAKMP_EXCH_AGGRESSIVE) + ie->group = group_get(group_desc); + + proposal_len = ISAKMP_PROP_SPI_OFF; + proposal = malloc(proposal_len); + if (!proposal) { + log_error("ike_phase_1_initiator_send_SA: malloc (%lu) failed", + (unsigned long) proposal_len); + goto bail_out; + } + SET_ISAKMP_PROP_NO(proposal, 1); + SET_ISAKMP_PROP_PROTO(proposal, ISAKMP_PROTO_ISAKMP); + SET_ISAKMP_PROP_SPI_SZ(proposal, 0); + SET_ISAKMP_PROP_NTRANSFORMS(proposal, conf->cnt); + + /* XXX I would like to see this factored out. */ + proto = calloc(1, sizeof *proto); + if (!proto) { + log_error("ike_phase_1_initiator_send_SA: " + "calloc (1, %lu) failed", (unsigned long) sizeof *proto); + goto bail_out; + } + proto->no = 1; + proto->proto = ISAKMP_PROTO_ISAKMP; + proto->sa = TAILQ_FIRST(&exchange->sa_list); + proto->xf_cnt = conf->cnt; + TAILQ_INIT(&proto->xfs); + for (i = 0; i < proto->xf_cnt; i++) { + pa = (struct proto_attr *) calloc(1, sizeof *pa); + if (!pa) + goto bail_out; + pa->len = transform_len[i]; + pa->attrs = (u_int8_t *) malloc(pa->len); + if (!pa->attrs) { + free(pa); + goto bail_out; + } + memcpy(pa->attrs, transform[i], pa->len); + TAILQ_INSERT_TAIL(&proto->xfs, pa, next); + } + TAILQ_INSERT_TAIL(&TAILQ_FIRST(&exchange->sa_list)->protos, proto, + link); + + sa_len = ISAKMP_SA_SIT_OFF + IPSEC_SIT_SIT_LEN; + sa_buf = malloc(sa_len); + if (!sa_buf) { + log_error("ike_phase_1_initiator_send_SA: malloc (%lu) failed", + (unsigned long) sa_len); + goto bail_out; + } + SET_ISAKMP_SA_DOI(sa_buf, IPSEC_DOI_IPSEC); + SET_IPSEC_SIT_SIT(sa_buf + ISAKMP_SA_SIT_OFF, IPSEC_SIT_IDENTITY_ONLY); + + /* + * Add the payloads. As this is a SA, we need to recompute the + * lengths of the payloads containing others. + */ + if (message_add_payload(msg, ISAKMP_PAYLOAD_SA, sa_buf, sa_len, 1)) + goto bail_out; + SET_ISAKMP_GEN_LENGTH(sa_buf, + sa_len + proposal_len + transforms_len); + sa_buf = 0; + + saved_nextp = msg->nextp; + if (message_add_payload(msg, ISAKMP_PAYLOAD_PROPOSAL, proposal, + proposal_len, 0)) + goto bail_out; + SET_ISAKMP_GEN_LENGTH(proposal, proposal_len + transforms_len); + proposal = 0; + + update_nextp = 0; + for (i = 0; i < conf->cnt; i++) { + if (message_add_payload(msg, ISAKMP_PAYLOAD_TRANSFORM, + transform[i], transform_len[i], update_nextp)) + goto bail_out; + update_nextp = 1; + transform[i] = 0; + } + msg->nextp = saved_nextp; + + /* Save SA payload body in ie->sa_i_b, length ie->sa_i_b_len. */ + ie->sa_i_b_len = sa_len + proposal_len + transforms_len - + ISAKMP_GEN_SZ; + ie->sa_i_b = malloc(ie->sa_i_b_len); + if (!ie->sa_i_b) { + log_error("ike_phase_1_initiator_send_SA: malloc (%lu) failed", + (unsigned long) ie->sa_i_b_len); + goto bail_out; + } + memcpy(ie->sa_i_b, + payload_first(msg, ISAKMP_PAYLOAD_SA)->p + ISAKMP_GEN_SZ, + sa_len - ISAKMP_GEN_SZ); + memcpy(ie->sa_i_b + sa_len - ISAKMP_GEN_SZ, + payload_first(msg, ISAKMP_PAYLOAD_PROPOSAL)->p, proposal_len); + transforms_len = 0; + for (i = 0, p = payload_first(msg, ISAKMP_PAYLOAD_TRANSFORM); + i < conf->cnt; i++, p = TAILQ_NEXT(p, link)) { + memcpy(ie->sa_i_b + sa_len + proposal_len + transforms_len - + ISAKMP_GEN_SZ, p->p, transform_len[i]); + transforms_len += transform_len[i]; + } + +#if defined (USE_NAT_TRAVERSAL) + /* Advertise NAT-T capability. */ + if (nat_t_add_vendor_payloads(msg)) + goto bail_out; +#endif + +#if defined (USE_DPD) + /* Advertise DPD capability. */ + if (dpd_add_vendor_payload(msg)) + goto bail_out; +#endif + + conf_free_list(conf); + free(transform); + free(transform_len); + return 0; + +bail_out: + if (sa_buf) + free(sa_buf); + if (proposal) + free(proposal); + if (transform) { + for (i = 0; i < conf->cnt; i++) + if (transform[i]) + free(transform[i]); + free(transform); + } + if (transform_len) + free(transform_len); + conf_free_list(conf); + return -1; +} + +/* Figure out what transform the responder chose. */ +int +ike_phase_1_initiator_recv_SA(struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct sa *sa = TAILQ_FIRST(&exchange->sa_list); + struct ipsec_exch *ie = exchange->data; + struct ipsec_sa *isa = sa->data; + struct payload *sa_p = payload_first(msg, ISAKMP_PAYLOAD_SA); + struct payload *prop = payload_first(msg, ISAKMP_PAYLOAD_PROPOSAL); + struct payload *xf = payload_first(msg, ISAKMP_PAYLOAD_TRANSFORM); + + /* + * IKE requires that only one SA with only one proposal exists and + * since we are getting an answer on our transform offer, only one + * transform. + */ + if (TAILQ_NEXT(sa_p, link) || TAILQ_NEXT(prop, link) || + TAILQ_NEXT(xf, link)) { + log_print("ike_phase_1_initiator_recv_SA: " + "multiple SA, proposal or transform payloads in phase 1"); + /* XXX Is there a better notification type? */ + message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); + return -1; + } + /* Check that the chosen transform matches an offer. */ + if (message_negotiate_sa(msg, ike_phase_1_validate_prop) || + !TAILQ_FIRST(&sa->protos)) + return -1; + + ipsec_decode_transform(msg, sa, TAILQ_FIRST(&sa->protos), xf->p); + + /* XXX I don't like exchange-specific stuff in here. */ + if (exchange->type != ISAKMP_EXCH_AGGRESSIVE) + ie->group = group_get(isa->group_desc); + + /* Mark the SA as handled. */ + sa_p->flags |= PL_MARK; + + return 0; +} + +/* Send our public DH value and a nonce to the responder. */ +int +ike_phase_1_initiator_send_KE_NONCE(struct message *msg) +{ + struct ipsec_exch *ie = msg->exchange->data; + + ie->g_x_len = dh_getlen(ie->group); + + /* XXX I want a better way to specify the nonce's size. */ + return ike_phase_1_send_KE_NONCE(msg, 16); +} + +/* Accept responder's public DH value and nonce. */ +int +ike_phase_1_initiator_recv_KE_NONCE(struct message *msg) +{ + if (ike_phase_1_recv_KE_NONCE(msg)) + return -1; + + return ike_phase_1_post_exchange_KE_NONCE(msg); +} + +/* + * Accept a set of transforms offered by the initiator and chose one we can + * handle. + */ +int +ike_phase_1_responder_recv_SA(struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct sa *sa = TAILQ_FIRST(&exchange->sa_list); + struct ipsec_sa *isa = sa->data; + struct payload *sa_p = payload_first(msg, ISAKMP_PAYLOAD_SA); + struct payload *prop = payload_first(msg, ISAKMP_PAYLOAD_PROPOSAL); + struct ipsec_exch *ie = exchange->data; + + /* Mark the SA as handled. */ + sa_p->flags |= PL_MARK; + + /* IKE requires that only one SA with only one proposal exists. */ + if (TAILQ_NEXT(sa_p, link) || TAILQ_NEXT(prop, link)) { + log_print("ike_phase_1_responder_recv_SA: " + "multiple SA or proposal payloads in phase 1"); + /* XXX Is there a better notification type? */ + message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); + return -1; + } + /* Chose a transform from the SA. */ + if (message_negotiate_sa(msg, ike_phase_1_validate_prop) || + !TAILQ_FIRST(&sa->protos)) + return -1; + + /* XXX Move into message_negotiate_sa? */ + ipsec_decode_transform(msg, sa, TAILQ_FIRST(&sa->protos), + TAILQ_FIRST(&sa->protos)->chosen->p); + + ie->group = group_get(isa->group_desc); + + /* + * Check that the mandatory attributes: encryption, hash, + * authentication method and Diffie-Hellman group description, has + * been supplied. + */ + if (!exchange->crypto || !ie->hash || !ie->ike_auth || !ie->group) { + message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); + return -1; + } + /* Save the body for later hash computation. */ + ie->sa_i_b_len = GET_ISAKMP_GEN_LENGTH(sa_p->p) - ISAKMP_GEN_SZ; + ie->sa_i_b = malloc(ie->sa_i_b_len); + if (!ie->sa_i_b) { + /* XXX How to notify peer? */ + log_error("ike_phase_1_responder_recv_SA: malloc (%lu) failed", + (unsigned long) ie->sa_i_b_len); + return -1; + } + memcpy(ie->sa_i_b, sa_p->p + ISAKMP_GEN_SZ, ie->sa_i_b_len); + return 0; +} + +/* Reply with the transform we chose. */ +int +ike_phase_1_responder_send_SA(struct message *msg) +{ + /* Add the SA payload with the transform that was chosen. */ + if (message_add_sa_payload(msg)) + return -1; + +#if defined (USE_NAT_TRAVERSAL) + /* Advertise NAT-T capability. */ + if (nat_t_add_vendor_payloads(msg)) + return -1; +#endif + +#if defined (USE_DPD) + /* Advertise DPD capability. */ + if (dpd_add_vendor_payload(msg)) + return -1; +#endif + return 0; +} + +/* Send our public DH value and a nonce to the peer. */ +int +ike_phase_1_send_KE_NONCE(struct message *msg, size_t nonce_sz) +{ + /* Public DH key. */ + if (ipsec_gen_g_x(msg)) { + /* XXX How to log and notify peer? */ + return -1; + } + /* Generate a nonce, and add it to the message. */ + if (exchange_gen_nonce(msg, nonce_sz)) { + /* XXX Log? */ + return -1; + } + /* Try to add certificates which are acceptable for the CERTREQs */ + if (exchange_add_certs(msg)) { + /* XXX Log? */ + return -1; + } +#if defined (USE_NAT_TRAVERSAL) + /* If this exchange uses NAT-Traversal, add NAT-D payloads now. */ + if (msg->exchange->flags & EXCHANGE_FLAG_NAT_T_CAP_PEER) + if (nat_t_exchange_add_nat_d(msg)) { + /* XXX Log? */ + return -1; + } +#endif + return 0; +} + +/* Receive our peer's public DH value and nonce. */ +int +ike_phase_1_recv_KE_NONCE(struct message *msg) +{ + /* Copy out the initiator's DH public value. */ + if (ipsec_save_g_x(msg)) { + /* XXX How to log and notify peer? */ + return -1; + } + /* Copy out the initiator's nonce. */ + if (exchange_save_nonce(msg)) { + /* XXX How to log and notify peer? */ + return -1; + } + /* Copy out the initiator's cert requests. */ + if (exchange_save_certreq(msg)) { + /* XXX How to log and notify peer? */ + return -1; + } +#if defined (USE_NAT_TRAVERSAL) + /* MainMode: Check for NAT-D payloads and contents. */ + if (msg->exchange->type == ISAKMP_EXCH_ID_PROT && + msg->exchange->flags & EXCHANGE_FLAG_NAT_T_CAP_PEER) + (void)nat_t_exchange_check_nat_d(msg); +#endif + return 0; +} + +/* + * Compute DH values and key material. This is done in a post-send function + * as that means we can do parallel work in both the initiator and responder + * thus speeding up exchanges. + */ +int +ike_phase_1_post_exchange_KE_NONCE(struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct prf *prf; + struct hash *hash = ie->hash; + enum cryptoerr err; + + /* Compute Diffie-Hellman shared value. */ + ie->g_xy = malloc(ie->g_x_len); + if (!ie->g_xy) { + /* XXX How to notify peer? */ + log_error("ike_phase_1_post_exchange_KE_NONCE: " + "malloc (%lu) failed", (unsigned long) ie->g_x_len); + return -1; + } + if (dh_create_shared(ie->group, ie->g_xy, + exchange->initiator ? ie->g_xr : ie->g_xi)) { + log_print("ike_phase_1_post_exchange_KE_NONCE: " + "dh_create_shared failed"); + return -1; + } + LOG_DBG_BUF((LOG_NEGOTIATION, 80, + "ike_phase_1_post_exchange_KE_NONCE: g^xy", ie->g_xy, + ie->g_x_len)); + + /* Compute the SKEYID depending on the authentication method. */ + ie->skeyid = ie->ike_auth->gen_skeyid(exchange, &ie->skeyid_len); + if (!ie->skeyid) { + /* XXX Log and teardown? */ + return -1; + } + LOG_DBG_BUF((LOG_NEGOTIATION, 80, + "ike_phase_1_post_exchange_KE_NONCE: SKEYID", ie->skeyid, + ie->skeyid_len)); + + /* SKEYID_d. */ + ie->skeyid_d = malloc(ie->skeyid_len); + if (!ie->skeyid_d) { + /* XXX How to notify peer? */ + log_error("ike_phase_1_post_exchange_KE_NONCE: " + "malloc (%lu) failed", (unsigned long) ie->skeyid_len); + return -1; + } + prf = prf_alloc(ie->prf_type, hash->type, ie->skeyid, ie->skeyid_len); + if (!prf) { + /* XXX Log and teardown? */ + return -1; + } + prf->Init(prf->prfctx); + prf->Update(prf->prfctx, ie->g_xy, ie->g_x_len); + prf->Update(prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); + prf->Update(prf->prfctx, (unsigned char *) "\0", 1); + prf->Final(ie->skeyid_d, prf->prfctx); + LOG_DBG_BUF((LOG_NEGOTIATION, 80, + "ike_phase_1_post_exchange_KE_NONCE: SKEYID_d", ie->skeyid_d, + ie->skeyid_len)); + + /* SKEYID_a. */ + ie->skeyid_a = malloc(ie->skeyid_len); + if (!ie->skeyid_a) { + log_error("ike_phase_1_post_exchange_KE_NONCE: " + "malloc (%lu) failed", (unsigned long) ie->skeyid_len); + prf_free(prf); + return -1; + } + prf->Init(prf->prfctx); + prf->Update(prf->prfctx, ie->skeyid_d, ie->skeyid_len); + prf->Update(prf->prfctx, ie->g_xy, ie->g_x_len); + prf->Update(prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); + prf->Update(prf->prfctx, (unsigned char *) "\1", 1); + prf->Final(ie->skeyid_a, prf->prfctx); + LOG_DBG_BUF((LOG_NEGOTIATION, 80, + "ike_phase_1_post_exchange_KE_NONCE: SKEYID_a", ie->skeyid_a, + ie->skeyid_len)); + + /* SKEYID_e. */ + ie->skeyid_e = malloc(ie->skeyid_len); + if (!ie->skeyid_e) { + /* XXX How to notify peer? */ + log_error("ike_phase_1_post_exchange_KE_NONCE: " + "malloc (%lu) failed", (unsigned long) ie->skeyid_len); + prf_free(prf); + return -1; + } + prf->Init(prf->prfctx); + prf->Update(prf->prfctx, ie->skeyid_a, ie->skeyid_len); + prf->Update(prf->prfctx, ie->g_xy, ie->g_x_len); + prf->Update(prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); + prf->Update(prf->prfctx, (unsigned char *) "\2", 1); + prf->Final(ie->skeyid_e, prf->prfctx); + prf_free(prf); + LOG_DBG_BUF((LOG_NEGOTIATION, 80, + "ike_phase_1_post_exchange_KE_NONCE: SKEYID_e", ie->skeyid_e, + ie->skeyid_len)); + + /* Key length determination. */ + if (!exchange->key_length) + exchange->key_length = exchange->crypto->keymax; + + /* Derive a longer key from skeyid_e */ + if (ie->skeyid_len < exchange->key_length) { + u_int16_t len, keylen; + u_int8_t *key, *p; + + prf = prf_alloc(ie->prf_type, hash->type, ie->skeyid_e, + ie->skeyid_len); + if (!prf) { + /* XXX - notify peer */ + return -1; + } + /* Make keylen a multiple of prf->blocksize */ + keylen = exchange->key_length; + if (keylen % prf->blocksize) + keylen += prf->blocksize - (keylen % prf->blocksize); + + key = malloc(keylen); + if (!key) { + /* XXX - Notify peer. */ + log_error("ike_phase_1_post_exchange_KE_NONCE: " + "malloc (%d) failed", keylen); + return -1; + } + prf->Init(prf->prfctx); + prf->Update(prf->prfctx, (unsigned char *) "\0", 1); + prf->Final(key, prf->prfctx); + + for (len = prf->blocksize, p = key; len < exchange->key_length; + len += prf->blocksize, p += prf->blocksize) { + prf->Init(prf->prfctx); + prf->Update(prf->prfctx, p, prf->blocksize); + prf->Final(p + prf->blocksize, prf->prfctx); + } + prf_free(prf); + + /* Setup our keystate using the derived encryption key. */ + exchange->keystate = crypto_init(exchange->crypto, key, + exchange->key_length, &err); + + free(key); + } else + /* Setup our keystate using the raw skeyid_e. */ + exchange->keystate = crypto_init(exchange->crypto, + ie->skeyid_e, exchange->key_length, &err); + + /* Special handling for DES weak keys. */ + if (!exchange->keystate && err == EWEAKKEY && + (exchange->key_length << 1) <= ie->skeyid_len) { + log_print("ike_phase_1_post_exchange_KE_NONCE: " + "weak key, trying subseq. skeyid_e"); + exchange->keystate = crypto_init(exchange->crypto, + ie->skeyid_e + exchange->key_length, + exchange->key_length, &err); + } + if (!exchange->keystate) { + log_print("ike_phase_1_post_exchange_KE_NONCE: " + "exchange->crypto->init () failed: %d", err); + + /* + * XXX We really need to know if problems are of transient + * nature or fatal (like failed assertions etc.) + */ + return -1; + } + /* Setup IV. XXX Only for CBC transforms, no? */ + hash->Init(hash->ctx); + hash->Update(hash->ctx, ie->g_xi, ie->g_x_len); + hash->Update(hash->ctx, ie->g_xr, ie->g_x_len); + hash->Final(hash->digest, hash->ctx); + crypto_init_iv(exchange->keystate, hash->digest, + exchange->crypto->blocksize); + return 0; +} + +int +ike_phase_1_responder_send_ID_AUTH(struct message *msg) +{ + if (ike_phase_1_send_ID(msg)) + return -1; + + return ike_phase_1_send_AUTH(msg); +} + +int +ike_phase_1_send_ID(struct message *msg) +{ + struct exchange *exchange = msg->exchange; + u_int8_t *buf; + char header[80]; + ssize_t sz; + struct sockaddr *src; + int initiator = exchange->initiator; + u_int8_t **id; + size_t *id_len; + char *my_id = 0, *data; + u_int8_t id_type; + + /* Choose the right fields to fill-in. */ + id = initiator ? &exchange->id_i : &exchange->id_r; + id_len = initiator ? &exchange->id_i_len : &exchange->id_r_len; + + if (exchange->name) + my_id = conf_get_str(exchange->name, "ID"); + + if (!my_id) + my_id = conf_get_str("General", "Default-phase-1-ID"); + + msg->transport->vtbl->get_src(msg->transport, &src); + sz = my_id ? ipsec_id_size(my_id, &id_type) : sockaddr_addrlen(src); + if (sz == -1) + return -1; + + sz += ISAKMP_ID_DATA_OFF; + buf = malloc(sz); + if (!buf) { + log_error("ike_phase_1_send_ID: malloc (%lu) failed", + (unsigned long) sz); + return -1; + } + SET_IPSEC_ID_PROTO(buf + ISAKMP_ID_DOI_DATA_OFF, 0); + SET_IPSEC_ID_PORT(buf + ISAKMP_ID_DOI_DATA_OFF, 0); + if (my_id) { + SET_ISAKMP_ID_TYPE(buf, id_type); + switch (id_type) { + case IPSEC_ID_IPV4_ADDR: + case IPSEC_ID_IPV6_ADDR: + /* Already in network byteorder. */ + memcpy(buf + ISAKMP_ID_DATA_OFF, + sockaddr_addrdata(src), sockaddr_addrlen(src)); + break; + + case IPSEC_ID_IPV4_ADDR_SUBNET: + case IPSEC_ID_IPV6_ADDR_SUBNET: + /* Network */ + data = conf_get_str(my_id, "Network"); + if (!data) { + log_print("ike_phase_1_send_ID: section %s " + "has no \"Network\" tag", my_id); + return -1; + } + if (text2sockaddr(data, NULL, &src)) { + log_error("ike_phase_1_send_ID: " + "text2sockaddr() failed"); + return -1; + } + memcpy(buf + ISAKMP_ID_DATA_OFF, + sockaddr_addrdata(src), sockaddr_addrlen(src)); + free(src); + /* Netmask */ + data = conf_get_str(my_id, "Netmask"); + if (!data) { + log_print("ike_phase_1_send_ID: section %s " + "has no \"Netmask\" tag", my_id); + return -1; + } + if (text2sockaddr(data, NULL, &src)) { + log_error("ike_phase_1_send_ID: " + "text2sockaddr() failed"); + return -1; + } + memcpy(buf + ISAKMP_ID_DATA_OFF + + sockaddr_addrlen(src), sockaddr_addrdata(src), + sockaddr_addrlen(src)); + free(src); + break; + + case IPSEC_ID_FQDN: + case IPSEC_ID_USER_FQDN: + case IPSEC_ID_KEY_ID: + data = conf_get_str(my_id, "Name"); + if (!data) { + log_print("ike_phase_1_send_ID: section %s " + "has no \"Name\" tag", my_id); + return -1; + } + memcpy(buf + ISAKMP_ID_DATA_OFF, data, + sz - ISAKMP_ID_DATA_OFF); + break; + + default: + log_print("ike_phase_1_send_ID: " + "unsupported ID type %d", id_type); + free(buf); + return -1; + } + } else { + switch (src->sa_family) { + case AF_INET: + SET_ISAKMP_ID_TYPE(buf, IPSEC_ID_IPV4_ADDR); + break; + case AF_INET6: + SET_ISAKMP_ID_TYPE(buf, IPSEC_ID_IPV6_ADDR); + break; + } + /* Already in network byteorder. */ + memcpy(buf + ISAKMP_ID_DATA_OFF, sockaddr_addrdata(src), + sockaddr_addrlen(src)); + } + + if (message_add_payload(msg, ISAKMP_PAYLOAD_ID, buf, sz, 1)) { + free(buf); + return -1; + } + *id_len = sz - ISAKMP_GEN_SZ; + *id = malloc(*id_len); + if (!*id) { + log_error("ike_phase_1_send_ID: malloc (%lu) failed", + (unsigned long) *id_len); + return -1; + } + memcpy(*id, buf + ISAKMP_GEN_SZ, *id_len); + snprintf(header, sizeof header, "ike_phase_1_send_ID: %s", + constant_name(ipsec_id_cst, GET_ISAKMP_ID_TYPE(buf))); + LOG_DBG_BUF((LOG_NEGOTIATION, 40, header, buf + ISAKMP_ID_DATA_OFF, + sz - ISAKMP_ID_DATA_OFF)); + return 0; +} + +int +ike_phase_1_send_AUTH(struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + + if (ie->ike_auth->encode_hash(msg)) { + /* XXX Log? */ + return -1; + } + /* + * XXX Many people say the COMMIT flag is just junk, especially in + * Phase 1. + */ +#ifdef notyet + if ((exchange->flags & EXCHANGE_FLAG_COMMITTED) == 0) + exchange->flags |= EXCHANGE_FLAG_I_COMMITTED; +#endif + + return 0; +} + +/* Receive ID and HASH and check that the exchange has been consistent. */ +int +ike_phase_1_recv_ID_AUTH(struct message *msg) +{ + if (ike_phase_1_recv_ID(msg)) + return -1; + + return ike_phase_1_recv_AUTH(msg); +} + +/* Receive ID. */ +int +ike_phase_1_recv_ID(struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct payload *payload; + char header[80], *rs = 0, *rid = 0, *p; + int initiator = exchange->initiator; + u_int8_t **id, id_type; + size_t *id_len; + ssize_t sz; + struct sockaddr *sa; + + payload = payload_first(msg, ISAKMP_PAYLOAD_ID); + + if (exchange->name) + rs = conf_get_str(exchange->name, "Remote-ID"); + + if (rs) { + sz = ipsec_id_size(rs, &id_type); + if (sz == -1) { + log_print("ike_phase_1_recv_ID: could not handle " + "specified Remote-ID [%s]", rs); + return -1; + } + rid = malloc(sz); + if (!rid) { + log_error("ike_phase_1_recv_ID: malloc (%lu) failed", + (unsigned long) sz); + return -1; + } + switch (id_type) { + case IPSEC_ID_IPV4_ADDR: + case IPSEC_ID_IPV6_ADDR: + p = conf_get_str(rs, "Address"); + if (!p) { + log_print("ike_phase_1_recv_ID: failed to get " + "Address in Remote-ID section [%s]", rs); + free(rid); + return -1; + } + if (text2sockaddr(p, 0, &sa) == -1) { + log_print("ike_phase_1_recv_ID: " + "failed to parse address %s", p); + free(rid); + return -1; + } + if ((id_type == IPSEC_ID_IPV4_ADDR && + sa->sa_family != AF_INET) || + (id_type == IPSEC_ID_IPV6_ADDR && + sa->sa_family != AF_INET6)) { + log_print("ike_phase_1_recv_ID: " + "address %s not of expected family", p); + free(rid); + free(sa); + return -1; + } + memcpy(rid, sockaddr_addrdata(sa), + sockaddr_addrlen(sa)); + free(sa); + break; + + case IPSEC_ID_FQDN: + case IPSEC_ID_USER_FQDN: + case IPSEC_ID_KEY_ID: + p = conf_get_str(rs, "Name"); + if (!p) { + log_print("ike_phase_1_recv_ID: failed to " + "get Name in Remote-ID section [%s]", rs); + free(rid); + return -1; + } + memcpy(rid, p, sz); + break; + + default: + log_print("ike_phase_1_recv_ID: " + "unsupported ID type %d", id_type); + free(rid); + return -1; + } + + /* Compare expected/desired and received remote ID */ + if (bcmp(rid, payload->p + ISAKMP_ID_DATA_OFF, sz)) { + log_print("ike_phase_1_recv_ID: " + "received remote ID other than expected %s - %s", p, payload->p); + free(rid); + return -1; + } + free(rid); + } + /* Choose the right fields to fill in */ + id = initiator ? &exchange->id_r : &exchange->id_i; + id_len = initiator ? &exchange->id_r_len : &exchange->id_i_len; + + *id_len = GET_ISAKMP_GEN_LENGTH(payload->p) - ISAKMP_GEN_SZ; + *id = malloc(*id_len); + if (!*id) { + log_error("ike_phase_1_recv_ID: malloc (%lu) failed", + (unsigned long) *id_len); + return -1; + } + memcpy(*id, payload->p + ISAKMP_GEN_SZ, *id_len); + snprintf(header, sizeof header, "ike_phase_1_recv_ID: %s", + constant_name(ipsec_id_cst, GET_ISAKMP_ID_TYPE(payload->p))); + LOG_DBG_BUF((LOG_NEGOTIATION, 40, header, + payload->p + ISAKMP_ID_DATA_OFF, + *id_len + ISAKMP_GEN_SZ - ISAKMP_ID_DATA_OFF)); + payload->flags |= PL_MARK; + return 0; +} + +/* Receive HASH and check that the exchange has been consistent. */ +int +ike_phase_1_recv_AUTH(struct message *msg) +{ + struct exchange *exchange = msg->exchange; + struct ipsec_exch *ie = exchange->data; + struct prf *prf; + struct hash *hash = ie->hash; + char header[80]; + size_t hashsize = hash->hashsize; + int initiator = exchange->initiator; + u_int8_t **hash_p, *id; + size_t id_len; + + /* Choose the right fields to fill in */ + hash_p = initiator ? &ie->hash_r : &ie->hash_i; + id = initiator ? exchange->id_r : exchange->id_i; + id_len = initiator ? exchange->id_r_len : exchange->id_i_len; + + /* The decoded hash will be in ie->hash_r or ie->hash_i */ + if (ie->ike_auth->decode_hash(msg)) { + message_drop(msg, ISAKMP_NOTIFY_INVALID_ID_INFORMATION, 0, 1, + 0); + return -1; + } + /* Allocate the prf and start calculating his HASH. */ + prf = prf_alloc(ie->prf_type, hash->type, ie->skeyid, ie->skeyid_len); + if (!prf) { + /* XXX Log? */ + return -1; + } + prf->Init(prf->prfctx); + prf->Update(prf->prfctx, initiator ? ie->g_xr : ie->g_xi, ie->g_x_len); + prf->Update(prf->prfctx, initiator ? ie->g_xi : ie->g_xr, ie->g_x_len); + prf->Update(prf->prfctx, exchange->cookies + + (initiator ? ISAKMP_HDR_RCOOKIE_OFF : ISAKMP_HDR_ICOOKIE_OFF), + ISAKMP_HDR_ICOOKIE_LEN); + prf->Update(prf->prfctx, exchange->cookies + + (initiator ? ISAKMP_HDR_ICOOKIE_OFF : ISAKMP_HDR_RCOOKIE_OFF), + ISAKMP_HDR_ICOOKIE_LEN); + prf->Update(prf->prfctx, ie->sa_i_b, ie->sa_i_b_len); + prf->Update(prf->prfctx, id, id_len); + prf->Final(hash->digest, prf->prfctx); + prf_free(prf); + snprintf(header, sizeof header, "ike_phase_1_recv_AUTH: " + "computed HASH_%c", initiator ? 'R' : 'I'); + LOG_DBG_BUF((LOG_NEGOTIATION, 80, header, hash->digest, hashsize)); + + /* Check that the hash we got matches the one we computed. */ + if (memcmp(*hash_p, hash->digest, hashsize) != 0) { + /* XXX Log? */ + return -1; + } + + /* Mark message as authenticated. */ + msg->flags |= MSG_AUTHENTICATED; + + return 0; +} + +struct attr_node { + LIST_ENTRY(attr_node) link; + u_int16_t type; +}; + +struct validation_state { + struct conf_list_node *xf; + LIST_HEAD(attr_head, attr_node) attrs; + char *life; +}; + +/* Validate a proposal inside SA according to EXCHANGE's policy. */ +static int +ike_phase_1_validate_prop(struct exchange *exchange, struct sa *sa, + struct sa *isakmp_sa) +{ + struct conf_list *conf, *tags; + struct conf_list_node *xf, *tag; + struct proto *proto; + struct validation_state vs; + struct attr_node *node, *next_node; + + /* Get the list of transforms. */ + conf = conf_get_list(exchange->policy, "Transforms"); + if (!conf) + return 0; + + for (xf = TAILQ_FIRST(&conf->fields); xf; xf = TAILQ_NEXT(xf, link)) { + for (proto = TAILQ_FIRST(&sa->protos); proto; + proto = TAILQ_NEXT(proto, link)) { + /* Mark all attributes in our policy as unseen. */ + LIST_INIT(&vs.attrs); + vs.xf = xf; + vs.life = 0; + if (attribute_map(proto->chosen->p + + ISAKMP_TRANSFORM_SA_ATTRS_OFF, + GET_ISAKMP_GEN_LENGTH(proto->chosen->p) - + ISAKMP_TRANSFORM_SA_ATTRS_OFF, + attribute_unacceptable, &vs)) + goto try_next; + + /* Sweep over unseen tags in this section. */ + tags = conf_get_tag_list(xf->field); + if (tags) { + for (tag = TAILQ_FIRST(&tags->fields); tag; + tag = TAILQ_NEXT(tag, link)) + /* + * XXX Should we care about attributes + * we have, they do not provide? + */ + for (node = LIST_FIRST(&vs.attrs); + node; node = next_node) { + next_node = + LIST_NEXT(node, link); + if (node->type == + constant_value(ike_attr_cst, + tag->field)) { + LIST_REMOVE(node, link); + free(node); + } + } + conf_free_list(tags); + } + /* Are there leftover tags in this section? */ + node = LIST_FIRST(&vs.attrs); + if (node) + goto try_next; + } + + /* All protocols were OK, we succeeded. */ + LOG_DBG((LOG_NEGOTIATION, 20, "ike_phase_1_validate_prop: " + "success")); + conf_free_list(conf); + if (vs.life) + free(vs.life); + return 1; + +try_next: + /* Are there leftover tags in this section? */ + node = LIST_FIRST(&vs.attrs); + while (node) { + LIST_REMOVE(node, link); + free(node); + node = LIST_FIRST(&vs.attrs); + } + if (vs.life) + free(vs.life); + } + + LOG_DBG((LOG_NEGOTIATION, 20, "ike_phase_1_validate_prop: failure")); + conf_free_list(conf); + return 0; +} + +/* + * Look at the attribute of type TYPE, located at VALUE for LEN bytes forward. + * The VVS argument holds a validation state kept across invocations. + * If the attribute is unacceptable to use, return non-zero, otherwise zero. + */ +static int +attribute_unacceptable(u_int16_t type, u_int8_t *value, u_int16_t len, + void *vvs) +{ + struct validation_state *vs = vvs; + struct conf_list *life_conf; + struct conf_list_node *xf = vs->xf, *life; + char *tag = constant_lookup(ike_attr_cst, type); + char *str; + struct constant_map *map; + struct attr_node *node; + int rv; + + if (!tag) { + LOG_DBG((LOG_NEGOTIATION, 60, "attribute_unacceptable: " + "attribute type %d not known", type)); + return 1; + } + switch (type) { + case IKE_ATTR_ENCRYPTION_ALGORITHM: + case IKE_ATTR_HASH_ALGORITHM: + case IKE_ATTR_AUTHENTICATION_METHOD: + case IKE_ATTR_GROUP_DESCRIPTION: + case IKE_ATTR_GROUP_TYPE: + case IKE_ATTR_PRF: + str = conf_get_str(xf->field, tag); + if (!str) { + /* This attribute does not exist in this policy. */ + LOG_DBG((LOG_NEGOTIATION, 70, + "attribute_unacceptable: attr %s does not exist " + "in %s", tag, xf->field)); + return 1; + } + map = constant_link_lookup(ike_attr_cst, type); + if (!map) + return 1; + + if ((constant_value(map, str) == decode_16(value)) || + (!strcmp(str, "ANY"))) { + /* Mark this attribute as seen. */ + node = malloc(sizeof *node); + if (!node) { + log_error("attribute_unacceptable: " + "malloc (%lu) failed", + (unsigned long) sizeof *node); + return 1; + } + node->type = type; + LIST_INSERT_HEAD(&vs->attrs, node, link); + return 0; + } + LOG_DBG((LOG_NEGOTIATION, 70, + "attribute_unacceptable: %s: got %s, expected %s", tag, + constant_name(map, decode_16(value)), str)); + return 1; + + case IKE_ATTR_GROUP_PRIME: + case IKE_ATTR_GROUP_GENERATOR_1: + case IKE_ATTR_GROUP_GENERATOR_2: + case IKE_ATTR_GROUP_CURVE_A: + case IKE_ATTR_GROUP_CURVE_B: + /* XXX Bignums not handled yet. */ + return 1; + + case IKE_ATTR_LIFE_TYPE: + case IKE_ATTR_LIFE_DURATION: + life_conf = conf_get_list(xf->field, "Life"); + if (life_conf && + !strcmp(conf_get_str(xf->field, "Life"), "ANY")) + return 0; + + rv = 1; + if (!life_conf) { + /* Life attributes given, but not in our policy. */ + LOG_DBG((LOG_NEGOTIATION, 70, + "attribute_unacceptable: received unexpected life " + "attribute")); + return 1; + } + /* + * Each lifetime type must match, otherwise we turn the + * proposal down. In order to do this we need to find the + * specific section of our policy's "Life" list and match + * its duration. + */ + switch (type) { + case IKE_ATTR_LIFE_TYPE: + for (life = TAILQ_FIRST(&life_conf->fields); life; + life = TAILQ_NEXT(life, link)) { + str = conf_get_str(life->field, "LIFE_TYPE"); + if (!str) { + LOG_DBG((LOG_NEGOTIATION, 70, + "attribute_unacceptable: " + "section [%s] has no LIFE_TYPE", + life->field)); + continue; + } + + /* + * If this is the type we are looking at, + * to save a pointer this section in vs->life. + */ + if (constant_value(ike_duration_cst, str) == + decode_16(value)) { + vs->life = strdup(life->field); + rv = 0; + goto bail_out; + } + } + LOG_DBG((LOG_NEGOTIATION, 70, "attribute_unacceptable:" + " unrecognized LIFE_TYPE %d", decode_16(value))); + vs->life = 0; + break; + + case IKE_ATTR_LIFE_DURATION: + if (!vs->life) { + LOG_DBG((LOG_NEGOTIATION, 70, + "attribute_unacceptable: " + "LIFE_DURATION without LIFE_TYPE")); + rv = 1; + goto bail_out; + } + str = conf_get_str(vs->life, "LIFE_DURATION"); + if (str) { + if (!strcmp(str, "ANY")) + rv = 0; + else + rv = !conf_match_num(vs->life, + "LIFE_DURATION", + len == 4 ? decode_32(value) : + decode_16(value)); + } else { + LOG_DBG((LOG_NEGOTIATION, 70, + "attribute_unacceptable: section [%s] has " + "no LIFE_DURATION", vs->life)); + rv = 1; + } + + free(vs->life); + vs->life = 0; + break; + } + +bail_out: + conf_free_list(life_conf); + return rv; + + case IKE_ATTR_KEY_LENGTH: + case IKE_ATTR_FIELD_SIZE: + case IKE_ATTR_GROUP_ORDER: + if (conf_match_num(xf->field, tag, decode_16(value))) { + /* Mark this attribute as seen. */ + node = malloc(sizeof *node); + if (!node) { + log_error("attribute_unacceptable: " + "malloc (%lu) failed", + (unsigned long) sizeof *node); + return 1; + } + node->type = type; + LIST_INSERT_HEAD(&vs->attrs, node, link); + return 0; + } + return 1; + } + return 1; +} |