File: [local] / src / sbin / isakmpd / ike_phase_1.c (download)
Revision 1.78, Thu Sep 20 11:49:55 2018 UTC (5 years, 8 months ago) by jsg
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, OPENBSD_6_5_BASE, OPENBSD_6_5, OPENBSD_6_4_BASE, OPENBSD_6_4, HEAD Changes since 1.77: +3 -2 lines
add missing braces implied by indentation
ok millert@ mpi@
|
/* $OpenBSD: ike_phase_1.c,v 1.78 2018/09/20 11:49:55 jsg 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 "attribute.h"
#include "conf.h"
#include "constants.h"
#include "crypto.h"
#include "dh.h"
#include "doi.h"
#include "dpd.h"
#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 "message.h"
#include "nat_traversal.h"
#include "prf.h"
#include "sa.h"
#include "transport.h"
#include "util.h"
#include "vendor.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 = calloc(1, sizeof *pa);
if (!pa)
goto bail_out;
pa->len = transform_len[i];
pa->attrs = 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 = TAILQ_FIRST(&msg->payload[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];
}
/* Advertise OpenBSD isakmpd. */
if (add_vendor_openbsd(msg))
goto bail_out;
/* Advertise NAT-T capability. */
if (nat_t_add_vendor_payloads(msg))
goto bail_out;
/* Advertise DPD capability. */
if (dpd_add_vendor_payload(msg))
goto bail_out;
conf_free_list(conf);
free(transform);
free(transform_len);
return 0;
bail_out:
free(sa_buf);
free(proposal);
if (transform) {
for (i = 0; i < conf->cnt; i++)
free(transform[i]);
free(transform);
}
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;
/* Advertise OpenBSD isakmpd. */
if (add_vendor_openbsd(msg))
return -1;
/* Advertise NAT-T capability. */
if (nat_t_add_vendor_payloads(msg))
return -1;
/* Advertise DPD capability. */
if (dpd_add_vendor_payload(msg))
return -1;
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;
}
/* Are there any CERTREQs to send? */
if (exchange_add_certreqs(msg)) {
/* XXX Log? */
return -1;
}
/* Try to add certificates which are acceptable for the CERTREQs */
if (exchange_add_certs(msg)) {
/* XXX Log? */
return -1;
}
/* 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;
}
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;
}
/* 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);
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_len = dh_secretlen(ie->group);
ie->g_xy = malloc(ie->g_xy_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_xy_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_xy_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_xy_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_xy_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_xy_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. */
prf_free(prf);
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;
sa_family_t af = 0;
/* 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_IPV4_ADDR_SUBNET:
af = AF_INET;
break;
case IPSEC_ID_IPV6_ADDR:
case IPSEC_ID_IPV6_ADDR_SUBNET:
af = AF_INET6;
break;
}
switch (id_type) {
case IPSEC_ID_IPV4_ADDR:
case IPSEC_ID_IPV6_ADDR:
data = conf_get_str(my_id, "Address");
if (!data) {
log_print("ike_phase_1_send_ID: section %s "
"has no \"Address\" tag", my_id);
free(buf);
return -1;
}
if (text2sockaddr(data, NULL, &src, af, 0)) {
log_error("ike_phase_1_send_ID: "
"text2sockaddr() failed");
free(buf);
return -1;
}
memcpy(buf + ISAKMP_ID_DATA_OFF,
sockaddr_addrdata(src), sockaddr_addrlen(src));
free(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);
free(buf);
return -1;
}
if (text2sockaddr(data, NULL, &src, af, 0)) {
log_error("ike_phase_1_send_ID: "
"text2sockaddr() failed");
free(buf);
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);
free(buf);
return -1;
}
if (text2sockaddr(data, NULL, &src, af, 1)) {
log_error("ike_phase_1_send_ID: "
"text2sockaddr() failed");
free(buf);
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);
free(buf);
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;
sa_family_t af = 0;
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:
af = AF_INET;
break;
case IPSEC_ID_IPV6_ADDR:
af = AF_INET6;
break;
}
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, af, 0) == -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 (memcmp(rid, payload->p + ISAKMP_ID_DATA_OFF, sz) != 0) {
free(rid);
log_print("ike_phase_1_recv_ID: "
"received remote ID other than expected %s", p);
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);
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);
}
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, dur = 0;
if (!tag) {
log_print("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_print("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_print("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. */
log_print("attribute_unacceptable: "
"bignum type %d not supported", type);
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")) {
conf_free_list(life_conf);
return 0;
}
rv = 1;
if (!life_conf) {
/* Life attributes given, but not in our policy. */
log_print("attribute_unacceptable: "
"life attribute received, none in policy");
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_print("attribute_unacceptable: "
"section [%s] has no LIFE_TYPE",
life->field);
continue;
}
/*
* If this is the type we are looking at,
* save a pointer to 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_print("attribute_unacceptable: "
"unrecognized LIFE_TYPE %d", decode_16(value));
vs->life = 0;
break;
case IKE_ATTR_LIFE_DURATION:
if (!vs->life) {
log_print("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 {
dur = (len == 4) ? decode_32(value) :
decode_16(value);
if ((rv = !conf_match_num(vs->life,
"LIFE_DURATION", dur))) {
log_print(
"attribute_unacceptable: "
"LIFE_DURATION: got %d, "
" expected %s", dur, str);
}
}
} else {
log_print("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;
default:
log_print("attribute_unacceptable: unexpected type %d",
type);
}
return 1;
}