/* $KAME: ipsec_doi.c,v 1.164 2003/10/23 07:22:45 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * 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. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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. */ #include __RCSID("$NetBSD: ipsec_doi.c,v 1.14 2003/10/23 07:23:50 itojun Exp $"); #include #include #include #include #include #ifdef IPV6_INRIA_VERSION #include #else #include #endif #include #include #include #include #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #include "var.h" #include "vmbuf.h" #include "misc.h" #include "plog.h" #include "debug.h" #include "cfparse_proto.h" #include "isakmp_var.h" #include "isakmp.h" #include "ipsec_doi.h" #include "oakley.h" #include "remoteconf.h" #include "localconf.h" #include "sockmisc.h" #include "handler.h" #include "policy.h" #include "algorithm.h" #include "sainfo.h" #include "proposal.h" #include "crypto_openssl.h" #include "strnames.h" #include "gcmalloc.h" #ifdef HAVE_GSSAPI #include "gssapi.h" #endif int verbose_proposal_check = 1; static vchar_t *get_ph1approval __P((struct ph1handle *, struct prop_pair **)); static struct isakmpsa *get_ph1approvalx __P((struct prop_pair *, struct isakmpsa *, struct isakmpsa *)); static void print_ph1mismatched __P((struct prop_pair *, struct isakmpsa *)); static int t2isakmpsa __P((struct isakmp_pl_t *, struct isakmpsa *)); static int cmp_aproppair_i __P((struct prop_pair *, struct prop_pair *)); static struct prop_pair *get_ph2approval __P((struct ph2handle *, struct prop_pair **)); static struct prop_pair *get_ph2approvalx __P((struct ph2handle *, struct prop_pair *)); static void free_proppair0 __P((struct prop_pair *)); static int get_transform __P((struct isakmp_pl_p *, struct prop_pair **, int *, int)); static u_int32_t ipsecdoi_set_ld __P((vchar_t *)); static int check_doi __P((u_int32_t)); static int check_situation __P((u_int32_t)); static int check_prot_main __P((int)); static int check_prot_quick __P((int)); static int (*check_protocol[]) __P((int)) = { check_prot_main, /* IPSECDOI_TYPE_PH1 */ check_prot_quick, /* IPSECDOI_TYPE_PH2 */ }; static int check_spi_size __P((int, int)); static int check_trns_isakmp __P((int)); static int check_trns_ah __P((int)); static int check_trns_esp __P((int)); static int check_trns_ipcomp __P((int)); static int (*check_transform[]) __P((int)) = { 0, check_trns_isakmp, /* IPSECDOI_PROTO_ISAKMP */ check_trns_ah, /* IPSECDOI_PROTO_IPSEC_AH */ check_trns_esp, /* IPSECDOI_PROTO_IPSEC_ESP */ check_trns_ipcomp, /* IPSECDOI_PROTO_IPCOMP */ }; static int check_attr_isakmp __P((struct isakmp_pl_t *)); static int check_attr_ah __P((struct isakmp_pl_t *)); static int check_attr_esp __P((struct isakmp_pl_t *)); static int check_attr_ipsec __P((int, struct isakmp_pl_t *)); static int check_attr_ipcomp __P((struct isakmp_pl_t *)); static int (*check_attributes[]) __P((struct isakmp_pl_t *)) = { 0, check_attr_isakmp, /* IPSECDOI_PROTO_ISAKMP */ check_attr_ah, /* IPSECDOI_PROTO_IPSEC_AH */ check_attr_esp, /* IPSECDOI_PROTO_IPSEC_ESP */ check_attr_ipcomp, /* IPSECDOI_PROTO_IPCOMP */ }; static int setph1prop __P((struct isakmpsa *, caddr_t)); static int setph1trns __P((struct isakmpsa *, caddr_t)); static int setph1attr __P((struct isakmpsa *, caddr_t)); static vchar_t *setph2proposal0 __P((const struct ph2handle *, const struct saprop *, const struct saproto *)); static vchar_t *getidval __P((int, vchar_t *)); #ifdef HAVE_GSSAPI static struct isakmpsa *fixup_initiator_sa __P((struct isakmpsa *, struct isakmpsa *)); #endif /*%%%*/ /* * check phase 1 SA payload. * make new SA payload to be replyed not including general header. * the pointer to one of isakmpsa in proposal is set into iph1->approval. * OUT: * positive: the pointer to new buffer of SA payload. * network byte order. * NULL : error occurd. */ int ipsecdoi_checkph1proposal(sa, iph1) vchar_t *sa; struct ph1handle *iph1; { vchar_t *newsa; /* new SA payload approved. */ struct prop_pair **pair; /* get proposal pair */ pair = get_proppair(sa, IPSECDOI_TYPE_PH1); if (pair == NULL) return -1; /* check and get one SA for use */ newsa = get_ph1approval(iph1, pair); free_proppair(pair); if (newsa == NULL) return -1; iph1->sa_ret = newsa; return 0; } /* * acceptable check for remote configuration. * return a new SA payload to be reply to peer. */ static vchar_t * get_ph1approval(iph1, pair) struct ph1handle *iph1; struct prop_pair **pair; { vchar_t *newsa; struct isakmpsa *sa, tsa; struct prop_pair *s, *p; int prophlen; int i; iph1->approval = NULL; for (i = 0; i < MAXPROPPAIRLEN; i++) { if (pair[i] == NULL) continue; for (s = pair[i]; s; s = s->next) { prophlen = sizeof(struct isakmp_pl_p) + s->prop->spi_size; /* compare proposal and select one */ for (p = s; p; p = p->tnext) { sa = get_ph1approvalx(p, iph1->rmconf->proposal, &tsa); if (sa != NULL) goto found; } } } /* * if there is no suitable proposal, racoon complains about all of * mismatched items in those proposal. */ if (verbose_proposal_check) { for (i = 0; i < MAXPROPPAIRLEN; i++) { if (pair[i] == NULL) continue; for (s = pair[i]; s; s = s->next) { prophlen = sizeof(struct isakmp_pl_p) + s->prop->spi_size; for (p = s; p; p = p->tnext) { print_ph1mismatched(p, iph1->rmconf->proposal); } } } } plog(LLV_ERROR, LOCATION, NULL, "no suitable proposal found.\n"); return NULL; found: plog(LLV_DEBUG, LOCATION, NULL, "an acceptable proposal found.\n"); /* check DH group settings */ if (sa->dhgrp) { if (sa->dhgrp->prime && sa->dhgrp->gen1) { /* it's ok */ goto saok; } plog(LLV_WARNING, LOCATION, NULL, "invalid DH parameter found, use default.\n"); oakley_dhgrp_free(sa->dhgrp); } if (oakley_setdhgroup(sa->dh_group, &sa->dhgrp) == -1) { sa->dhgrp = NULL; return NULL; } saok: #ifdef HAVE_GSSAPI if (sa->gssid != NULL) plog(LLV_DEBUG, LOCATION, NULL, "gss id in new sa '%s'\n", sa->gssid->v); if (iph1-> side == INITIATOR) { if (iph1->rmconf->proposal->gssid != NULL) iph1->gi_i = vdup(iph1->rmconf->proposal->gssid); if (tsa.gssid != NULL) iph1->gi_r = vdup(tsa.gssid); iph1->approval = fixup_initiator_sa(sa, &tsa); } else { if (tsa.gssid != NULL) { iph1->gi_r = vdup(tsa.gssid); if (iph1->rmconf->proposal->gssid != NULL) iph1->gi_i = vdup(iph1->rmconf->proposal->gssid); else iph1->gi_i = gssapi_get_default_id(iph1); if (sa->gssid == NULL && iph1->gi_i != NULL) sa->gssid = vdup(iph1->gi_i); } iph1->approval = sa; } if (iph1->gi_i != NULL) plog(LLV_DEBUG, LOCATION, NULL, "GIi is %*s\n", iph1->gi_i->l, iph1->gi_i->v); if (iph1->gi_r != NULL) plog(LLV_DEBUG, LOCATION, NULL, "GIr is %*s\n", iph1->gi_r->l, iph1->gi_r->v); #else iph1->approval = sa; #endif newsa = get_sabyproppair(p, iph1); if (newsa == NULL) iph1->approval = NULL; return newsa; } /* * compare peer's single proposal and all of my proposal. * and select one if suiatable. * p : one of peer's proposal. * proposal: my proposals. */ static struct isakmpsa * get_ph1approvalx(p, proposal, sap) struct prop_pair *p; struct isakmpsa *proposal, *sap; { struct isakmp_pl_p *prop = p->prop; struct isakmp_pl_t *trns = p->trns; struct isakmpsa sa, *s, *tsap; plog(LLV_DEBUG, LOCATION, NULL, "prop#=%d, prot-id=%s, spi-size=%d, #trns=%d\n", prop->p_no, s_ipsecdoi_proto(prop->proto_id), prop->spi_size, prop->num_t); plog(LLV_DEBUG, LOCATION, NULL, "trns#=%d, trns-id=%s\n", trns->t_no, s_ipsecdoi_trns(prop->proto_id, trns->t_id)); tsap = sap != NULL ? sap : &sa; memset(tsap, 0, sizeof(*tsap)); if (t2isakmpsa(trns, tsap) < 0) return NULL; for (s = proposal; s != NULL; s = s->next) { plog(LLV_DEBUG, LOCATION, NULL, "Compared: DB:Peer\n"); plog(LLV_DEBUG, LOCATION, NULL, "(lifetime = %ld:%ld)\n", s->lifetime, tsap->lifetime); plog(LLV_DEBUG, LOCATION, NULL, "(lifebyte = %ld:%ld)\n", s->lifebyte, tsap->lifebyte); plog(LLV_DEBUG, LOCATION, NULL, "enctype = %s:%s\n", s_oakley_attr_v(OAKLEY_ATTR_ENC_ALG, s->enctype), s_oakley_attr_v(OAKLEY_ATTR_ENC_ALG, tsap->enctype)); plog(LLV_DEBUG, LOCATION, NULL, "(encklen = %d:%d)\n", s->encklen, tsap->encklen); plog(LLV_DEBUG, LOCATION, NULL, "hashtype = %s:%s\n", s_oakley_attr_v(OAKLEY_ATTR_HASH_ALG, s->hashtype), s_oakley_attr_v(OAKLEY_ATTR_HASH_ALG, tsap->hashtype)); plog(LLV_DEBUG, LOCATION, NULL, "authmethod = %s:%s\n", s_oakley_attr_v(OAKLEY_ATTR_AUTH_METHOD, s->authmethod), s_oakley_attr_v(OAKLEY_ATTR_AUTH_METHOD, tsap->authmethod)); plog(LLV_DEBUG, LOCATION, NULL, "dh_group = %s:%s\n", s_oakley_attr_v(OAKLEY_ATTR_GRP_DESC, s->dh_group), s_oakley_attr_v(OAKLEY_ATTR_GRP_DESC, tsap->dh_group)); #if 0 /* XXX to be considered */ if (tsap->lifetime > s->lifetime) ; if (tsap->lifebyte > s->lifebyte) ; #endif /* * if responder side and peer's key length in proposal * is bigger than mine, it might be accepted. */ if(tsap->enctype == s->enctype && tsap->authmethod == s->authmethod && tsap->hashtype == s->hashtype && tsap->dh_group == s->dh_group && tsap->encklen == s->encklen) break; } if (tsap->dhgrp != NULL) oakley_dhgrp_free(tsap->dhgrp); return s; } /* * print all of items in peer's proposal which are mismatched to my proposal. * p : one of peer's proposal. * proposal: my proposals. */ static void print_ph1mismatched(p, proposal) struct prop_pair *p; struct isakmpsa *proposal; { struct isakmpsa sa, *s; memset(&sa, 0, sizeof(sa)); if (t2isakmpsa(p->trns, &sa) < 0) return; for (s = proposal; s ; s = s->next) { if (sa.enctype != s->enctype) { plog(LLV_ERROR, LOCATION, NULL, "rejected enctype: " "DB(prop#%d:trns#%d):Peer(prop#%d:trns#%d) = " "%s:%s\n", s->prop_no, s->trns_no, p->prop->p_no, p->trns->t_no, s_oakley_attr_v(OAKLEY_ATTR_ENC_ALG, s->enctype), s_oakley_attr_v(OAKLEY_ATTR_ENC_ALG, sa.enctype)); } if (sa.authmethod != s->authmethod) { plog(LLV_ERROR, LOCATION, NULL, "rejected authmethod: " "DB(prop#%d:trns#%d):Peer(prop#%d:trns#%d) = " "%s:%s\n", s->prop_no, s->trns_no, p->prop->p_no, p->trns->t_no, s_oakley_attr_v(OAKLEY_ATTR_AUTH_METHOD, s->authmethod), s_oakley_attr_v(OAKLEY_ATTR_AUTH_METHOD, sa.authmethod)); } if (sa.hashtype != s->hashtype) { plog(LLV_ERROR, LOCATION, NULL, "rejected hashtype: " "DB(prop#%d:trns#%d):Peer(prop#%d:trns#%d) = " "%s:%s\n", s->prop_no, s->trns_no, p->prop->p_no, p->trns->t_no, s_oakley_attr_v(OAKLEY_ATTR_HASH_ALG, s->hashtype), s_oakley_attr_v(OAKLEY_ATTR_HASH_ALG, sa.hashtype)); } if (sa.dh_group != s->dh_group) { plog(LLV_ERROR, LOCATION, NULL, "rejected dh_group: " "DB(prop#%d:trns#%d):Peer(prop#%d:trns#%d) = " "%s:%s\n", s->prop_no, s->trns_no, p->prop->p_no, p->trns->t_no, s_oakley_attr_v(OAKLEY_ATTR_GRP_DESC, s->dh_group), s_oakley_attr_v(OAKLEY_ATTR_GRP_DESC, sa.dh_group)); } } if (sa.dhgrp != NULL) oakley_dhgrp_free(sa.dhgrp); } /* * get ISAKMP data attributes */ static int t2isakmpsa(trns, sa) struct isakmp_pl_t *trns; struct isakmpsa *sa; { struct isakmp_data *d, *prev; int flag, type; int error = -1; int life_t; int keylen = 0; vchar_t *val = NULL; int len, tlen; u_char *p; tlen = ntohs(trns->h.len) - sizeof(*trns); prev = (struct isakmp_data *)NULL; d = (struct isakmp_data *)(trns + 1); /* default */ life_t = OAKLEY_ATTR_SA_LD_TYPE_DEFAULT; sa->lifetime = OAKLEY_ATTR_SA_LD_SEC_DEFAULT; sa->lifebyte = 0; sa->dhgrp = racoon_calloc(1, sizeof(struct dhgroup)); if (!sa->dhgrp) goto err; while (tlen > 0) { type = ntohs(d->type) & ~ISAKMP_GEN_MASK; flag = ntohs(d->type) & ISAKMP_GEN_MASK; plog(LLV_DEBUG, LOCATION, NULL, "type=%s, flag=0x%04x, lorv=%s\n", s_oakley_attr(type), flag, s_oakley_attr_v(type, ntohs(d->lorv))); /* get variable-sized item */ switch (type) { case OAKLEY_ATTR_GRP_PI: case OAKLEY_ATTR_GRP_GEN_ONE: case OAKLEY_ATTR_GRP_GEN_TWO: case OAKLEY_ATTR_GRP_CURVE_A: case OAKLEY_ATTR_GRP_CURVE_B: case OAKLEY_ATTR_SA_LD: case OAKLEY_ATTR_GRP_ORDER: if (flag) { /*TV*/ len = 2; p = (u_char *)&d->lorv; } else { /*TLV*/ len = ntohs(d->lorv); p = (u_char *)(d + 1); } val = vmalloc(len); if (!val) return -1; memcpy(val->v, p, len); break; default: break; } switch (type) { case OAKLEY_ATTR_ENC_ALG: sa->enctype = (u_int16_t)ntohs(d->lorv); break; case OAKLEY_ATTR_HASH_ALG: sa->hashtype = (u_int16_t)ntohs(d->lorv); break; case OAKLEY_ATTR_AUTH_METHOD: sa->authmethod = ntohs(d->lorv); break; case OAKLEY_ATTR_GRP_DESC: sa->dh_group = (u_int16_t)ntohs(d->lorv); break; case OAKLEY_ATTR_GRP_TYPE: { int type = (int)ntohs(d->lorv); if (type == OAKLEY_ATTR_GRP_TYPE_MODP) sa->dhgrp->type = type; else return -1; break; } case OAKLEY_ATTR_GRP_PI: sa->dhgrp->prime = val; break; case OAKLEY_ATTR_GRP_GEN_ONE: vfree(val); if (!flag) sa->dhgrp->gen1 = ntohs(d->lorv); else { int len = ntohs(d->lorv); sa->dhgrp->gen1 = 0; if (len > 4) return -1; memcpy(&sa->dhgrp->gen1, d + 1, len); sa->dhgrp->gen1 = ntohl(sa->dhgrp->gen1); } break; case OAKLEY_ATTR_GRP_GEN_TWO: vfree(val); if (!flag) sa->dhgrp->gen2 = ntohs(d->lorv); else { int len = ntohs(d->lorv); sa->dhgrp->gen2 = 0; if (len > 4) return -1; memcpy(&sa->dhgrp->gen2, d + 1, len); sa->dhgrp->gen2 = ntohl(sa->dhgrp->gen2); } break; case OAKLEY_ATTR_GRP_CURVE_A: sa->dhgrp->curve_a = val; break; case OAKLEY_ATTR_GRP_CURVE_B: sa->dhgrp->curve_b = val; break; case OAKLEY_ATTR_SA_LD_TYPE: { int type = (int)ntohs(d->lorv); switch (type) { case OAKLEY_ATTR_SA_LD_TYPE_SEC: case OAKLEY_ATTR_SA_LD_TYPE_KB: life_t = type; break; default: life_t = OAKLEY_ATTR_SA_LD_TYPE_DEFAULT; break; } break; } case OAKLEY_ATTR_SA_LD: if (!prev || (ntohs(prev->type) & ~ISAKMP_GEN_MASK) != OAKLEY_ATTR_SA_LD_TYPE) { plog(LLV_ERROR, LOCATION, NULL, "life duration must follow ltype\n"); break; } switch (life_t) { case IPSECDOI_ATTR_SA_LD_TYPE_SEC: sa->lifetime = ipsecdoi_set_ld(val); vfree(val); if (sa->lifetime == 0) { plog(LLV_ERROR, LOCATION, NULL, "invalid life duration.\n"); goto err; } break; case IPSECDOI_ATTR_SA_LD_TYPE_KB: sa->lifebyte = ipsecdoi_set_ld(val); vfree(val); if (sa->lifetime == 0) { plog(LLV_ERROR, LOCATION, NULL, "invalid life duration.\n"); goto err; } break; default: vfree(val); plog(LLV_ERROR, LOCATION, NULL, "invalid life type: %d\n", life_t); goto err; } break; case OAKLEY_ATTR_KEY_LEN: { int len = ntohs(d->lorv); if (len % 8 != 0) { plog(LLV_ERROR, LOCATION, NULL, "keylen %d: not multiple of 8\n", len); goto err; } sa->encklen = (u_int16_t)len; keylen++; break; } case OAKLEY_ATTR_PRF: case OAKLEY_ATTR_FIELD_SIZE: /* unsupported */ break; case OAKLEY_ATTR_GRP_ORDER: sa->dhgrp->order = val; break; #ifdef HAVE_GSSAPI case OAKLEY_ATTR_GSS_ID: { int len = ntohs(d->lorv); sa->gssid = vmalloc(len); memcpy(sa->gssid->v, d + 1, len); plog(LLV_DEBUG, LOCATION, NULL, "received gss id '%s' (len %d)\n", sa->gssid->v, sa->gssid->l); break; } #endif default: break; } prev = d; if (flag) { tlen -= sizeof(*d); d = (struct isakmp_data *)((char *)d + sizeof(*d)); } else { tlen -= (sizeof(*d) + ntohs(d->lorv)); d = (struct isakmp_data *)((char *)d + sizeof(*d) + ntohs(d->lorv)); } } /* key length must not be specified on some algorithms */ if (keylen) { if (sa->enctype == OAKLEY_ATTR_ENC_ALG_DES #ifdef HAVE_OPENSSL_IDEA_H || sa->enctype == OAKLEY_ATTR_ENC_ALG_IDEA #endif || sa->enctype == OAKLEY_ATTR_ENC_ALG_3DES) { plog(LLV_ERROR, LOCATION, NULL, "keylen must not be specified " "for encryption algorithm %d\n", sa->enctype); return -1; } } return 0; err: return error; } /*%%%*/ /* * check phase 2 SA payload and select single proposal. * make new SA payload to be replyed not including general header. * This function is called by responder only. * OUT: * 0: succeed. * -1: error occured. */ int ipsecdoi_selectph2proposal(iph2) struct ph2handle *iph2; { struct prop_pair **pair; struct prop_pair *ret; /* get proposal pair */ pair = get_proppair(iph2->sa, IPSECDOI_TYPE_PH2); if (pair == NULL) return -1; /* check and select a proposal. */ ret = get_ph2approval(iph2, pair); free_proppair(pair); if (ret == NULL) return -1; /* make a SA to be replayed. */ /* SPI must be updated later. */ iph2->sa_ret = get_sabyproppair(ret, iph2->ph1); free_proppair0(ret); if (iph2->sa_ret == NULL) return -1; return 0; } /* * check phase 2 SA payload returned from responder. * This function is called by initiator only. * OUT: * 0: valid. * -1: invalid. */ int ipsecdoi_checkph2proposal(iph2) struct ph2handle *iph2; { struct prop_pair **rpair = NULL, **spair = NULL; struct prop_pair *p; int i, n, num; int error = -1; /* get proposal pair of SA sent. */ spair = get_proppair(iph2->sa, IPSECDOI_TYPE_PH2); if (spair == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get prop pair.\n"); goto end; } /* XXX should check the number of transform */ /* get proposal pair of SA replyed */ rpair = get_proppair(iph2->sa_ret, IPSECDOI_TYPE_PH2); if (rpair == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get prop pair.\n"); goto end; } /* check proposal is only one ? */ n = 0; num = 0; for (i = 0; i < MAXPROPPAIRLEN; i++) { if (rpair[i]) { n = i; num++; } } if (num == 0) { plog(LLV_ERROR, LOCATION, NULL, "no proposal received.\n"); goto end; } if (num != 1) { plog(LLV_ERROR, LOCATION, NULL, "some proposals received.\n"); goto end; } if (spair[n] == NULL) { plog(LLV_WARNING, LOCATION, NULL, "invalid proposal number:%d received.\n", i); } if (rpair[n]->tnext != NULL) { plog(LLV_ERROR, LOCATION, NULL, "multi transforms replyed.\n"); goto end; } if (cmp_aproppair_i(rpair[n], spair[n])) { plog(LLV_ERROR, LOCATION, NULL, "proposal mismathed.\n"); goto end; } /* * check and select a proposal. * ensure that there is no modification of the proposal by * cmp_aproppair_i() */ p = get_ph2approval(iph2, rpair); if (p == NULL) goto end; /* make a SA to be replayed. */ vfree(iph2->sa_ret); iph2->sa_ret = get_sabyproppair(p, iph2->ph1); free_proppair0(p); if (iph2->sa_ret == NULL) goto end; error = 0; end: if (rpair) free_proppair(rpair); if (spair) free_proppair(spair); return error; } /* * compare two prop_pair which is assumed to have same proposal number. * the case of bundle or single SA, NOT multi transforms. * a: a proposal that is multi protocols and single transform, usually replyed. * b: a proposal that is multi protocols and multi transform, usually sent. * NOTE: this function is for initiator. * OUT * 0: equal * 1: not equal * XXX cannot understand the comment! */ static int cmp_aproppair_i(a, b) struct prop_pair *a, *b; { struct prop_pair *p, *q, *r; int len; for (p = a, q = b; p && q; p = p->next, q = q->next) { for (r = q; r; r = r->tnext) { /* compare trns */ if (p->trns->t_no == r->trns->t_no) break; } if (!r) { /* no suitable transform found */ plog(LLV_ERROR, LOCATION, NULL, "no suitable transform found.\n"); return -1; } /* compare prop */ if (p->prop->p_no != r->prop->p_no) { plog(LLV_WARNING, LOCATION, NULL, "proposal #%d mismatched, " "expected #%d.\n", r->prop->p_no, p->prop->p_no); /*FALLTHROUGH*/ } if (p->prop->proto_id != r->prop->proto_id) { plog(LLV_ERROR, LOCATION, NULL, "proto_id mismathed: my:%d peer:%d\n", r->prop->proto_id, p->prop->proto_id); return -1; } if (p->prop->proto_id != r->prop->proto_id) { plog(LLV_ERROR, LOCATION, NULL, "invalid spi size: %d.\n", p->prop->proto_id); return -1; } /* check #of transforms */ if (p->prop->num_t != 1) { plog(LLV_WARNING, LOCATION, NULL, "#of transform is %d, " "but expected 1.\n", p->prop->num_t); /*FALLTHROUGH*/ } if (p->trns->t_id != r->trns->t_id) { plog(LLV_WARNING, LOCATION, NULL, "transform number has been modified.\n"); /*FALLTHROUGH*/ } if (p->trns->reserved != r->trns->reserved) { plog(LLV_WARNING, LOCATION, NULL, "reserved field should be zero.\n"); /*FALLTHROUGH*/ } /* compare attribute */ len = ntohs(r->trns->h.len) - sizeof(*p->trns); if (memcmp(p->trns + 1, r->trns + 1, len) != 0) { plog(LLV_WARNING, LOCATION, NULL, "attribute has been modified.\n"); /*FALLTHROUGH*/ } } if ((p && !q) || (!p && q)) { /* # of protocols mismatched */ plog(LLV_ERROR, LOCATION, NULL, "#of protocols mismatched.\n"); return -1; } return 0; } /* * acceptable check for policy configuration. * return a new SA payload to be reply to peer. */ static struct prop_pair * get_ph2approval(iph2, pair) struct ph2handle *iph2; struct prop_pair **pair; { struct prop_pair *ret; int i; iph2->approval = NULL; plog(LLV_DEBUG, LOCATION, NULL, "begin compare proposals.\n"); for (i = 0; i < MAXPROPPAIRLEN; i++) { if (pair[i] == NULL) continue; plog(LLV_DEBUG, LOCATION, NULL, "pair[%d]: %p\n", i, pair[i]); print_proppair(LLV_DEBUG, pair[i]);; /* compare proposal and select one */ ret = get_ph2approvalx(iph2, pair[i]); if (ret != NULL) { /* found */ return ret; } } plog(LLV_ERROR, LOCATION, NULL, "no suitable policy found.\n"); return NULL; } /* * compare my proposal and peers just one proposal. * set a approval. */ static struct prop_pair * get_ph2approvalx(iph2, pp) struct ph2handle *iph2; struct prop_pair *pp; { struct prop_pair *ret = NULL; struct saprop *pr0, *pr = NULL; struct saprop *q1, *q2; pr0 = aproppair2saprop(pp); if (pr0 == NULL) return NULL; for (q1 = pr0; q1; q1 = q1->next) { for (q2 = iph2->proposal; q2; q2 = q2->next) { plog(LLV_DEBUG, LOCATION, NULL, "peer's single bundle:\n"); printsaprop0(LLV_DEBUG, q1); plog(LLV_DEBUG, LOCATION, NULL, "my single bundle:\n"); printsaprop0(LLV_DEBUG, q2); pr = cmpsaprop_alloc(iph2->ph1, q1, q2, iph2->side); if (pr != NULL) goto found; plog(LLV_ERROR, LOCATION, NULL, "not matched\n"); } } /* no proposal matching */ err: flushsaprop(pr0); return NULL; found: flushsaprop(pr0); plog(LLV_DEBUG, LOCATION, NULL, "matched\n"); iph2->approval = pr; { struct saproto *sp; struct prop_pair *p, *n, *x; ret = NULL; for (p = pp; p; p = p->next) { /* * find a proposal with matching proto_id. * we have analyzed validity already, in cmpsaprop_alloc(). */ for (sp = pr->head; sp; sp = sp->next) { if (sp->proto_id == p->prop->proto_id) break; } if (!sp) goto err; if (sp->head->next) goto err; /* XXX */ for (x = p; x; x = x->tnext) if (sp->head->trns_no == x->trns->t_no) break; if (!x) goto err; /* XXX */ n = racoon_calloc(1, sizeof(struct prop_pair)); if (!n) { plog(LLV_ERROR, LOCATION, NULL, "failed to get buffer.\n"); goto err; } n->prop = x->prop; n->trns = x->trns; /* need to preserve the order */ for (x = ret; x && x->next; x = x->next) ; if (x && x->prop == n->prop) { for (/*nothing*/; x && x->tnext; x = x->tnext) ; x->tnext = n; } else { if (x) x->next = n; else { ret = n; } } /* #of transforms should be updated ? */ } } return ret; } void free_proppair(pair) struct prop_pair **pair; { int i; for (i = 0; i < MAXPROPPAIRLEN; i++) { free_proppair0(pair[i]); pair[i] = NULL; } racoon_free(pair); } static void free_proppair0(pair) struct prop_pair *pair; { struct prop_pair *p, *q, *r, *s; for (p = pair; p; p = q) { q = p->next; for (r = p; r; r = s) { s = r->tnext; racoon_free(r); } } } /* * get proposal pairs from SA payload. * tiny check for proposal payload. */ struct prop_pair ** get_proppair(sa, mode) vchar_t *sa; int mode; { struct prop_pair **pair; int num_p = 0; /* number of proposal for use */ int tlen; caddr_t bp; int i; struct ipsecdoi_sa_b *sab = (struct ipsecdoi_sa_b *)sa->v; plog(LLV_DEBUG, LOCATION, NULL, "total SA len=%d\n", sa->l); plogdump(LLV_DEBUG, sa->v, sa->l); /* check SA payload size */ if (sa->l < sizeof(*sab)) { plog(LLV_ERROR, LOCATION, NULL, "Invalid SA length = %d.\n", sa->l); return NULL; } /* check DOI */ if (check_doi(ntohl(sab->doi)) < 0) return NULL; /* check SITUATION */ if (check_situation(ntohl(sab->sit)) < 0) return NULL; pair = racoon_calloc(1, MAXPROPPAIRLEN * sizeof(*pair)); if (pair == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get buffer.\n"); return NULL; } memset(pair, 0, sizeof(pair)); bp = (caddr_t)(sab + 1); tlen = sa->l - sizeof(*sab); { struct isakmp_pl_p *prop; int proplen; vchar_t *pbuf = NULL; struct isakmp_parse_t *pa; pbuf = isakmp_parsewoh(ISAKMP_NPTYPE_P, (struct isakmp_gen *)bp, tlen); if (pbuf == NULL) return NULL; for (pa = (struct isakmp_parse_t *)pbuf->v; pa->type != ISAKMP_NPTYPE_NONE; pa++) { /* check the value of next payload */ if (pa->type != ISAKMP_NPTYPE_P) { plog(LLV_ERROR, LOCATION, NULL, "Invalid payload type=%u\n", pa->type); vfree(pbuf); return NULL; } if (mode == IPSECDOI_TYPE_PH1 && pa != (struct isakmp_parse_t *)pbuf->v) { plog(LLV_ERROR, LOCATION, NULL, "Only a single proposal payload is allowed " "during phase 1 processing.\n"); vfree(pbuf); return NULL; } prop = (struct isakmp_pl_p *)pa->ptr; proplen = pa->len; plog(LLV_DEBUG, LOCATION, NULL, "proposal #%u len=%d\n", prop->p_no, proplen); if (proplen == 0) { plog(LLV_ERROR, LOCATION, NULL, "invalid proposal with length %d\n", proplen); vfree(pbuf); return NULL; } /* check Protocol ID */ if (!check_protocol[mode]) { plog(LLV_ERROR, LOCATION, NULL, "unsupported mode %d\n", mode); continue; } if (check_protocol[mode](prop->proto_id) < 0) continue; /* check SPI length when IKE. */ if (check_spi_size(prop->proto_id, prop->spi_size) < 0) continue; /* get transform */ if (get_transform(prop, pair, &num_p, mode) < 0) { vfree(pbuf); return NULL; } } vfree(pbuf); pbuf = NULL; } { int notrans, nprop; struct prop_pair *p, *q; /* check for proposals with no transforms */ for (i = 0; i < MAXPROPPAIRLEN; i++) { if (!pair[i]) continue; plog(LLV_DEBUG, LOCATION, NULL, "pair %d:\n", i); print_proppair(LLV_DEBUG, pair[i]); notrans = nprop = 0; for (p = pair[i]; p; p = p->next) { if (p->trns == NULL) { notrans++; break; } for (q = p; q; q = q->tnext) nprop++; } #if 0 /* * XXX at this moment, we cannot accept proposal group * with multiple proposals. this should be fixed. */ if (pair[i]->next) { plog(LLV_WARNING, LOCATION, NULL, "proposal #%u ignored " "(multiple proposal not supported)\n", pair[i]->prop->p_no); notrans++; } #endif if (notrans) { for (p = pair[i]; p; p = q) { q = p->next; racoon_free(p); } pair[i] = NULL; num_p--; } else { plog(LLV_DEBUG, LOCATION, NULL, "proposal #%u: %d transform\n", pair[i]->prop->p_no, nprop); } } } /* bark if no proposal is found. */ if (num_p <= 0) { plog(LLV_ERROR, LOCATION, NULL, "no Proposal found.\n"); return NULL; } return pair; } /* * check transform payload. * OUT: * positive: return the pointer to the payload of valid transform. * 0 : No valid transform found. */ static int get_transform(prop, pair, num_p, mode) struct isakmp_pl_p *prop; struct prop_pair **pair; int *num_p; int mode; { int tlen; /* total length of all transform in a proposal */ caddr_t bp; struct isakmp_pl_t *trns; int trnslen; vchar_t *pbuf = NULL; struct isakmp_parse_t *pa; struct prop_pair *p = NULL, *q; int num_t; bp = (caddr_t)prop + sizeof(struct isakmp_pl_p) + prop->spi_size; tlen = ntohs(prop->h.len) - (sizeof(struct isakmp_pl_p) + prop->spi_size); pbuf = isakmp_parsewoh(ISAKMP_NPTYPE_T, (struct isakmp_gen *)bp, tlen); if (pbuf == NULL) return -1; /* check and get transform for use */ num_t = 0; for (pa = (struct isakmp_parse_t *)pbuf->v; pa->type != ISAKMP_NPTYPE_NONE; pa++) { num_t++; /* check the value of next payload */ if (pa->type != ISAKMP_NPTYPE_T) { plog(LLV_ERROR, LOCATION, NULL, "Invalid payload type=%u\n", pa->type); break; } if (mode == IPSECDOI_TYPE_PH1 && pa != (struct isakmp_parse_t *)pbuf->v) { plog(LLV_ERROR, LOCATION, NULL, "Only a single transform payload is allowed " "during phase 1 processing.\n"); break; } trns = (struct isakmp_pl_t *)pa->ptr; trnslen = pa->len; plog(LLV_DEBUG, LOCATION, NULL, "transform #%u len=%u\n", trns->t_no, trnslen); /* check transform ID */ if (prop->proto_id >= ARRAYLEN(check_transform)) { plog(LLV_WARNING, LOCATION, NULL, "unsupported proto_id %u\n", prop->proto_id); continue; } if (prop->proto_id >= ARRAYLEN(check_attributes)) { plog(LLV_WARNING, LOCATION, NULL, "unsupported proto_id %u\n", prop->proto_id); continue; } if (!check_transform[prop->proto_id] || !check_attributes[prop->proto_id]) { plog(LLV_WARNING, LOCATION, NULL, "unsupported proto_id %u\n", prop->proto_id); continue; } if (check_transform[prop->proto_id](trns->t_id) < 0) continue; /* check data attributes */ if (check_attributes[prop->proto_id](trns) != 0) continue; p = racoon_calloc(1, sizeof(*p)); if (p == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get buffer.\n"); vfree(pbuf); return -1; } p->prop = prop; p->trns = trns; /* need to preserve the order */ for (q = pair[prop->p_no]; q && q->next; q = q->next) ; if (q && q->prop == p->prop) { for (/*nothing*/; q && q->tnext; q = q->tnext) ; q->tnext = p; } else { if (q) q->next = p; else { pair[prop->p_no] = p; (*num_p)++; } } } vfree(pbuf); return 0; } /* * make a new SA payload from prop_pair. * NOTE: this function make spi value clear. */ vchar_t * get_sabyproppair(pair, iph1) struct prop_pair *pair; struct ph1handle *iph1; { vchar_t *newsa; int newtlen; u_int8_t *np_p = NULL; struct prop_pair *p; int prophlen, trnslen; caddr_t bp; newtlen = sizeof(struct ipsecdoi_sa_b); for (p = pair; p; p = p->next) { newtlen += (sizeof(struct isakmp_pl_p) + p->prop->spi_size + ntohs(p->trns->h.len)); } newsa = vmalloc(newtlen); if (newsa == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get newsa.\n"); return NULL; } bp = newsa->v; ((struct isakmp_gen *)bp)->len = htons(newtlen); /* update some of values in SA header */ ((struct ipsecdoi_sa_b *)bp)->doi = htonl(iph1->rmconf->doitype); ((struct ipsecdoi_sa_b *)bp)->sit = htonl(iph1->rmconf->sittype); bp += sizeof(struct ipsecdoi_sa_b); /* create proposal payloads */ for (p = pair; p; p = p->next) { prophlen = sizeof(struct isakmp_pl_p) + p->prop->spi_size; trnslen = ntohs(p->trns->h.len); if (np_p) *np_p = ISAKMP_NPTYPE_P; /* create proposal */ memcpy(bp, p->prop, prophlen); ((struct isakmp_pl_p *)bp)->h.np = ISAKMP_NPTYPE_NONE; ((struct isakmp_pl_p *)bp)->h.len = htons(prophlen + trnslen); ((struct isakmp_pl_p *)bp)->num_t = 1; np_p = &((struct isakmp_pl_p *)bp)->h.np; memset(bp + sizeof(struct isakmp_pl_p), 0, p->prop->spi_size); bp += prophlen; /* create transform */ memcpy(bp, p->trns, trnslen); ((struct isakmp_pl_t *)bp)->h.np = ISAKMP_NPTYPE_NONE; ((struct isakmp_pl_t *)bp)->h.len = htons(trnslen); bp += trnslen; } return newsa; } /* * update responder's spi */ int ipsecdoi_updatespi(iph2) struct ph2handle *iph2; { struct prop_pair **pair, *p; struct saprop *pp; struct saproto *pr; int i; int error = -1; u_int8_t *spi; pair = get_proppair(iph2->sa_ret, IPSECDOI_TYPE_PH2); if (pair == NULL) return -1; for (i = 0; i < MAXPROPPAIRLEN; i++) { if (pair[i]) break; } if (i == MAXPROPPAIRLEN || pair[i]->tnext) { /* multiple transform must be filtered by selectph2proposal.*/ goto end; } pp = iph2->approval; /* create proposal payloads */ for (p = pair[i]; p; p = p->next) { /* * find a proposal/transform with matching proto_id/t_id. * we have analyzed validity already, in cmpsaprop_alloc(). */ for (pr = pp->head; pr; pr = pr->next) { if (p->prop->proto_id == pr->proto_id && p->trns->t_id == pr->head->trns_id) { break; } } if (!pr) goto end; /* * XXX SPI bits are left-filled, for use with IPComp. * we should be switching to variable-length spi field... */ spi = (u_int8_t *)&pr->spi; spi += sizeof(pr->spi); spi -= pr->spisize; memcpy((caddr_t)p->prop + sizeof(*p->prop), spi, pr->spisize); } error = 0; end: free_proppair(pair); return error; } /* * make a new SA payload from prop_pair. */ vchar_t * get_sabysaprop(pp0, sa0) struct saprop *pp0; vchar_t *sa0; { struct prop_pair **pair; vchar_t *newsa; int newtlen; u_int8_t *np_p = NULL; struct prop_pair *p = NULL; struct saprop *pp; struct saproto *pr; struct satrns *tr; int prophlen, trnslen; caddr_t bp; /* get proposal pair */ pair = get_proppair(sa0, IPSECDOI_TYPE_PH2); if (pair == NULL) return NULL; newtlen = sizeof(struct ipsecdoi_sa_b); for (pp = pp0; pp; pp = pp->next) { if (pair[pp->prop_no] == NULL) return NULL; for (pr = pp->head; pr; pr = pr->next) { newtlen += (sizeof(struct isakmp_pl_p) + pr->spisize); for (tr = pr->head; tr; tr = tr->next) { for (p = pair[pp->prop_no]; p; p = p->tnext) { if (tr->trns_no == p->trns->t_no) break; } if (p == NULL) return NULL; newtlen += ntohs(p->trns->h.len); } } } newsa = vmalloc(newtlen); if (newsa == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get newsa.\n"); return NULL; } bp = newsa->v; /* some of values of SA must be updated in the out of this function */ ((struct isakmp_gen *)bp)->len = htons(newtlen); bp += sizeof(struct ipsecdoi_sa_b); /* create proposal payloads */ for (pp = pp0; pp; pp = pp->next) { for (pr = pp->head; pr; pr = pr->next) { prophlen = sizeof(struct isakmp_pl_p) + p->prop->spi_size; for (tr = pr->head; tr; tr = tr->next) { for (p = pair[pp->prop_no]; p; p = p->tnext) { if (tr->trns_no == p->trns->t_no) break; } if (p == NULL) return NULL; trnslen = ntohs(p->trns->h.len); if (np_p) *np_p = ISAKMP_NPTYPE_P; /* create proposal */ memcpy(bp, p->prop, prophlen); ((struct isakmp_pl_p *)bp)->h.np = ISAKMP_NPTYPE_NONE; ((struct isakmp_pl_p *)bp)->h.len = htons(prophlen + trnslen); ((struct isakmp_pl_p *)bp)->num_t = 1; np_p = &((struct isakmp_pl_p *)bp)->h.np; bp += prophlen; /* create transform */ memcpy(bp, p->trns, trnslen); ((struct isakmp_pl_t *)bp)->h.np = ISAKMP_NPTYPE_NONE; ((struct isakmp_pl_t *)bp)->h.len = htons(trnslen); bp += trnslen; } } } return newsa; } /* * If some error happens then return 0. Although 0 means that lifetime is zero, * such a value should not be accepted. * Also 0 of lifebyte should not be included in a packet although 0 means not * to care of it. */ static u_int32_t ipsecdoi_set_ld(buf) vchar_t *buf; { u_int32_t ld; if (buf == 0) return 0; switch (buf->l) { case 2: ld = ntohs(*(u_int16_t *)buf->v); break; case 4: ld = ntohl(*(u_int32_t *)buf->v); break; default: plog(LLV_ERROR, LOCATION, NULL, "length %d of life duration " "isn't supported.\n", buf->l); return 0; } return ld; } /*%%%*/ /* * check DOI */ static int check_doi(doi) u_int32_t doi; { switch (doi) { case IPSEC_DOI: return 0; default: plog(LLV_ERROR, LOCATION, NULL, "invalid value of DOI 0x%08x.\n", doi); return -1; } /* NOT REACHED */ } /* * check situation */ static int check_situation(sit) u_int32_t sit; { switch (sit) { case IPSECDOI_SIT_IDENTITY_ONLY: return 0; case IPSECDOI_SIT_SECRECY: case IPSECDOI_SIT_INTEGRITY: plog(LLV_ERROR, LOCATION, NULL, "situation 0x%08x unsupported yet.\n", sit); return -1; default: plog(LLV_ERROR, LOCATION, NULL, "invalid situation 0x%08x.\n", sit); return -1; } /* NOT REACHED */ } /* * check protocol id in main mode */ static int check_prot_main(proto_id) int proto_id; { switch (proto_id) { case IPSECDOI_PROTO_ISAKMP: return 0; default: plog(LLV_ERROR, LOCATION, NULL, "Illegal protocol id=%u.\n", proto_id); return -1; } /* NOT REACHED */ } /* * check protocol id in quick mode */ static int check_prot_quick(proto_id) int proto_id; { switch (proto_id) { case IPSECDOI_PROTO_IPSEC_AH: case IPSECDOI_PROTO_IPSEC_ESP: return 0; case IPSECDOI_PROTO_IPCOMP: return 0; default: plog(LLV_ERROR, LOCATION, NULL, "invalid protocol id %d.\n", proto_id); return -1; } /* NOT REACHED */ } static int check_spi_size(proto_id, size) int proto_id, size; { switch (proto_id) { case IPSECDOI_PROTO_ISAKMP: if (size != 0) { /* WARNING */ plog(LLV_WARNING, LOCATION, NULL, "SPI size isn't zero, but IKE proposal.\n"); } return 0; case IPSECDOI_PROTO_IPSEC_AH: case IPSECDOI_PROTO_IPSEC_ESP: if (size != 4) { plog(LLV_ERROR, LOCATION, NULL, "invalid SPI size=%d for IPSEC proposal.\n", size); return -1; } return 0; case IPSECDOI_PROTO_IPCOMP: if (size != 2 && size != 4) { plog(LLV_ERROR, LOCATION, NULL, "invalid SPI size=%d for IPCOMP proposal.\n", size); return -1; } return 0; default: /* ??? */ return -1; } /* NOT REACHED */ } /* * check transform ID in ISAKMP. */ static int check_trns_isakmp(t_id) int t_id; { switch (t_id) { case IPSECDOI_KEY_IKE: return 0; default: plog(LLV_ERROR, LOCATION, NULL, "invalid transform-id=%u in proto_id=%u.\n", t_id, IPSECDOI_KEY_IKE); return -1; } /* NOT REACHED */ } /* * check transform ID in AH. */ static int check_trns_ah(t_id) int t_id; { switch (t_id) { case IPSECDOI_AH_MD5: case IPSECDOI_AH_SHA: return 0; case IPSECDOI_AH_DES: plog(LLV_ERROR, LOCATION, NULL, "not support transform-id=%u in AH.\n", t_id); return -1; default: plog(LLV_ERROR, LOCATION, NULL, "invalid transform-id=%u in AH.\n", t_id); return -1; } /* NOT REACHED */ } /* * check transform ID in ESP. */ static int check_trns_esp(t_id) int t_id; { switch (t_id) { case IPSECDOI_ESP_DES: case IPSECDOI_ESP_3DES: case IPSECDOI_ESP_NULL: case IPSECDOI_ESP_RC5: case IPSECDOI_ESP_CAST: case IPSECDOI_ESP_BLOWFISH: case IPSECDOI_ESP_RIJNDAEL: case IPSECDOI_ESP_TWOFISH: return 0; case IPSECDOI_ESP_DES_IV32: case IPSECDOI_ESP_DES_IV64: case IPSECDOI_ESP_IDEA: case IPSECDOI_ESP_3IDEA: case IPSECDOI_ESP_RC4: plog(LLV_ERROR, LOCATION, NULL, "not support transform-id=%u in ESP.\n", t_id); return -1; default: plog(LLV_ERROR, LOCATION, NULL, "invalid transform-id=%u in ESP.\n", t_id); return -1; } /* NOT REACHED */ } /* * check transform ID in IPCOMP. */ static int check_trns_ipcomp(t_id) int t_id; { switch (t_id) { case IPSECDOI_IPCOMP_OUI: case IPSECDOI_IPCOMP_DEFLATE: case IPSECDOI_IPCOMP_LZS: return 0; default: plog(LLV_ERROR, LOCATION, NULL, "invalid transform-id=%u in IPCOMP.\n", t_id); return -1; } /* NOT REACHED */ } /* * check data attributes in IKE. */ static int check_attr_isakmp(trns) struct isakmp_pl_t *trns; { struct isakmp_data *d; int tlen; int flag, type; u_int16_t lorv; tlen = ntohs(trns->h.len) - sizeof(struct isakmp_pl_t); d = (struct isakmp_data *)((caddr_t)trns + sizeof(struct isakmp_pl_t)); while (tlen > 0) { type = ntohs(d->type) & ~ISAKMP_GEN_MASK; flag = ntohs(d->type) & ISAKMP_GEN_MASK; lorv = ntohs(d->lorv); plog(LLV_DEBUG, LOCATION, NULL, "type=%s, flag=0x%04x, lorv=%s\n", s_oakley_attr(type), flag, s_oakley_attr_v(type, lorv)); /* * some of the attributes must be encoded in TV. * see RFC2409 Appendix A "Attribute Classes". */ switch (type) { case OAKLEY_ATTR_ENC_ALG: case OAKLEY_ATTR_HASH_ALG: case OAKLEY_ATTR_AUTH_METHOD: case OAKLEY_ATTR_GRP_DESC: case OAKLEY_ATTR_GRP_TYPE: case OAKLEY_ATTR_SA_LD_TYPE: case OAKLEY_ATTR_PRF: case OAKLEY_ATTR_KEY_LEN: case OAKLEY_ATTR_FIELD_SIZE: if (!flag) { /* TLV*/ plog(LLV_ERROR, LOCATION, NULL, "oakley attribute %d must be TV.\n", type); return -1; } break; } /* sanity check for TLV. length must be specified. */ if (!flag && lorv == 0) { /*TLV*/ plog(LLV_ERROR, LOCATION, NULL, "invalid length %d for TLV attribute %d.\n", lorv, type); return -1; } switch (type) { case OAKLEY_ATTR_ENC_ALG: if (!alg_oakley_encdef_ok(lorv)) { plog(LLV_ERROR, LOCATION, NULL, "invalied encryption algorithm=%d.\n", lorv); return -1; } break; case OAKLEY_ATTR_HASH_ALG: if (!alg_oakley_hashdef_ok(lorv)) { plog(LLV_ERROR, LOCATION, NULL, "invalied hash algorithm=%d.\n", lorv); return -1; } break; case OAKLEY_ATTR_AUTH_METHOD: switch (lorv) { case OAKLEY_ATTR_AUTH_METHOD_PSKEY: case OAKLEY_ATTR_AUTH_METHOD_RSASIG: case OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB: break; case OAKLEY_ATTR_AUTH_METHOD_DSSSIG: case OAKLEY_ATTR_AUTH_METHOD_RSAENC: case OAKLEY_ATTR_AUTH_METHOD_RSAREV: plog(LLV_ERROR, LOCATION, NULL, "auth method %d isn't supported.\n", lorv); return -1; default: plog(LLV_ERROR, LOCATION, NULL, "invalid auth method %d.\n", lorv); return -1; } break; case OAKLEY_ATTR_GRP_DESC: if (!alg_oakley_dhdef_ok(lorv)) { plog(LLV_ERROR, LOCATION, NULL, "invalid DH group %d.\n", lorv); return -1; } break; case OAKLEY_ATTR_GRP_TYPE: switch (lorv) { case OAKLEY_ATTR_GRP_TYPE_MODP: break; default: plog(LLV_ERROR, LOCATION, NULL, "unsupported DH group type %d.\n", lorv); return -1; } break; case OAKLEY_ATTR_GRP_PI: case OAKLEY_ATTR_GRP_GEN_ONE: /* sanity checks? */ break; case OAKLEY_ATTR_GRP_GEN_TWO: case OAKLEY_ATTR_GRP_CURVE_A: case OAKLEY_ATTR_GRP_CURVE_B: plog(LLV_ERROR, LOCATION, NULL, "attr type=%u isn't supported.\n", type); return -1; case OAKLEY_ATTR_SA_LD_TYPE: switch (lorv) { case OAKLEY_ATTR_SA_LD_TYPE_SEC: case OAKLEY_ATTR_SA_LD_TYPE_KB: break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid life type %d.\n", lorv); return -1; } break; case OAKLEY_ATTR_SA_LD: /* should check the value */ break; case OAKLEY_ATTR_PRF: case OAKLEY_ATTR_KEY_LEN: break; case OAKLEY_ATTR_FIELD_SIZE: plog(LLV_ERROR, LOCATION, NULL, "attr type=%u isn't supported.\n", type); return -1; case OAKLEY_ATTR_GRP_ORDER: break; case OAKLEY_ATTR_GSS_ID: break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid attribute type %d.\n", type); return -1; } if (flag) { tlen -= sizeof(*d); d = (struct isakmp_data *)((char *)d + sizeof(*d)); } else { tlen -= (sizeof(*d) + lorv); d = (struct isakmp_data *)((char *)d + sizeof(*d) + lorv); } } return 0; } /* * check data attributes in IPSEC AH/ESP. */ static int check_attr_ah(trns) struct isakmp_pl_t *trns; { return check_attr_ipsec(IPSECDOI_PROTO_IPSEC_AH, trns); } static int check_attr_esp(trns) struct isakmp_pl_t *trns; { return check_attr_ipsec(IPSECDOI_PROTO_IPSEC_ESP, trns); } static int check_attr_ipsec(proto_id, trns) int proto_id; struct isakmp_pl_t *trns; { struct isakmp_data *d; int tlen; int flag, type = 0; u_int16_t lorv; int attrseen[16]; /* XXX magic number */ tlen = ntohs(trns->h.len) - sizeof(struct isakmp_pl_t); d = (struct isakmp_data *)((caddr_t)trns + sizeof(struct isakmp_pl_t)); memset(attrseen, 0, sizeof(attrseen)); while (tlen > 0) { type = ntohs(d->type) & ~ISAKMP_GEN_MASK; flag = ntohs(d->type) & ISAKMP_GEN_MASK; lorv = ntohs(d->lorv); plog(LLV_DEBUG, LOCATION, NULL, "type=%s, flag=0x%04x, lorv=%s\n", s_ipsecdoi_attr(type), flag, s_ipsecdoi_attr_v(type, lorv)); if (type < sizeof(attrseen)/sizeof(attrseen[0])) attrseen[type]++; switch (type) { case IPSECDOI_ATTR_ENC_MODE: if (! flag) { plog(LLV_ERROR, LOCATION, NULL, "must be TV when ENC_MODE.\n"); return -1; } switch (lorv) { case IPSECDOI_ATTR_ENC_MODE_TUNNEL: case IPSECDOI_ATTR_ENC_MODE_TRNS: break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid encryption mode=%u.\n", lorv); return -1; } break; case IPSECDOI_ATTR_AUTH: if (! flag) { plog(LLV_ERROR, LOCATION, NULL, "must be TV when AUTH.\n"); return -1; } switch (lorv) { case IPSECDOI_ATTR_AUTH_HMAC_MD5: if (proto_id == IPSECDOI_PROTO_IPSEC_AH && trns->t_id != IPSECDOI_AH_MD5) { ahmismatch: plog(LLV_ERROR, LOCATION, NULL, "auth algorithm %u conflicts " "with transform %u.\n", lorv, trns->t_id); return -1; } break; case IPSECDOI_ATTR_AUTH_HMAC_SHA1: if (proto_id == IPSECDOI_PROTO_IPSEC_AH) { if (trns->t_id != IPSECDOI_AH_SHA) goto ahmismatch; } break; case IPSECDOI_ATTR_AUTH_DES_MAC: case IPSECDOI_ATTR_AUTH_KPDK: plog(LLV_ERROR, LOCATION, NULL, "auth algorithm %u isn't supported.\n", lorv); return -1; default: plog(LLV_ERROR, LOCATION, NULL, "invalid auth algorithm=%u.\n", lorv); return -1; } break; case IPSECDOI_ATTR_SA_LD_TYPE: if (! flag) { plog(LLV_ERROR, LOCATION, NULL, "must be TV when LD_TYPE.\n"); return -1; } switch (lorv) { case IPSECDOI_ATTR_SA_LD_TYPE_SEC: case IPSECDOI_ATTR_SA_LD_TYPE_KB: break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid life type %d.\n", lorv); return -1; } break; case IPSECDOI_ATTR_SA_LD: if (flag) { /* i.e. ISAKMP_GEN_TV */ plog(LLV_DEBUG, LOCATION, NULL, "life duration was in TLV.\n"); } else { /* i.e. ISAKMP_GEN_TLV */ if (lorv == 0) { plog(LLV_ERROR, LOCATION, NULL, "invalid length of LD\n"); return -1; } } break; case IPSECDOI_ATTR_GRP_DESC: if (! flag) { plog(LLV_ERROR, LOCATION, NULL, "must be TV when GRP_DESC.\n"); return -1; } if (!alg_oakley_dhdef_ok(lorv)) { plog(LLV_ERROR, LOCATION, NULL, "invalid group description=%u.\n", lorv); return -1; } break; case IPSECDOI_ATTR_KEY_LENGTH: if (! flag) { plog(LLV_ERROR, LOCATION, NULL, "must be TV when KEY_LENGTH.\n"); return -1; } break; case IPSECDOI_ATTR_KEY_ROUNDS: case IPSECDOI_ATTR_COMP_DICT_SIZE: case IPSECDOI_ATTR_COMP_PRIVALG: plog(LLV_ERROR, LOCATION, NULL, "attr type=%u isn't supported.\n", type); return -1; default: plog(LLV_ERROR, LOCATION, NULL, "invalid attribute type %d.\n", type); return -1; } if (flag) { tlen -= sizeof(*d); d = (struct isakmp_data *)((char *)d + sizeof(*d)); } else { tlen -= (sizeof(*d) + lorv); d = (struct isakmp_data *)((caddr_t)d + sizeof(*d) + lorv); } } if (proto_id == IPSECDOI_PROTO_IPSEC_AH && !attrseen[IPSECDOI_ATTR_AUTH]) { plog(LLV_ERROR, LOCATION, NULL, "attr AUTH must be present for AH.\n", type); return -1; } if (proto_id == IPSECDOI_PROTO_IPSEC_ESP && trns->t_id == IPSECDOI_ESP_NULL && !attrseen[IPSECDOI_ATTR_AUTH]) { plog(LLV_ERROR, LOCATION, NULL, "attr AUTH must be present for ESP NULL encryption.\n"); return -1; } return 0; } static int check_attr_ipcomp(trns) struct isakmp_pl_t *trns; { struct isakmp_data *d; int tlen; int flag, type = 0; u_int16_t lorv; int attrseen[16]; /* XXX magic number */ tlen = ntohs(trns->h.len) - sizeof(struct isakmp_pl_t); d = (struct isakmp_data *)((caddr_t)trns + sizeof(struct isakmp_pl_t)); memset(attrseen, 0, sizeof(attrseen)); while (tlen > 0) { type = ntohs(d->type) & ~ISAKMP_GEN_MASK; flag = ntohs(d->type) & ISAKMP_GEN_MASK; lorv = ntohs(d->lorv); plog(LLV_DEBUG, LOCATION, NULL, "type=%d, flag=0x%04x, lorv=0x%04x\n", type, flag, lorv); if (type < sizeof(attrseen)/sizeof(attrseen[0])) attrseen[type]++; switch (type) { case IPSECDOI_ATTR_ENC_MODE: if (! flag) { plog(LLV_ERROR, LOCATION, NULL, "must be TV when ENC_MODE.\n"); return -1; } switch (lorv) { case IPSECDOI_ATTR_ENC_MODE_TUNNEL: case IPSECDOI_ATTR_ENC_MODE_TRNS: break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid encryption mode=%u.\n", lorv); return -1; } break; case IPSECDOI_ATTR_SA_LD_TYPE: if (! flag) { plog(LLV_ERROR, LOCATION, NULL, "must be TV when LD_TYPE.\n"); return -1; } switch (lorv) { case IPSECDOI_ATTR_SA_LD_TYPE_SEC: case IPSECDOI_ATTR_SA_LD_TYPE_KB: break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid life type %d.\n", lorv); return -1; } break; case IPSECDOI_ATTR_SA_LD: if (flag) { /* i.e. ISAKMP_GEN_TV */ plog(LLV_DEBUG, LOCATION, NULL, "life duration was in TLV.\n"); } else { /* i.e. ISAKMP_GEN_TLV */ if (lorv == 0) { plog(LLV_ERROR, LOCATION, NULL, "invalid length of LD\n"); return -1; } } break; case IPSECDOI_ATTR_GRP_DESC: if (! flag) { plog(LLV_ERROR, LOCATION, NULL, "must be TV when GRP_DESC.\n"); return -1; } if (!alg_oakley_dhdef_ok(lorv)) { plog(LLV_ERROR, LOCATION, NULL, "invalid group description=%u.\n", lorv); return -1; } break; case IPSECDOI_ATTR_AUTH: plog(LLV_ERROR, LOCATION, NULL, "invalid attr type=%u.\n", type); return -1; case IPSECDOI_ATTR_KEY_LENGTH: case IPSECDOI_ATTR_KEY_ROUNDS: case IPSECDOI_ATTR_COMP_DICT_SIZE: case IPSECDOI_ATTR_COMP_PRIVALG: plog(LLV_ERROR, LOCATION, NULL, "attr type=%u isn't supported.\n", type); return -1; default: plog(LLV_ERROR, LOCATION, NULL, "invalid attribute type %d.\n", type); return -1; } if (flag) { tlen -= sizeof(*d); d = (struct isakmp_data *)((char *)d + sizeof(*d)); } else { tlen -= (sizeof(*d) + lorv); d = (struct isakmp_data *)((caddr_t)d + sizeof(*d) + lorv); } } #if 0 if (proto_id == IPSECDOI_PROTO_IPCOMP && !attrseen[IPSECDOI_ATTR_AUTH]) { plog(LLV_ERROR, LOCATION, NULL, "attr AUTH must be present for AH.\n", type); return -1; } #endif return 0; } /* %%% */ /* * create phase1 proposal from remote configuration. * NOT INCLUDING isakmp general header of SA payload */ vchar_t * ipsecdoi_setph1proposal(props) struct isakmpsa *props; { vchar_t *mysa; int sablen; /* count total size of SA minus isakmp general header */ /* not including isakmp general header of SA payload */ sablen = sizeof(struct ipsecdoi_sa_b); sablen += setph1prop(props, NULL); mysa = vmalloc(sablen); if (mysa == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to allocate my sa buffer\n"); return NULL; } /* create SA payload */ /* not including isakmp general header */ ((struct ipsecdoi_sa_b *)mysa->v)->doi = htonl(props->rmconf->doitype); ((struct ipsecdoi_sa_b *)mysa->v)->sit = htonl(props->rmconf->sittype); (void)setph1prop(props, mysa->v + sizeof(struct ipsecdoi_sa_b)); return mysa; } static int setph1prop(props, buf) struct isakmpsa *props; caddr_t buf; { struct isakmp_pl_p *prop = NULL; struct isakmpsa *s = NULL; int proplen, trnslen; u_int8_t *np_t; /* pointer next trns type in previous header */ int trns_num; caddr_t p = buf; proplen = sizeof(*prop); if (buf) { /* create proposal */ prop = (struct isakmp_pl_p *)p; prop->h.np = ISAKMP_NPTYPE_NONE; prop->p_no = props->prop_no; prop->proto_id = IPSECDOI_PROTO_ISAKMP; prop->spi_size = 0; p += sizeof(*prop); } np_t = NULL; trns_num = 0; for (s = props; s != NULL; s = s->next) { if (np_t) *np_t = ISAKMP_NPTYPE_T; trnslen = setph1trns(s, p); proplen += trnslen; if (buf) { /* save buffer to pre-next payload */ np_t = &((struct isakmp_pl_t *)p)->h.np; p += trnslen; /* count up transform length */ trns_num++; } } /* update proposal length */ if (buf) { prop->h.len = htons(proplen); prop->num_t = trns_num; } return proplen; } static int setph1trns(sa, buf) struct isakmpsa *sa; caddr_t buf; { struct isakmp_pl_t *trns = NULL; int trnslen, attrlen; caddr_t p = buf; trnslen = sizeof(*trns); if (buf) { /* create transform */ trns = (struct isakmp_pl_t *)p; trns->h.np = ISAKMP_NPTYPE_NONE; trns->t_no = sa->trns_no; trns->t_id = IPSECDOI_KEY_IKE; p += sizeof(*trns); } attrlen = setph1attr(sa, p); trnslen += attrlen; if (buf) p += attrlen; if (buf) trns->h.len = htons(trnslen); return trnslen; } static int setph1attr(sa, buf) struct isakmpsa *sa; caddr_t buf; { caddr_t p = buf; int attrlen = 0; if (sa->lifetime) { attrlen += sizeof(struct isakmp_data) + sizeof(struct isakmp_data); if (sa->lifetime > 0xffff) attrlen += sizeof(sa->lifetime); if (buf) { p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD_TYPE, OAKLEY_ATTR_SA_LD_TYPE_SEC); if (sa->lifetime > 0xffff) { u_int32_t v = htonl((u_int32_t)sa->lifetime); p = isakmp_set_attr_v(p, OAKLEY_ATTR_SA_LD, (caddr_t)&v, sizeof(v)); } else { p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD, sa->lifetime); } } } if (sa->lifebyte) { attrlen += sizeof(struct isakmp_data) + sizeof(struct isakmp_data); if (sa->lifebyte > 0xffff) attrlen += sizeof(sa->lifebyte); if (buf) { p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD_TYPE, OAKLEY_ATTR_SA_LD_TYPE_KB); if (sa->lifebyte > 0xffff) { u_int32_t v = htonl((u_int32_t)sa->lifebyte); p = isakmp_set_attr_v(p, OAKLEY_ATTR_SA_LD, (caddr_t)&v, sizeof(v)); } else { p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD, sa->lifebyte); } } } if (sa->enctype) { attrlen += sizeof(struct isakmp_data); if (buf) p = isakmp_set_attr_l(p, OAKLEY_ATTR_ENC_ALG, sa->enctype); } if (sa->encklen) { attrlen += sizeof(struct isakmp_data); if (buf) p = isakmp_set_attr_l(p, OAKLEY_ATTR_KEY_LEN, sa->encklen); } if (sa->authmethod) { attrlen += sizeof(struct isakmp_data); if (buf) p = isakmp_set_attr_l(p, OAKLEY_ATTR_AUTH_METHOD, sa->authmethod); } if (sa->hashtype) { attrlen += sizeof(struct isakmp_data); if (buf) p = isakmp_set_attr_l(p, OAKLEY_ATTR_HASH_ALG, sa->hashtype); } switch (sa->dh_group) { case OAKLEY_ATTR_GRP_DESC_MODP768: case OAKLEY_ATTR_GRP_DESC_MODP1024: case OAKLEY_ATTR_GRP_DESC_MODP1536: case OAKLEY_ATTR_GRP_DESC_MODP2048: case OAKLEY_ATTR_GRP_DESC_MODP3072: case OAKLEY_ATTR_GRP_DESC_MODP4096: case OAKLEY_ATTR_GRP_DESC_MODP6144: case OAKLEY_ATTR_GRP_DESC_MODP8192: /* don't attach group type for known groups */ attrlen += sizeof(struct isakmp_data); if (buf) { p = isakmp_set_attr_l(p, OAKLEY_ATTR_GRP_DESC, sa->dh_group); } break; case OAKLEY_ATTR_GRP_DESC_EC2N155: case OAKLEY_ATTR_GRP_DESC_EC2N185: /* don't attach group type for known groups */ attrlen += sizeof(struct isakmp_data); if (buf) { p = isakmp_set_attr_l(p, OAKLEY_ATTR_GRP_TYPE, OAKLEY_ATTR_GRP_TYPE_EC2N); } break; case 0: default: break; } #ifdef HAVE_GSSAPI if (sa->authmethod == OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB && sa->gssid != NULL) { attrlen += sizeof(struct isakmp_data); attrlen += sa->gssid->l; if (buf) { plog(LLV_DEBUG, LOCATION, NULL, "gss id attr: len %d, " "val '%s'\n", sa->gssid->l, sa->gssid->v); p = isakmp_set_attr_v(p, OAKLEY_ATTR_GSS_ID, (caddr_t)sa->gssid->v, sa->gssid->l); } } #endif return attrlen; } static vchar_t * setph2proposal0(iph2, pp, pr) const struct ph2handle *iph2; const struct saprop *pp; const struct saproto *pr; { vchar_t *p; struct isakmp_pl_p *prop; struct isakmp_pl_t *trns; struct satrns *tr; int attrlen; size_t trnsoff; caddr_t x0, x; u_int8_t *np_t; /* pointer next trns type in previous header */ const u_int8_t *spi; p = vmalloc(sizeof(*prop) + sizeof(pr->spi)); if (p == NULL) return NULL; /* create proposal */ prop = (struct isakmp_pl_p *)p->v; prop->h.np = ISAKMP_NPTYPE_NONE; prop->p_no = pp->prop_no; prop->proto_id = pr->proto_id; prop->num_t = 1; spi = (const u_int8_t *)&pr->spi; switch (pr->proto_id) { case IPSECDOI_PROTO_IPCOMP: /* * draft-shacham-ippcp-rfc2393bis-05.txt: * construct 16bit SPI (CPI). * XXX we may need to provide a configuration option to * generate 32bit SPI. otherwise we cannot interoeprate * with nodes that uses 32bit SPI, in case we are initiator. */ prop->spi_size = sizeof(u_int16_t); spi += sizeof(pr->spi) - sizeof(u_int16_t); p->l -= sizeof(pr->spi); p->l += sizeof(u_int16_t); break; default: prop->spi_size = sizeof(pr->spi); break; } memcpy(prop + 1, spi, prop->spi_size); /* create transform */ trnsoff = sizeof(*prop) + prop->spi_size; np_t = NULL; for (tr = pr->head; tr; tr = tr->next) { switch (pr->proto_id) { case IPSECDOI_PROTO_IPSEC_ESP: /* * don't build a null encryption * with no authentication transform. */ if (tr->trns_id == IPSECDOI_ESP_NULL && tr->authtype == IPSECDOI_ATTR_AUTH_NONE) continue; break; } if (np_t) { *np_t = ISAKMP_NPTYPE_T; prop->num_t++; } /* get attribute length */ attrlen = 0; if (pp->lifetime) { attrlen += sizeof(struct isakmp_data) + sizeof(struct isakmp_data); if (pp->lifetime > 0xffff) attrlen += sizeof(u_int32_t); } if (pp->lifebyte && pp->lifebyte != IPSECDOI_ATTR_SA_LD_KB_MAX) { attrlen += sizeof(struct isakmp_data) + sizeof(struct isakmp_data); if (pp->lifebyte > 0xffff) attrlen += sizeof(u_int32_t); } attrlen += sizeof(struct isakmp_data); /* enc mode */ if (tr->encklen) attrlen += sizeof(struct isakmp_data); switch (pr->proto_id) { case IPSECDOI_PROTO_IPSEC_ESP: /* non authentication mode ? */ if (tr->authtype != IPSECDOI_ATTR_AUTH_NONE) attrlen += sizeof(struct isakmp_data); break; case IPSECDOI_PROTO_IPSEC_AH: if (tr->authtype == IPSECDOI_ATTR_AUTH_NONE) { plog(LLV_ERROR, LOCATION, NULL, "no authentication algorithm found " "but protocol is AH.\n"); vfree(p); return NULL; } attrlen += sizeof(struct isakmp_data); break; case IPSECDOI_PROTO_IPCOMP: break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid protocol: %d\n", pr->proto_id); vfree(p); return NULL; } if (alg_oakley_dhdef_ok(iph2->sainfo->pfs_group)) attrlen += sizeof(struct isakmp_data); p = vrealloc(p, p->l + sizeof(*trns) + attrlen); if (p == NULL) return NULL; prop = (struct isakmp_pl_p *)p->v; /* set transform's values */ trns = (struct isakmp_pl_t *)(p->v + trnsoff); trns->h.np = ISAKMP_NPTYPE_NONE; trns->t_no = tr->trns_no; trns->t_id = tr->trns_id; /* set attributes */ x = x0 = p->v + trnsoff + sizeof(*trns); if (pp->lifetime) { x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD_TYPE, IPSECDOI_ATTR_SA_LD_TYPE_SEC); if (pp->lifetime > 0xffff) { u_int32_t v = htonl((u_int32_t)pp->lifetime); x = isakmp_set_attr_v(x, IPSECDOI_ATTR_SA_LD, (caddr_t)&v, sizeof(v)); } else { x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD, pp->lifetime); } } if (pp->lifebyte && pp->lifebyte != IPSECDOI_ATTR_SA_LD_KB_MAX) { x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD_TYPE, IPSECDOI_ATTR_SA_LD_TYPE_KB); if (pp->lifebyte > 0xffff) { u_int32_t v = htonl((u_int32_t)pp->lifebyte); x = isakmp_set_attr_v(x, IPSECDOI_ATTR_SA_LD, (caddr_t)&v, sizeof(v)); } else { x = isakmp_set_attr_l(x, IPSECDOI_ATTR_SA_LD, pp->lifebyte); } } x = isakmp_set_attr_l(x, IPSECDOI_ATTR_ENC_MODE, pr->encmode); if (tr->encklen) x = isakmp_set_attr_l(x, IPSECDOI_ATTR_KEY_LENGTH, tr->encklen); /* mandatory check has done above. */ if ((pr->proto_id == IPSECDOI_PROTO_IPSEC_ESP && tr->authtype != IPSECDOI_ATTR_AUTH_NONE) || pr->proto_id == IPSECDOI_PROTO_IPSEC_AH) x = isakmp_set_attr_l(x, IPSECDOI_ATTR_AUTH, tr->authtype); if (alg_oakley_dhdef_ok(iph2->sainfo->pfs_group)) x = isakmp_set_attr_l(x, IPSECDOI_ATTR_GRP_DESC, iph2->sainfo->pfs_group); /* update length of this transform. */ trns = (struct isakmp_pl_t *)(p->v + trnsoff); trns->h.len = htons(sizeof(*trns) + attrlen); /* save buffer to pre-next payload */ np_t = &trns->h.np; trnsoff += (sizeof(*trns) + attrlen); } /* update length of this protocol. */ prop->h.len = htons(p->l); return p; } /* * create phase2 proposal from policy configuration. * NOT INCLUDING isakmp general header of SA payload. * This function is called by initiator only. */ int ipsecdoi_setph2proposal(iph2) struct ph2handle *iph2; { struct saprop *proposal, *a; struct saproto *b = NULL; vchar_t *q; struct ipsecdoi_sa_b *sab; struct isakmp_pl_p *prop; size_t propoff; /* for previous field of type of next payload. */ proposal = iph2->proposal; iph2->sa = vmalloc(sizeof(*sab)); if (iph2->sa == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to allocate my sa buffer\n"); return -1; } /* create SA payload */ sab = (struct ipsecdoi_sa_b *)iph2->sa->v; sab->doi = htonl(IPSEC_DOI); sab->sit = htonl(IPSECDOI_SIT_IDENTITY_ONLY); /* XXX configurable ? */ prop = NULL; propoff = 0; for (a = proposal; a; a = a->next) { for (b = a->head; b; b = b->next) { q = setph2proposal0(iph2, a, b); if (q == NULL) { vfree(iph2->sa); return -1; } iph2->sa = vrealloc(iph2->sa, iph2->sa->l + q->l); if (iph2->sa == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to allocate my sa buffer\n"); if (q) vfree(q); return -1; } memcpy(iph2->sa->v + iph2->sa->l - q->l, q->v, q->l); if (propoff != 0) { prop = (struct isakmp_pl_p *)(iph2->sa->v + propoff); prop->h.np = ISAKMP_NPTYPE_P; } propoff = iph2->sa->l - q->l; vfree(q); } } return 0; } /* * return 1 if all of the given protocols are transport mode. */ int ipsecdoi_transportmode(pp) struct saprop *pp; { struct saproto *pr = NULL; for (; pp; pp = pp->next) { for (pr = pp->head; pr; pr = pr->next) { if (pr->encmode != IPSECDOI_ATTR_ENC_MODE_TRNS) return 0; } } return 1; } int ipsecdoi_get_defaultlifetime() { return IPSECDOI_ATTR_SA_LD_SEC_DEFAULT; } int ipsecdoi_checkalgtypes(proto_id, enc, auth, comp) int proto_id, enc, auth, comp; { #define TMPALGTYPE2STR(n) s_algtype(algclass_ipsec_##n, n) switch (proto_id) { case IPSECDOI_PROTO_IPSEC_ESP: if (enc == 0 || comp != 0) { plog(LLV_ERROR, LOCATION, NULL, "illegal algorithm defined " "ESP enc=%s auth=%s comp=%s.\n", TMPALGTYPE2STR(enc), TMPALGTYPE2STR(auth), TMPALGTYPE2STR(comp)); return -1; } break; case IPSECDOI_PROTO_IPSEC_AH: if (enc != 0 || auth == 0 || comp != 0) { plog(LLV_ERROR, LOCATION, NULL, "illegal algorithm defined " "AH enc=%s auth=%s comp=%s.\n", TMPALGTYPE2STR(enc), TMPALGTYPE2STR(auth), TMPALGTYPE2STR(comp)); return -1; } break; case IPSECDOI_PROTO_IPCOMP: if (enc != 0 || auth != 0 || comp == 0) { plog(LLV_ERROR, LOCATION, NULL, "illegal algorithm defined " "IPcomp enc=%s auth=%s comp=%s.\n", TMPALGTYPE2STR(enc), TMPALGTYPE2STR(auth), TMPALGTYPE2STR(comp)); return -1; } break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid ipsec protocol %d\n", proto_id); return -1; } #undef TMPALGTYPE2STR return 0; } int ipproto2doi(proto) int proto; { switch (proto) { case IPPROTO_AH: return IPSECDOI_PROTO_IPSEC_AH; case IPPROTO_ESP: return IPSECDOI_PROTO_IPSEC_ESP; case IPPROTO_IPCOMP: return IPSECDOI_PROTO_IPCOMP; } return -1; /* XXX */ } int doi2ipproto(proto) int proto; { switch (proto) { case IPSECDOI_PROTO_IPSEC_AH: return IPPROTO_AH; case IPSECDOI_PROTO_IPSEC_ESP: return IPPROTO_ESP; case IPSECDOI_PROTO_IPCOMP: return IPPROTO_IPCOMP; } return -1; /* XXX */ } /* * check the following: * - In main mode with pre-shared key, only address type can be used. * - if proper type for phase 1 ? * - if phase 1 ID payload conformed RFC2407 4.6.2. * (proto, port) must be (0, 0), (udp, 500) or (udp, [specified]). * - if ID payload sent from peer is equal to the ID expected by me. * * both of "id" and "id_p" should be ID payload without general header, */ int ipsecdoi_checkid1(iph1) struct ph1handle *iph1; { struct ipsecdoi_id_b *id_b; if (iph1->id_p == NULL) { plog(LLV_ERROR, LOCATION, NULL, "invalid iph1 passed id_p == NULL\n"); return ISAKMP_INTERNAL_ERROR; } if (iph1->id_p->l < sizeof(*id_b)) { plog(LLV_ERROR, LOCATION, NULL, "invalid value passed as \"ident\" (len=%lu)\n", (u_long)iph1->id_p->l); return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } id_b = (struct ipsecdoi_id_b *)iph1->id_p->v; /* In main mode with pre-shared key, only address type can be used. */ if (iph1->etype == ISAKMP_ETYPE_IDENT && iph1->approval->authmethod == OAKLEY_ATTR_AUTH_METHOD_PSKEY) { if (id_b->type != IPSECDOI_ID_IPV4_ADDR && id_b->type != IPSECDOI_ID_IPV6_ADDR) { plog(LLV_ERROR, LOCATION, NULL, "Expecting IP address type in main mode, " "but %s.\n", s_ipsecdoi_ident(id_b->type)); return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } } /* if proper type for phase 1 ? */ switch (id_b->type) { case IPSECDOI_ID_IPV4_ADDR_SUBNET: case IPSECDOI_ID_IPV6_ADDR_SUBNET: case IPSECDOI_ID_IPV4_ADDR_RANGE: case IPSECDOI_ID_IPV6_ADDR_RANGE: plog(LLV_WARNING, LOCATION, NULL, "such ID type %s is not proper.\n", s_ipsecdoi_ident(id_b->type)); /*FALLTHROUGH*/ } /* if phase 1 ID payload conformed RFC2407 4.6.2. */ if (id_b->type == IPSECDOI_ID_IPV4_ADDR && id_b->type == IPSECDOI_ID_IPV6_ADDR) { if (id_b->proto_id == 0 && ntohs(id_b->port) != 0) { plog(LLV_WARNING, LOCATION, NULL, "protocol ID and Port mismatched. " "proto_id:%d port:%d\n", id_b->proto_id, ntohs(id_b->port)); /*FALLTHROUGH*/ } else if (id_b->proto_id == IPPROTO_UDP) { /* * copmaring with expecting port. * always permit if port is equal to PORT_ISAKMP */ if (ntohs(id_b->port) != PORT_ISAKMP) { u_int16_t port; switch (iph1->remote->sa_family) { case AF_INET: port = ((struct sockaddr_in *)iph1->remote)->sin_port; break; #ifdef INET6 case AF_INET6: port = ((struct sockaddr_in6 *)iph1->remote)->sin6_port; break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "invalid family: %d\n", iph1->remote->sa_family); return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } if (ntohs(id_b->port) != port) { plog(LLV_WARNING, LOCATION, NULL, "port %d expected, but %d\n", port, ntohs(id_b->port)); /*FALLTHROUGH*/ } } } } /* compare with the ID if specified. */ if (iph1->rmconf->idv_p) { vchar_t *ident0 = NULL; vchar_t ident; /* check the type of both IDs */ if (iph1->rmconf->idvtype_p != doi2idtype(id_b->type)) { plog(LLV_WARNING, LOCATION, NULL, "ID type mismatched.\n"); if (iph1->rmconf->verify_identifier) return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } /* compare defined ID with the ID sent by peer. */ ident0 = getidval(iph1->rmconf->idvtype_p, iph1->rmconf->idv_p); switch (iph1->rmconf->idvtype_p) { case IDTYPE_ASN1DN: ident.v = (caddr_t)(id_b + 1); ident.l = ident0->l; if (eay_cmp_asn1dn(ident0, &ident)) { plog(LLV_WARNING, LOCATION, NULL, "ID value mismatched.\n"); if (iph1->rmconf->verify_identifier) return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } break; default: if (memcmp(ident0->v, id_b + 1, ident0->l)) { plog(LLV_WARNING, LOCATION, NULL, "ID value mismatched.\n"); if (iph1->rmconf->verify_identifier) return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } break; } vfree(ident0); } return 0; } /* * create ID payload for phase 1 and set into iph1->id. * NOT INCLUDING isakmp general header. * see, RFC2407 4.6.2.1 */ int ipsecdoi_setid1(iph1) struct ph1handle *iph1; { vchar_t *ret = NULL; struct ipsecdoi_id_b id_b; vchar_t *ident = NULL; struct sockaddr *ipid = NULL; /* init */ id_b.proto_id = 0; id_b.port = 0; ident = NULL; switch (iph1->rmconf->idvtype) { case IDTYPE_FQDN: id_b.type = IPSECDOI_ID_FQDN; ident = getidval(iph1->rmconf->idvtype, iph1->rmconf->idv); break; case IDTYPE_USERFQDN: id_b.type = IPSECDOI_ID_USER_FQDN; ident = getidval(iph1->rmconf->idvtype, iph1->rmconf->idv); break; case IDTYPE_KEYID: id_b.type = IPSECDOI_ID_KEY_ID; ident = getidval(iph1->rmconf->idvtype, iph1->rmconf->idv); break; #ifdef HAVE_SIGNING_C case IDTYPE_ASN1DN: id_b.type = IPSECDOI_ID_DER_ASN1_DN; if (iph1->rmconf->idv) { /* XXX it must be encoded to asn1dn. */ ident = vdup(iph1->rmconf->idv); } else { if (oakley_getmycert(iph1) < 0) { plog(LLV_ERROR, LOCATION, NULL, "failed to get own CERT.\n"); goto err; } ident = eay_get_x509asn1subjectname(&iph1->cert->cert); } break; #endif case IDTYPE_ADDRESS: /* * if the value of the id type was set by the configuration * file, then use it. otherwise the value is get from local * ip address by using ike negotiation. */ if (iph1->rmconf->idv) ipid = (struct sockaddr *)iph1->rmconf->idv->v; /*FALLTHROUGH*/ default: { int l; caddr_t p; if (ipid == NULL) ipid = iph1->local; /* use IP address */ switch (ipid->sa_family) { case AF_INET: id_b.type = IPSECDOI_ID_IPV4_ADDR; l = sizeof(struct in_addr); p = (caddr_t)&((struct sockaddr_in *)ipid)->sin_addr; break; #ifdef INET6 case AF_INET6: id_b.type = IPSECDOI_ID_IPV6_ADDR; l = sizeof(struct in6_addr); p = (caddr_t)&((struct sockaddr_in6 *)ipid)->sin6_addr; break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "invalid address family.\n"); goto err; } id_b.proto_id = IPPROTO_UDP; id_b.port = htons(PORT_ISAKMP); ident = vmalloc(l); if (!ident) { plog(LLV_ERROR, LOCATION, NULL, "failed to get ID buffer.\n"); return 0; } memcpy(ident->v, p, ident->l); } } if (!ident) { plog(LLV_ERROR, LOCATION, NULL, "failed to get ID buffer.\n"); return 0; } ret = vmalloc(sizeof(id_b) + ident->l); if (ret == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get ID buffer.\n"); goto err; } memcpy(ret->v, &id_b, sizeof(id_b)); memcpy(ret->v + sizeof(id_b), ident->v, ident->l); iph1->id = ret; plog(LLV_DEBUG, LOCATION, NULL, "use ID type of %s\n", s_ipsecdoi_ident(id_b.type)); if (ident) vfree(ident); return 0; err: if (ident) vfree(ident); plog(LLV_ERROR, LOCATION, NULL, "failed get my ID\n"); return -1; } static vchar_t * getidval(type, val) int type; vchar_t *val; { vchar_t *new = NULL; if (val) new = vdup(val); else if (lcconf->ident[type]) new = vdup(lcconf->ident[type]); return new; } /* it's only called by cfparse.y. */ int set_identifier(vpp, type, value) vchar_t **vpp, *value; int type; { vchar_t *new = NULL; /* simply return if value is null. */ if (!value) return 0; switch (type) { case IDTYPE_FQDN: case IDTYPE_USERFQDN: /* length is adjusted since QUOTEDSTRING teminates NULL. */ new = vmalloc(value->l - 1); if (new == NULL) return -1; memcpy(new->v, value->v, new->l); break; case IDTYPE_KEYID: { FILE *fp; char b[512]; int tlen, len; fp = fopen(value->v, "r"); if (fp == NULL) { plog(LLV_ERROR, LOCATION, NULL, "can not open %s\n", value->v); return -1; } tlen = 0; while ((len = fread(b, 1, sizeof(b), fp)) != 0) { new = vrealloc(new, tlen + len); if (!new) { fclose(fp); return -1; } memcpy(new->v + tlen, b, len); tlen += len; } break; } case IDTYPE_ADDRESS: { struct sockaddr *sa; /* length is adjusted since QUOTEDSTRING teminates NULL. */ if (value->l == 0) break; sa = str2saddr(value->v, NULL); if (sa == NULL) { plog(LLV_ERROR, LOCATION, NULL, "invalid ip address %s\n", value->v); return -1; } new = vmalloc(sa->sa_len); if (new == NULL) return -1; memcpy(new->v, sa, new->l); break; } case IDTYPE_ASN1DN: new = eay_str2asn1dn(value->v, value->l - 1); if (new == NULL) return -1; break; } *vpp = new; return 0; } /* * create ID payload for phase 2, and set into iph2->id and id_p. There are * NOT INCLUDING isakmp general header. * this function is for initiator. responder will get to copy from payload. * responder ID type is always address type. * see, RFC2407 4.6.2.1 */ int ipsecdoi_setid2(iph2) struct ph2handle *iph2; { struct secpolicy *sp; /* check there is phase 2 handler ? */ sp = getspbyspid(iph2->spid); if (sp == NULL) { plog(LLV_ERROR, LOCATION, NULL, "no policy found for spid:%lu.\n", iph2->spid); return -1; } iph2->id = ipsecdoi_sockaddr2id((struct sockaddr *)&sp->spidx.src, sp->spidx.prefs, sp->spidx.ul_proto); if (iph2->id == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get ID for %s\n", spidx2str(&sp->spidx)); return -1; } plog(LLV_DEBUG, LOCATION, NULL, "use local ID type %s\n", s_ipsecdoi_ident(((struct ipsecdoi_id_b *)iph2->id->v)->type)); /* remote side */ iph2->id_p = ipsecdoi_sockaddr2id((struct sockaddr *)&sp->spidx.dst, sp->spidx.prefd, sp->spidx.ul_proto); if (iph2->id_p == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get ID for %s\n", spidx2str(&sp->spidx)); vfree(iph2->id); iph2->id = NULL; return -1; } plog(LLV_DEBUG, LOCATION, NULL, "use remote ID type %s\n", s_ipsecdoi_ident(((struct ipsecdoi_id_b *)iph2->id_p->v)->type)); return 0; } /* * set address type of ID. * NOT INCLUDING general header. */ vchar_t * ipsecdoi_sockaddr2id(saddr, prefixlen, ul_proto) struct sockaddr *saddr; u_int prefixlen; u_int ul_proto; { vchar_t *new; int type, len1, len2; caddr_t sa; u_short port; /* * Q. When type is SUBNET, is it allowed to be ::1/128. * A. Yes. (consensus at bake-off) */ switch (saddr->sa_family) { case AF_INET: len1 = sizeof(struct in_addr); if (prefixlen == (sizeof(struct in_addr) << 3)) { type = IPSECDOI_ID_IPV4_ADDR; len2 = 0; } else { type = IPSECDOI_ID_IPV4_ADDR_SUBNET; len2 = sizeof(struct in_addr); } sa = (caddr_t)&((struct sockaddr_in *)(saddr))->sin_addr; port = ((struct sockaddr_in *)(saddr))->sin_port; break; #ifdef INET6 case AF_INET6: len1 = sizeof(struct in6_addr); if (prefixlen == (sizeof(struct in6_addr) << 3)) { type = IPSECDOI_ID_IPV6_ADDR; len2 = 0; } else { type = IPSECDOI_ID_IPV6_ADDR_SUBNET; len2 = sizeof(struct in6_addr); } sa = (caddr_t)&((struct sockaddr_in6 *)(saddr))->sin6_addr; port = ((struct sockaddr_in6 *)(saddr))->sin6_port; break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "invalid family: %d.\n", saddr->sa_family); return NULL; } /* get ID buffer */ new = vmalloc(sizeof(struct ipsecdoi_id_b) + len1 + len2); if (new == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get ID buffer.\n"); return NULL; } memset(new->v, 0, new->l); /* set the part of header. */ ((struct ipsecdoi_id_b *)new->v)->type = type; /* set ul_proto and port */ /* * NOTE: we use both IPSEC_ULPROTO_ANY and IPSEC_PORT_ANY as wild card * because 0 means port number of 0. Instead of 0, we use IPSEC_*_ANY. */ ((struct ipsecdoi_id_b *)new->v)->proto_id = ul_proto == IPSEC_ULPROTO_ANY ? 0 : ul_proto; ((struct ipsecdoi_id_b *)new->v)->port = port == IPSEC_PORT_ANY ? 0 : port; memcpy(new->v + sizeof(struct ipsecdoi_id_b), sa, len1); /* set address */ /* set prefix */ if (len2) { u_char *p = new->v + sizeof(struct ipsecdoi_id_b) + len1; u_int bits = prefixlen; while (bits >= 8) { *p++ = 0xff; bits -= 8; } if (bits > 0) *p = ~((1 << (8 - bits)) - 1); } return new; } /* * create sockaddr structure from ID payload (buf). * buffers (saddr, prefixlen, ul_proto) must be allocated. * see, RFC2407 4.6.2.1 */ int ipsecdoi_id2sockaddr(buf, saddr, prefixlen, ul_proto) vchar_t *buf; struct sockaddr *saddr; u_int8_t *prefixlen; u_int16_t *ul_proto; { struct ipsecdoi_id_b *id_b = (struct ipsecdoi_id_b *)buf->v; u_int plen = 0; /* * When a ID payload of subnet type with a IP address of full bit * masked, it has to be processed as host address. * e.g. below 2 type are same. * type = ipv6 subnet, data = 2001::1/128 * type = ipv6 address, data = 2001::1 */ switch (id_b->type) { case IPSECDOI_ID_IPV4_ADDR: case IPSECDOI_ID_IPV4_ADDR_SUBNET: saddr->sa_len = sizeof(struct sockaddr_in); saddr->sa_family = AF_INET; ((struct sockaddr_in *)saddr)->sin_port = (id_b->port == 0 ? IPSEC_PORT_ANY : id_b->port); /* see sockaddr2id() */ memcpy(&((struct sockaddr_in *)saddr)->sin_addr, buf->v + sizeof(*id_b), sizeof(struct in_addr)); break; #ifdef INET6 case IPSECDOI_ID_IPV6_ADDR: case IPSECDOI_ID_IPV6_ADDR_SUBNET: saddr->sa_len = sizeof(struct sockaddr_in6); saddr->sa_family = AF_INET6; ((struct sockaddr_in6 *)saddr)->sin6_port = (id_b->port == 0 ? IPSEC_PORT_ANY : id_b->port); /* see sockaddr2id() */ memcpy(&((struct sockaddr_in6 *)saddr)->sin6_addr, buf->v + sizeof(*id_b), sizeof(struct in6_addr)); break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "unsupported ID type %d\n", id_b->type); return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } /* get prefix length */ switch (id_b->type) { case IPSECDOI_ID_IPV4_ADDR: plen = sizeof(struct in_addr) << 3; break; #ifdef INET6 case IPSECDOI_ID_IPV6_ADDR: plen = sizeof(struct in6_addr) << 3; break; #endif case IPSECDOI_ID_IPV4_ADDR_SUBNET: #ifdef INET6 case IPSECDOI_ID_IPV6_ADDR_SUBNET: #endif { u_char *p; u_int max; int alen = sizeof(struct in_addr); switch (id_b->type) { case IPSECDOI_ID_IPV4_ADDR_SUBNET: alen = sizeof(struct in_addr); break; #ifdef INET6 case IPSECDOI_ID_IPV6_ADDR_SUBNET: alen = sizeof(struct in6_addr); break; #endif } /* sanity check */ if (buf->l < alen) return ISAKMP_INTERNAL_ERROR; /* get subnet mask length */ plen = 0; max = alen <<3; p = buf->v + sizeof(struct ipsecdoi_id_b) + alen; for (; *p == 0xff; p++) { if (plen >= max) break; plen += 8; } if (plen < max) { u_int l = 0; u_char b = ~(*p); while (b) { b >>= 1; l++; } l = 8 - l; plen += l; } } break; } *prefixlen = plen; *ul_proto = id_b->proto_id == 0 ? IPSEC_ULPROTO_ANY : id_b->proto_id; /* see sockaddr2id() */ return 0; } /* * make printable string from ID payload except of general header. */ const char * ipsecdoi_id2str(id) const vchar_t *id; { static char buf[256]; /* XXX */ buf[0] = '\0'; return buf; } /* * set IPsec data attributes into a proposal. * NOTE: MUST called per a transform. */ int ipsecdoi_t2satrns(t, pp, pr, tr) struct isakmp_pl_t *t; struct saprop *pp; struct saproto *pr; struct satrns *tr; { struct isakmp_data *d, *prev; int flag, type; int error = -1; int life_t; int tlen; tr->trns_no = t->t_no; tr->trns_id = t->t_id; tlen = ntohs(t->h.len) - sizeof(*t); prev = (struct isakmp_data *)NULL; d = (struct isakmp_data *)(t + 1); /* default */ life_t = IPSECDOI_ATTR_SA_LD_TYPE_DEFAULT; pp->lifetime = IPSECDOI_ATTR_SA_LD_SEC_DEFAULT; pp->lifebyte = 0; tr->authtype = IPSECDOI_ATTR_AUTH_NONE; while (tlen > 0) { type = ntohs(d->type) & ~ISAKMP_GEN_MASK; flag = ntohs(d->type) & ISAKMP_GEN_MASK; plog(LLV_DEBUG, LOCATION, NULL, "type=%s, flag=0x%04x, lorv=%s\n", s_ipsecdoi_attr(type), flag, s_ipsecdoi_attr_v(type, ntohs(d->lorv))); switch (type) { case IPSECDOI_ATTR_SA_LD_TYPE: { int type = ntohs(d->lorv); switch (type) { case IPSECDOI_ATTR_SA_LD_TYPE_SEC: case IPSECDOI_ATTR_SA_LD_TYPE_KB: life_t = type; break; default: plog(LLV_WARNING, LOCATION, NULL, "invalid life duration type. " "use default\n"); life_t = IPSECDOI_ATTR_SA_LD_TYPE_DEFAULT; break; } break; } case IPSECDOI_ATTR_SA_LD: if (prev == NULL || (ntohs(prev->type) & ~ISAKMP_GEN_MASK) != IPSECDOI_ATTR_SA_LD_TYPE) { plog(LLV_ERROR, LOCATION, NULL, "life duration must follow ltype\n"); break; } { u_int32_t t; vchar_t *ld_buf = NULL; if (flag) { /* i.e. ISAKMP_GEN_TV */ ld_buf = vmalloc(sizeof(d->lorv)); if (ld_buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get LD buffer.\n"); goto end; } memcpy(ld_buf->v, &d->lorv, sizeof(d->lorv)); } else { int len = ntohs(d->lorv); /* i.e. ISAKMP_GEN_TLV */ ld_buf = vmalloc(len); if (ld_buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get LD buffer.\n"); goto end; } memcpy(ld_buf->v, d + 1, len); } switch (life_t) { case IPSECDOI_ATTR_SA_LD_TYPE_SEC: t = ipsecdoi_set_ld(ld_buf); vfree(ld_buf); if (t == 0) { plog(LLV_ERROR, LOCATION, NULL, "invalid life duration.\n"); goto end; } /* lifetime must be equal in a proposal. */ if (pp->lifetime == IPSECDOI_ATTR_SA_LD_SEC_DEFAULT) pp->lifetime = t; else if (pp->lifetime != t) { plog(LLV_ERROR, LOCATION, NULL, "lifetime mismatched " "in a proposal, " "prev:%ld curr:%ld.\n", pp->lifetime, t); goto end; } break; case IPSECDOI_ATTR_SA_LD_TYPE_KB: t = ipsecdoi_set_ld(ld_buf); vfree(ld_buf); if (t == 0) { plog(LLV_ERROR, LOCATION, NULL, "invalid life duration.\n"); goto end; } /* lifebyte must be equal in a proposal. */ if (pp->lifebyte == 0) pp->lifebyte = t; else if (pp->lifebyte != t) { plog(LLV_ERROR, LOCATION, NULL, "lifebyte mismatched " "in a proposal, " "prev:%ld curr:%ld.\n", pp->lifebyte, t); goto end; } break; default: vfree(ld_buf); plog(LLV_ERROR, LOCATION, NULL, "invalid life type: %d\n", life_t); goto end; } } break; case IPSECDOI_ATTR_GRP_DESC: /* * RFC2407: 4.5 IPSEC Security Association Attributes * Specifies the Oakley Group to be used in a PFS QM * negotiation. For a list of supported values, see * Appendix A of [IKE]. */ if (pp->pfs_group == 0) pp->pfs_group = (u_int16_t)ntohs(d->lorv); else if (pp->pfs_group != (u_int16_t)ntohs(d->lorv)) { plog(LLV_ERROR, LOCATION, NULL, "pfs_group mismatched " "in a proposal.\n"); goto end; } break; case IPSECDOI_ATTR_ENC_MODE: if (pr->encmode && pr->encmode != (u_int16_t)ntohs(d->lorv)) { plog(LLV_ERROR, LOCATION, NULL, "multiple encmode exist " "in a transform.\n"); goto end; } pr->encmode = (u_int16_t)ntohs(d->lorv); break; case IPSECDOI_ATTR_AUTH: if (tr->authtype != IPSECDOI_ATTR_AUTH_NONE) { plog(LLV_ERROR, LOCATION, NULL, "multiple authtype exist " "in a transform.\n"); goto end; } tr->authtype = (u_int16_t)ntohs(d->lorv); break; case IPSECDOI_ATTR_KEY_LENGTH: if (pr->proto_id != IPSECDOI_PROTO_IPSEC_ESP) { plog(LLV_ERROR, LOCATION, NULL, "key length defined but not ESP"); goto end; } tr->encklen = ntohs(d->lorv); break; case IPSECDOI_ATTR_KEY_ROUNDS: case IPSECDOI_ATTR_COMP_DICT_SIZE: case IPSECDOI_ATTR_COMP_PRIVALG: default: break; } prev = d; if (flag) { tlen -= sizeof(*d); d = (struct isakmp_data *)((char *)d + sizeof(*d)); } else { tlen -= (sizeof(*d) + ntohs(d->lorv)); d = (struct isakmp_data *)((caddr_t)d + sizeof(*d) + ntohs(d->lorv)); } } error = 0; end: return error; } int ipsecdoi_authalg2trnsid(alg) int alg; { switch (alg) { case IPSECDOI_ATTR_AUTH_HMAC_MD5: return IPSECDOI_AH_MD5; case IPSECDOI_ATTR_AUTH_HMAC_SHA1: return IPSECDOI_AH_SHA; case IPSECDOI_ATTR_AUTH_DES_MAC: return IPSECDOI_AH_DES; case IPSECDOI_ATTR_AUTH_KPDK: return IPSECDOI_AH_MD5; /* XXX */ default: plog(LLV_ERROR, LOCATION, NULL, "invalid authentication algorithm:%d\n", alg); } return -1; } #ifdef HAVE_GSSAPI struct isakmpsa * fixup_initiator_sa(match, received) struct isakmpsa *match, *received; { struct isakmpsa *newsa; if (received->gssid == NULL) return match; newsa = newisakmpsa(); memcpy(newsa, match, sizeof *newsa); if (match->dhgrp != NULL) { newsa->dhgrp = racoon_calloc(1, sizeof(struct dhgroup)); memcpy(newsa->dhgrp, match->dhgrp, sizeof (struct dhgroup)); } newsa->next = NULL; newsa->rmconf = NULL; newsa->gssid = vdup(received->gssid); return newsa; } #endif static int rm_idtype2doi[] = { IPSECDOI_ID_FQDN, IPSECDOI_ID_USER_FQDN, IPSECDOI_ID_KEY_ID, 255, /* it's type of "address" * it expands into 4 types by another function. */ IPSECDOI_ID_DER_ASN1_DN, }; /* * convert idtype to DOI value. * OUT 255 : NG * other: converted. */ int idtype2doi(idtype) int idtype; { if (ARRAYLEN(rm_idtype2doi) > idtype) return rm_idtype2doi[idtype]; return 255; } int doi2idtype(doi) int doi; { switch(doi) { case IPSECDOI_ID_FQDN: return(IDTYPE_FQDN); case IPSECDOI_ID_USER_FQDN: return(IDTYPE_USERFQDN); case IPSECDOI_ID_KEY_ID: return(IDTYPE_KEYID); case IPSECDOI_ID_DER_ASN1_DN: return(IDTYPE_ASN1DN); case IPSECDOI_ID_IPV4_ADDR: case IPSECDOI_ID_IPV4_ADDR_SUBNET: case IPSECDOI_ID_IPV6_ADDR: case IPSECDOI_ID_IPV6_ADDR_SUBNET: return(IDTYPE_ADDRESS); default: plog(LLV_WARNING, LOCATION, NULL, "Inproper idtype:%d in this function.\n", s_ipsecdoi_ident(doi)); return(IDTYPE_ADDRESS); /* XXX */ } /*NOTREACHED*/ }