/* $NetBSD: ipsec_doi.c,v 1.46 2010/12/14 17:57:31 tteras Exp $ */ /* Id: ipsec_doi.c,v 1.55 2006/08/17 09:20:41 vanhu 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 "config.h" #include #include #include #include #include PATH_IPSEC_H #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 ENABLE_NATT #include "nattraversal.h" #endif #ifdef HAVE_GSSAPI #include #include "gssapi.h" #ifdef HAVE_ICONV_2ND_CONST #define __iconv_const const #else #define __iconv_const #endif #endif static vchar_t *get_ph1approval __P((struct ph1handle *, u_int32_t, u_int32_t, struct prop_pair **)); static int get_ph1approvalx __P((struct remoteconf *, void *)); static int t2isakmpsa __P((struct isakmp_pl_t *, struct isakmpsa *, u_int32_t)); 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 struct prop_pair ** get_proppair_and_doi_sit __P((vchar_t *, int, u_int32_t *, u_int32_t *)); static int get_transform __P((struct isakmp_pl_p *, struct prop_pair **, 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 *)); struct ph1approvalx_ctx { struct prop_pair *p; struct isakmpsa *sa; }; /*%%%*/ /* * 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; u_int32_t doitype, sittype; /* get proposal pair */ pair = get_proppair_and_doi_sit(sa, IPSECDOI_TYPE_PH1, &doitype, &sittype); if (pair == NULL) return -1; /* check and get one SA for use */ newsa = get_ph1approval(iph1, doitype, sittype, pair); free_proppair(pair); if (newsa == NULL) return -1; iph1->sa_ret = newsa; return 0; } static void print_ph1proposal(pair, s) struct prop_pair *pair; struct isakmpsa *s; { struct isakmp_pl_p *prop = pair->prop; struct isakmp_pl_t *trns = pair->trns; 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)); plog(LLV_DEBUG, LOCATION, NULL, " lifetime = %ld\n", (long) s->lifetime); plog(LLV_DEBUG, LOCATION, NULL, " lifebyte = %zu\n", s->lifebyte); plog(LLV_DEBUG, LOCATION, NULL, " enctype = %s\n", s_oakley_attr_v(OAKLEY_ATTR_ENC_ALG, s->enctype)); plog(LLV_DEBUG, LOCATION, NULL, " encklen = %d\n", s->encklen); plog(LLV_DEBUG, LOCATION, NULL, " hashtype = %s\n", s_oakley_attr_v(OAKLEY_ATTR_HASH_ALG, s->hashtype)); plog(LLV_DEBUG, LOCATION, NULL, " authmethod = %s\n", s_oakley_attr_v(OAKLEY_ATTR_AUTH_METHOD, s->authmethod)); plog(LLV_DEBUG, LOCATION, NULL, " dh_group = %s\n", s_oakley_attr_v(OAKLEY_ATTR_GRP_DESC, s->dh_group)); } /* * acceptable check for remote configuration. * return a new SA payload to be reply to peer. */ static vchar_t * get_ph1approval(iph1, doitype, sittype, pair) struct ph1handle *iph1; u_int32_t doitype, sittype; struct prop_pair **pair; { vchar_t *newsa; struct ph1approvalx_ctx ctx; struct prop_pair *s, *p; struct rmconfselector rmsel; struct isakmpsa *sa; int i; memset(&rmsel, 0, sizeof(rmsel)); rmsel.remote = iph1->remote; if (iph1->approval) { delisakmpsa(iph1->approval); iph1->approval = NULL; } for (i = 0; i < MAXPROPPAIRLEN; i++) { if (pair[i] == NULL) continue; for (s = pair[i]; s; s = s->next) { /* compare proposal and select one */ for (p = s; p; p = p->tnext) { struct isakmp_pl_p *prop = p->prop; sa = newisakmpsa(); ctx.p = p; ctx.sa = sa; if (t2isakmpsa(p->trns, sa, iph1->vendorid_mask) < 0) continue; print_ph1proposal(p, sa); if (iph1->rmconf != NULL) { if (get_ph1approvalx(iph1->rmconf, &ctx)) goto found; } else { if (enumrmconf(&rmsel, get_ph1approvalx, &ctx)) goto found; } delisakmpsa(sa); } } } plog(LLV_ERROR, LOCATION, NULL, "no suitable proposal found.\n"); return NULL; found: sa = ctx.sa; 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); sa->dhgrp=NULL; } if (oakley_setdhgroup(sa->dh_group, &sa->dhgrp) == -1) { sa->dhgrp = NULL; delisakmpsa(sa); return NULL; } saok: #ifdef HAVE_GSSAPI if (sa->gssid != NULL) plog(LLV_DEBUG, LOCATION, NULL, "gss id in new sa '%.*s'\n", (int)sa->gssid->l, sa->gssid->v); if (iph1->side == INITIATOR) { if (iph1->rmconf->proposal->gssid != NULL) iph1->gi_i = vdup(iph1->rmconf->proposal->gssid); if (sa->gssid != NULL) iph1->gi_r = vdup(sa->gssid); } else { if (sa->gssid != NULL) { iph1->gi_r = vdup(sa->gssid); iph1->gi_i = gssapi_get_id(iph1); } } if (iph1->gi_i != NULL) plog(LLV_DEBUG, LOCATION, NULL, "GIi is %.*s\n", (int)iph1->gi_i->l, iph1->gi_i->v); if (iph1->gi_r != NULL) plog(LLV_DEBUG, LOCATION, NULL, "GIr is %.*s\n", (int)iph1->gi_r->l, iph1->gi_r->v); #endif plog(LLV_DEBUG, LOCATION, NULL, "agreed on %s auth.\n", s_oakley_attr_method(sa->authmethod)); newsa = get_sabyproppair(doitype, sittype, p); if (newsa == NULL) delisakmpsa(sa); else iph1->approval = sa; return newsa; } /* * compare peer's single proposal and all of my proposal. * and select one if suiatable. */ static int get_ph1approvalx(rmconf, ctx) struct remoteconf *rmconf; void *ctx; { struct ph1approvalx_ctx *pctx = (struct ph1approvalx_ctx *) ctx; struct isakmpsa *sa; /* do the hard work */ sa = checkisakmpsa(rmconf->pcheck_level, pctx->sa, rmconf->proposal); if (sa == NULL) return 0; /* duplicate and modify the found SA to match proposal */ sa = dupisakmpsa(sa); switch (rmconf->pcheck_level) { case PROP_CHECK_OBEY: sa->lifetime = pctx->sa->lifetime; sa->lifebyte = pctx->sa->lifebyte; sa->encklen = pctx->sa->encklen; break; case PROP_CHECK_CLAIM: case PROP_CHECK_STRICT: if (pctx->sa->lifetime < sa->lifetime) sa->lifetime = pctx->sa->lifetime; if (pctx->sa->lifebyte < sa->lifebyte) sa->lifebyte = pctx->sa->lifebyte; if (pctx->sa->encklen > sa->encklen) sa->encklen = pctx->sa->encklen; break; default: break; } /* replace the proposal with our approval sa */ delisakmpsa(pctx->sa); pctx->sa = sa; return 1; } /* * get ISAKMP data attributes */ static int t2isakmpsa(trns, sa, vendorid_mask) struct isakmp_pl_t *trns; struct isakmpsa *sa; u_int32_t vendorid_mask; { 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); #ifdef HAVE_GSSAPI if (sa->authmethod == OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB_REAL && (vendorid_mask & VENDORID_GSSAPI_MASK)) sa->authmethod = OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB; #endif 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->lifebyte == 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 error = -1; iconv_t cd = (iconv_t) -1; size_t srcleft, dstleft, rv; __iconv_const char *src; char *dst; int len = ntohs(d->lorv); /* * Older verions of racoon just placed the * ISO-Latin-1 string on the wire directly. * Check to see if we are configured to be * compatible with this behavior. */ if (lcconf->gss_id_enc == LC_GSSENC_LATIN1) { if ((sa->gssid = vmalloc(len)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to allocate memory\n"); goto out; } memcpy(sa->gssid->v, d + 1, len); plog(LLV_DEBUG, LOCATION, NULL, "received old-style gss " "id '%.*s' (len %zu)\n", (int)sa->gssid->l, sa->gssid->v, sa->gssid->l); error = 0; goto out; } /* * For Windows 2000 compatibility, we expect * the GSS ID attribute on the wire to be * encoded in UTF-16LE. Internally, we work * in ISO-Latin-1. Therefore, we should need * 1/2 the specified length, which should always * be a multiple of 2 octets. */ cd = iconv_open("latin1", "utf-16le"); if (cd == (iconv_t) -1) { plog(LLV_ERROR, LOCATION, NULL, "unable to initialize utf-16le -> latin1 " "conversion descriptor: %s\n", strerror(errno)); goto out; } if ((sa->gssid = vmalloc(len / 2)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to allocate memory\n"); goto out; } src = (__iconv_const char *)(d + 1); srcleft = len; dst = sa->gssid->v; dstleft = len / 2; rv = iconv(cd, (__iconv_const char **)&src, &srcleft, &dst, &dstleft); if (rv != 0) { if (rv == -1) { plog(LLV_ERROR, LOCATION, NULL, "unable to convert GSS ID from " "utf-16le -> latin1: %s\n", strerror(errno)); } else { plog(LLV_ERROR, LOCATION, NULL, "%zd character%s in GSS ID cannot " "be represented in latin1\n", rv, rv == 1 ? "" : "s"); } goto out; } /* XXX dstleft should always be 0; assert it? */ sa->gssid->l = (len / 2) - dstleft; plog(LLV_DEBUG, LOCATION, NULL, "received gss id '%.*s' (len %zu)\n", (int)sa->gssid->l, sa->gssid->v, sa->gssid->l); error = 0; out: if (cd != (iconv_t)-1) (void)iconv_close(cd); if ((error != 0) && (sa->gssid != NULL)) { vfree(sa->gssid); sa->gssid = NULL; } break; } #endif /* HAVE_GSSAPI */ 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; u_int32_t doitype, sittype; /* get proposal pair */ pair = get_proppair_and_doi_sit(iph2->sa, IPSECDOI_TYPE_PH2, &doitype, &sittype); 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(doitype, sittype, ret); 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; vchar_t *sa_ret = NULL; u_int32_t doitype, sittype; /* get proposal pair of SA sent. */ spair = get_proppair_and_doi_sit(iph2->sa, IPSECDOI_TYPE_PH2, &doitype, &sittype); 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 replayed */ 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. */ sa_ret = iph2->sa_ret; iph2->sa_ret = get_sabyproppair(doitype, sittype, p); free_proppair0(p); if (iph2->sa_ret == NULL) goto end; error = 0; end: if (rpair) free_proppair(rpair); if (spair) free_proppair(spair); if (sa_ret) vfree(sa_ret); 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->spi_size != r->prop->spi_size) { plog(LLV_ERROR, LOCATION, NULL, "invalid spi size: %d.\n", p->prop->spi_size); 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, *x; struct prop_pair *n = NULL; 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 == NULL) { 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; p = pair; while (p) { q = p->next; r = p; while (r) { s = r->tnext; racoon_free(r); r = s; } p = q; } } /* * get proposal pairs from SA payload. * tiny check for proposal payload. */ static struct prop_pair ** get_proppair_and_doi_sit(sa, mode, doitype, sittype) vchar_t *sa; int mode; u_int32_t *doitype, *sittype; { struct prop_pair **pair = NULL; 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=%zu\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 = %zu.\n", sa->l); goto bad; } /* check DOI */ if (check_doi(ntohl(sab->doi)) < 0) goto bad; if (doitype != NULL) *doitype = ntohl(sab->doi); /* check SITUATION */ if (check_situation(ntohl(sab->sit)) < 0) goto bad; if (sittype != NULL) *sittype = ntohl(sab->sit); pair = racoon_calloc(1, MAXPROPPAIRLEN * sizeof(*pair)); if (pair == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get buffer.\n"); goto bad; } 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) goto bad; 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); goto bad; } 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); goto bad; } /* 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) < 0) { vfree(pbuf); goto bad; } } 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"); goto bad; } return pair; bad: if (pair != NULL) racoon_free(pair); return NULL; } struct prop_pair ** get_proppair(sa, mode) vchar_t *sa; int mode; { return get_proppair_and_doi_sit(sa, mode, NULL, NULL); } /* * 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) struct isakmp_pl_p *prop; struct prop_pair **pair; int *num_p; { 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; } 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(doitype, sittype, pair) u_int32_t doitype, sittype; struct prop_pair *pair; { 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); newtlen += p->prop->spi_size; 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; ((struct isakmp_gen *)bp)->len = htons(newtlen); /* update some of values in SA header */ ((struct ipsecdoi_sa_b *)bp)->doi = htonl(doitype); ((struct ipsecdoi_sa_b *)bp)->sit = htonl(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 = NULL; vchar_t *newsa = NULL; 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; int error = -1; /* get proposal pair */ pair = get_proppair(sa0, IPSECDOI_TYPE_PH2); if (pair == NULL) goto out; newtlen = sizeof(struct ipsecdoi_sa_b); for (pp = pp0; pp; pp = pp->next) { if (pair[pp->prop_no] == NULL) goto out; 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) goto out; newtlen += ntohs(p->trns->h.len); } } } newsa = vmalloc(newtlen); if (newsa == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get newsa.\n"); goto out; } 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) goto out; 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; } } } error = 0; out: if (pair != NULL) racoon_free(pair); if (error != 0) { if (newsa != NULL) { vfree(newsa); newsa = NULL; } } 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 %zu of life duration " "isn't supported.\n", buf->l); return 0; } return ld; } /* * parse responder-lifetime attributes from payload */ int ipsecdoi_parse_responder_lifetime(notify, lifetime_sec, lifetime_kb) struct isakmp_pl_n *notify; u_int32_t *lifetime_sec; u_int32_t *lifetime_kb; { struct isakmp_data *d; int flag, type, tlen, ld_type = -1; u_int16_t lorv; u_int32_t value; tlen = ntohs(notify->h.len) - sizeof(*notify) - notify->spi_size; d = (struct isakmp_data *)((char *)(notify + 1) + notify->spi_size); while (tlen >= sizeof(struct isakmp_data)) { 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)); switch (type) { case IPSECDOI_ATTR_SA_LD_TYPE: if (! flag) { plog(LLV_ERROR, LOCATION, NULL, "must be TV when LD_TYPE.\n"); return -1; } ld_type = lorv; break; case IPSECDOI_ATTR_SA_LD: if (flag) value = lorv; else if (lorv == 2) value = ntohs(*(u_int16_t *)(d + 1)); else if (lorv == 4) value = ntohl(*(u_int32_t *)(d + 1)); else { plog(LLV_ERROR, LOCATION, NULL, "payload length %d for lifetime " "data length is unsupported.\n", lorv); return -1; } switch (ld_type) { case IPSECDOI_ATTR_SA_LD_TYPE_SEC: if (lifetime_sec != NULL) *lifetime_sec = value; plog(LLV_INFO, LOCATION, NULL, "received RESPONDER-LIFETIME: %d " "seconds\n", value); break; case IPSECDOI_ATTR_SA_LD_TYPE_KB: if (lifetime_kb != NULL) *lifetime_kb = value; plog(LLV_INFO, LOCATION, NULL, "received RESPONDER-LIFETIME: %d " "kbytes\n", value); break; default: plog(LLV_ERROR, LOCATION, NULL, "lifetime data received without " "lifetime data type.\n"); return -1; } break; } 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 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: case IPSECDOI_AH_SHA256: case IPSECDOI_AH_SHA384: case IPSECDOI_AH_SHA512: 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_AES: case IPSECDOI_ESP_TWOFISH: case IPSECDOI_ESP_CAMELLIA: 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: #ifdef ENABLE_HYBRID case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I: #endif #if defined(ENABLE_HYBRID) || defined(HAVE_GSSAPI) /* These two authentication method IDs overlap. */ case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I: /*case OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB:*/ #endif break; case OAKLEY_ATTR_AUTH_METHOD_DSSSIG: #ifdef ENABLE_HYBRID case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_R: #endif case OAKLEY_ATTR_AUTH_METHOD_RSAENC: case OAKLEY_ATTR_AUTH_METHOD_RSAREV: plog(LLV_ERROR, LOCATION, NULL, "auth method %s isn't supported.\n", s_oakley_attr_method(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; #ifdef ENABLE_NATT case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC: case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC: case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT: case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT: plog(LLV_DEBUG, LOCATION, NULL, "UDP encapsulation requested\n"); break; #endif 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_HMAC_SHA2_256: if (proto_id == IPSECDOI_PROTO_IPSEC_AH) { if (trns->t_id != IPSECDOI_AH_SHA256) goto ahmismatch; } break; case IPSECDOI_ATTR_AUTH_HMAC_SHA2_384: if (proto_id == IPSECDOI_PROTO_IPSEC_AH) { if (trns->t_id != IPSECDOI_AH_SHA384) goto ahmismatch; } break; case IPSECDOI_ATTR_AUTH_HMAC_SHA2_512: if (proto_id == IPSECDOI_PROTO_IPSEC_AH) { if (trns->t_id != IPSECDOI_AH_SHA512) 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; #ifdef HAVE_SECCTX case IPSECDOI_ATTR_SECCTX: if (flag) { plog(LLV_ERROR, LOCATION, NULL, "SECCTX must be in TLV.\n"); return -1; } break; #endif 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"); 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; #ifdef ENABLE_NATT case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC: case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC: case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT: case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT: plog(LLV_DEBUG, LOCATION, NULL, "UDP encapsulation requested\n"); break; #endif 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(rmconf, props) struct remoteconf *rmconf; 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(rmconf->doitype); ((struct ipsecdoi_sa_b *)mysa->v)->sit = htonl(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) { u_int32_t lifetime = htonl((u_int32_t)sa->lifetime); attrlen += sizeof(struct isakmp_data) + sizeof(struct isakmp_data); if (sa->lifetime > 0xffff) attrlen += sizeof(lifetime); if (buf) { p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD_TYPE, OAKLEY_ATTR_SA_LD_TYPE_SEC); if (sa->lifetime > 0xffff) { p = isakmp_set_attr_v(p, OAKLEY_ATTR_SA_LD, (caddr_t)&lifetime, sizeof(lifetime)); } else { p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD, sa->lifetime); } } } if (sa->lifebyte) { u_int32_t lifebyte = htonl((u_int32_t)sa->lifebyte); attrlen += sizeof(struct isakmp_data) + sizeof(struct isakmp_data); if (sa->lifebyte > 0xffff) attrlen += sizeof(lifebyte); if (buf) { p = isakmp_set_attr_l(p, OAKLEY_ATTR_SA_LD_TYPE, OAKLEY_ATTR_SA_LD_TYPE_KB); if (sa->lifebyte > 0xffff) { p = isakmp_set_attr_v(p, OAKLEY_ATTR_SA_LD, (caddr_t)&lifebyte, sizeof(lifebyte)); } 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) { int authmethod; authmethod = isakmpsa_switch_authmethod(sa->authmethod); authmethod &= 0xffff; attrlen += sizeof(struct isakmp_data); if (buf) p = isakmp_set_attr_l(p, OAKLEY_ATTR_AUTH_METHOD, 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); /* * Older versions of racoon just placed the ISO-Latin-1 * string on the wire directly. Check to see if we are * configured to be compatible with this behavior. Otherwise, * we encode the GSS ID as UTF-16LE for Windows 2000 * compatibility, which requires twice the number of octets. */ if (lcconf->gss_id_enc == LC_GSSENC_LATIN1) attrlen += sa->gssid->l; else attrlen += sa->gssid->l * 2; if (buf) { plog(LLV_DEBUG, LOCATION, NULL, "gss id attr: len %zu, " "val '%.*s'\n", sa->gssid->l, (int)sa->gssid->l, sa->gssid->v); if (lcconf->gss_id_enc == LC_GSSENC_LATIN1) { p = isakmp_set_attr_v(p, OAKLEY_ATTR_GSS_ID, (caddr_t)sa->gssid->v, sa->gssid->l); } else { size_t dstleft = sa->gssid->l * 2; size_t srcleft = sa->gssid->l; const char *src = (const char *)sa->gssid->v; char *odst, *dst = racoon_malloc(dstleft); iconv_t cd; size_t rv; cd = iconv_open("utf-16le", "latin1"); if (cd == (iconv_t) -1) { plog(LLV_ERROR, LOCATION, NULL, "unable to initialize " "latin1 -> utf-16le " "converstion descriptor: %s\n", strerror(errno)); attrlen -= sa->gssid->l * 2; goto gssid_done; } odst = dst; rv = iconv(cd, (__iconv_const char **)&src, &srcleft, &dst, &dstleft); if (rv != 0) { if (rv == -1) { plog(LLV_ERROR, LOCATION, NULL, "unable to convert GSS ID " "from latin1 -> utf-16le: " "%s\n", strerror(errno)); } else { /* should never happen */ plog(LLV_ERROR, LOCATION, NULL, "%zd character%s in GSS ID " "cannot be represented " "in utf-16le\n", rv, rv == 1 ? "" : "s"); } (void) iconv_close(cd); attrlen -= sa->gssid->l * 2; goto gssid_done; } (void) iconv_close(cd); /* XXX Check srcleft and dstleft? */ p = isakmp_set_attr_v(p, OAKLEY_ATTR_GSS_ID, odst, sa->gssid->l * 2); racoon_free(odst); } } } gssid_done: #endif /* HAVE_GSSAPI */ 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; #ifdef HAVE_SECCTX int truectxlen = 0; #endif 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); #ifdef HAVE_SECCTX /* ctx_str is defined as char ctx_str[MAX_CTXSTR_SIZ]. * The string may be smaller than MAX_CTXSTR_SIZ. */ if (*pp->sctx.ctx_str) { truectxlen = sizeof(struct security_ctx) - (MAX_CTXSTR_SIZE - pp->sctx.ctx_strlen); attrlen += sizeof(struct isakmp_data) + truectxlen; } #endif /* HAVE_SECCTX */ 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); #ifdef HAVE_SECCTX if (*pp->sctx.ctx_str) { struct security_ctx secctx; secctx = pp->sctx; secctx.ctx_strlen = htons(pp->sctx.ctx_strlen); x = isakmp_set_attr_v(x, IPSECDOI_ATTR_SECCTX, (caddr_t)&secctx, truectxlen); } #endif /* 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); } if (np_t == NULL) { plog(LLV_ERROR, LOCATION, NULL, "no suitable proposal was created.\n"); return NULL; } /* 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) { #ifdef ENABLE_NATT if (iph2->ph1->natt_flags & NAT_DETECTED) { int udp_diff = iph2->ph1->natt_options->mode_udp_diff; plog (LLV_INFO, LOCATION, NULL, "NAT detected -> UDP encapsulation " "(ENC_MODE %d->%d).\n", b->encmode, b->encmode+udp_diff); /* Tunnel -> UDP-Tunnel, Transport -> UDP_Transport */ b->encmode += udp_diff; b->udp_encap = 1; } #endif q = setph2proposal0(iph2, a, b); if (q == NULL) { VPTRINIT(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 && pr->encmode != IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC && pr->encmode != IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT) 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 if a subnet id is valid for comparison * with an address id ( address length mask ) * and compare them * Return value * = 0 for match * = 1 for mismatch */ int ipsecdoi_subnetisaddr_v4( subnet, address ) const vchar_t *subnet; const vchar_t *address; { struct in_addr *mask; if (address->l != sizeof(struct in_addr)) return 1; if (subnet->l != (sizeof(struct in_addr)*2)) return 1; mask = (struct in_addr*)(subnet->v + sizeof(struct in_addr)); if (mask->s_addr!=0xffffffff) return 1; return memcmp(subnet->v,address->v,address->l); } #ifdef INET6 int ipsecdoi_subnetisaddr_v6( subnet, address ) const vchar_t *subnet; const vchar_t *address; { struct in6_addr *mask; int i; if (address->l != sizeof(struct in6_addr)) return 1; if (subnet->l != (sizeof(struct in6_addr)*2)) return 1; mask = (struct in6_addr*)(subnet->v + sizeof(struct in6_addr)); for (i=0; i<16; i++) if(mask->s6_addr[i]!=0xff) return 1; return memcmp(subnet->v,address->v,address->l); } #endif /* * Check and Compare two IDs * - specify 0 for exact if wildcards are allowed * Return value * = 0 for match * = 1 for misatch * = -1 for integrity error */ int ipsecdoi_chkcmpids( idt, ids, exact ) const vchar_t *idt; /* id cmp target */ const vchar_t *ids; /* id cmp source */ int exact; { struct ipsecdoi_id_b *id_bt; struct ipsecdoi_id_b *id_bs; vchar_t ident_t; vchar_t ident_s; int result; /* handle wildcard IDs */ if (idt == NULL || ids == NULL) { if( !exact ) { plog(LLV_DEBUG, LOCATION, NULL, "check and compare ids : values matched (ANONYMOUS)\n" ); return 0; } else { plog(LLV_DEBUG, LOCATION, NULL, "check and compare ids : value mismatch (ANONYMOUS)\n" ); return -1; } } /* make sure the ids are of the same type */ id_bt = (struct ipsecdoi_id_b *) idt->v; id_bs = (struct ipsecdoi_id_b *) ids->v; ident_t.v = idt->v + sizeof(*id_bt); ident_t.l = idt->l - sizeof(*id_bt); ident_s.v = ids->v + sizeof(*id_bs); ident_s.l = ids->l - sizeof(*id_bs); if (id_bs->type != id_bt->type) { /* * special exception for comparing * address to subnet id types when * the netmask is address length */ if ((id_bs->type == IPSECDOI_ID_IPV4_ADDR)&& (id_bt->type == IPSECDOI_ID_IPV4_ADDR_SUBNET)) { result = ipsecdoi_subnetisaddr_v4(&ident_t,&ident_s); goto cmpid_result; } if ((id_bs->type == IPSECDOI_ID_IPV4_ADDR_SUBNET)&& (id_bt->type == IPSECDOI_ID_IPV4_ADDR)) { result = ipsecdoi_subnetisaddr_v4(&ident_s,&ident_t); goto cmpid_result; } #ifdef INET6 if ((id_bs->type == IPSECDOI_ID_IPV6_ADDR)&& (id_bt->type == IPSECDOI_ID_IPV6_ADDR_SUBNET)) { result = ipsecdoi_subnetisaddr_v6(&ident_t,&ident_s); goto cmpid_result; } if ((id_bs->type == IPSECDOI_ID_IPV6_ADDR_SUBNET)&& (id_bt->type == IPSECDOI_ID_IPV6_ADDR)) { result = ipsecdoi_subnetisaddr_v6(&ident_s,&ident_t); goto cmpid_result; } #endif plog(LLV_DEBUG, LOCATION, NULL, "check and compare ids : id type mismatch %s != %s\n", s_ipsecdoi_ident(id_bs->type), s_ipsecdoi_ident(id_bt->type)); return 1; } if(id_bs->proto_id != id_bt->proto_id){ plog(LLV_DEBUG, LOCATION, NULL, "check and compare ids : proto_id mismatch %d != %d\n", id_bs->proto_id, id_bt->proto_id); return 1; } /* compare the ID data. */ switch (id_bt->type) { case IPSECDOI_ID_DER_ASN1_DN: case IPSECDOI_ID_DER_ASN1_GN: /* compare asn1 ids */ result = eay_cmp_asn1dn(&ident_t, &ident_s); goto cmpid_result; case IPSECDOI_ID_IPV4_ADDR: /* validate lengths */ if ((ident_t.l != sizeof(struct in_addr))|| (ident_s.l != sizeof(struct in_addr))) goto cmpid_invalid; break; case IPSECDOI_ID_IPV4_ADDR_SUBNET: case IPSECDOI_ID_IPV4_ADDR_RANGE: /* validate lengths */ if ((ident_t.l != (sizeof(struct in_addr)*2))|| (ident_s.l != (sizeof(struct in_addr)*2))) goto cmpid_invalid; break; #ifdef INET6 case IPSECDOI_ID_IPV6_ADDR: /* validate lengths */ if ((ident_t.l != sizeof(struct in6_addr))|| (ident_s.l != sizeof(struct in6_addr))) goto cmpid_invalid; break; case IPSECDOI_ID_IPV6_ADDR_SUBNET: case IPSECDOI_ID_IPV6_ADDR_RANGE: /* validate lengths */ if ((ident_t.l != (sizeof(struct in6_addr)*2))|| (ident_s.l != (sizeof(struct in6_addr)*2))) goto cmpid_invalid; break; #endif case IPSECDOI_ID_FQDN: case IPSECDOI_ID_USER_FQDN: case IPSECDOI_ID_KEY_ID: break; default: plog(LLV_ERROR, LOCATION, NULL, "Unhandled id type %i specified for comparison\n", id_bt->type); return -1; } /* validate matching data and length */ if (ident_t.l == ident_s.l) result = memcmp(ident_t.v,ident_s.v,ident_t.l); else result = 1; cmpid_result: /* debug level output */ if(loglevel >= LLV_DEBUG) { char *idstrt = ipsecdoi_id2str(idt); char *idstrs = ipsecdoi_id2str(ids); if (!result) plog(LLV_DEBUG, LOCATION, NULL, "check and compare ids : values matched (%s)\n", s_ipsecdoi_ident(id_bs->type) ); else plog(LLV_DEBUG, LOCATION, NULL, "check and compare ids : value mismatch (%s)\n", s_ipsecdoi_ident(id_bs->type)); plog(LLV_DEBUG, LOCATION, NULL, "cmpid target: \'%s\'\n", idstrt ); plog(LLV_DEBUG, LOCATION, NULL, "cmpid source: \'%s\'\n", idstrs ); racoon_free(idstrs); racoon_free(idstrt); } /* return result */ if( !result ) return 0; else return 1; cmpid_invalid: /* id integrity error */ plog(LLV_DEBUG, LOCATION, NULL, "check and compare ids : %s integrity error\n", s_ipsecdoi_ident(id_bs->type)); plog(LLV_DEBUG, LOCATION, NULL, "cmpid target: length = \'%zu\'\n", ident_t.l ); plog(LLV_DEBUG, LOCATION, NULL, "cmpid source: length = \'%zu\'\n", ident_s.l ); return -1; } /* * 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; port = extract_port(iph1->remote); if (ntohs(id_b->port) != port) { plog(LLV_WARNING, LOCATION, NULL, "port %d expected, but %d\n", port, ntohs(id_b->port)); /*FALLTHROUGH*/ } } } } /* resolve remote configuration if not done yet */ if (resolveph1rmconf(iph1) < 0) return ISAKMP_NTYPE_INVALID_ID_INFORMATION; if (iph1->rmconf == NULL) return ISAKMP_NTYPE_INVALID_ID_INFORMATION; 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 = vdup(iph1->rmconf->idv); break; case IDTYPE_USERFQDN: id_b.type = IPSECDOI_ID_USER_FQDN; ident = vdup(iph1->rmconf->idv); break; case IDTYPE_KEYID: id_b.type = IPSECDOI_ID_KEY_ID; ident = vdup(iph1->rmconf->idv); break; 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); } break; 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 -1; } memcpy(ident->v, p, ident->l); } } if (!ident) { plog(LLV_ERROR, LOCATION, NULL, "failed to get ID buffer.\n"); return -1; } 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; } /* it's only called by cfparse.y. */ int set_identifier(vpp, type, value) vchar_t **vpp, *value; int type; { return set_identifier_qual(vpp, type, value, IDQUAL_UNSPEC); } int set_identifier_qual(vpp, type, value, qual) vchar_t **vpp, *value; int type; int qual; { vchar_t *new = NULL; /* simply return if value is null. */ if (!value){ if( type == IDTYPE_FQDN || type == IDTYPE_USERFQDN){ plog(LLV_ERROR, LOCATION, NULL, "No %s\n", type == IDTYPE_FQDN ? "fqdn":"user fqdn"); return -1; } return 0; } switch (type) { case IDTYPE_FQDN: case IDTYPE_USERFQDN: if(value->l <= 1){ plog(LLV_ERROR, LOCATION, NULL, "Empty %s\n", type == IDTYPE_FQDN ? "fqdn":"user fqdn"); return -1; } /* 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: /* * If no qualifier is specified: IDQUAL_UNSPEC. It means * to use a file for backward compatibility sake. */ switch(qual) { case IDQUAL_FILE: case IDQUAL_UNSPEC: { 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; } fclose(fp); break; } case IDQUAL_TAG: new = vmalloc(value->l - 1); if (new == NULL) { plog(LLV_ERROR, LOCATION, NULL, "can not allocate memory"); return -1; } memcpy(new->v, value->v, new->l); break; default: plog(LLV_ERROR, LOCATION, NULL, "unknown qualifier"); return -1; } 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(sysdep_sa_len(sa)); if (new == NULL) { racoon_free(sa); return -1; } memcpy(new->v, sa, new->l); racoon_free(sa); break; } case IDTYPE_ASN1DN: if (value->v[0] == '~') /* Hex-encoded ASN1 strings */ new = eay_hex2asn1dn(value->v + 1, - 1); else /* DN encoded strings */ new = eay_str2asn1dn(value->v, value->l - 1); if (new == NULL) return -1; if (loglevel >= LLV_DEBUG) { X509_NAME *xn; BIO *bio; unsigned char *ptr = (unsigned char *) new->v, *buf; size_t len; char save; xn = d2i_X509_NAME(NULL, (void *)&ptr, new->l); bio = BIO_new(BIO_s_mem()); X509_NAME_print_ex(bio, xn, 0, 0); len = BIO_get_mem_data(bio, &ptr); save = ptr[len]; ptr[len] = 0; plog(LLV_DEBUG, LOCATION, NULL, "Parsed DN: %s\n", ptr); ptr[len] = save; X509_NAME_free(xn); BIO_free(bio); } 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:%u.\n", iph2->spid); return -1; } if (!ipsecdoi_transportmode(iph2->proposal)) iph2->id = ipsecdoi_sockaddr2id((struct sockaddr *)&sp->spidx.src, sp->spidx.prefs, sp->spidx.ul_proto); else if (iph2->sa_src != NULL) { /* He have a specific hint indicating that the transport * mode SA will be negotiated using addresses that differ * with the one from the SA. We need to indicate that to * our peer by setting the SA address as ID. * This is typically the case for the bootstrapping of the * transport mode SA protecting BU/BA for MIPv6 traffic * * --arno*/ iph2->id = ipsecdoi_sockaddr2id(iph2->sa_src, IPSECDOI_PREFIX_HOST, sp->spidx.ul_proto); } else iph2->id = ipsecdoi_sockaddr2id(iph2->src, IPSECDOI_PREFIX_HOST, 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 */ #ifdef ANDROID_PATCHED if (1) #else if (!ipsecdoi_transportmode(iph2->proposal)) #endif iph2->id_p = ipsecdoi_sockaddr2id((struct sockaddr *)&sp->spidx.dst, sp->spidx.prefd, sp->spidx.ul_proto); else if (iph2->sa_dst != NULL) { /* See comment above for local side. */ iph2->id_p = ipsecdoi_sockaddr2id(iph2->sa_dst, IPSECDOI_PREFIX_HOST, sp->spidx.ul_proto); } else iph2->id_p = ipsecdoi_sockaddr2id(iph2->dst, IPSECDOI_PREFIX_HOST, sp->spidx.ul_proto); if (iph2->id_p == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get ID for %s\n", spidx2str(&sp->spidx)); VPTRINIT(iph2->id); 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 = (unsigned char *) 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; } vchar_t * ipsecdoi_sockrange2id(laddr, haddr, ul_proto) struct sockaddr *laddr, *haddr; u_int ul_proto; { vchar_t *new; int type, len1, len2; u_short port; if (laddr->sa_family != haddr->sa_family) { plog(LLV_ERROR, LOCATION, NULL, "Address family mismatch\n"); return NULL; } switch (laddr->sa_family) { case AF_INET: type = IPSECDOI_ID_IPV4_ADDR_RANGE; len1 = sizeof(struct in_addr); len2 = sizeof(struct in_addr); break; #ifdef INET6 case AF_INET6: type = IPSECDOI_ID_IPV6_ADDR_RANGE; len1 = sizeof(struct in6_addr); len2 = sizeof(struct in6_addr); break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "invalid family: %d.\n", laddr->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; port = ((struct sockaddr_in *)(laddr))->sin_port; ((struct ipsecdoi_id_b *)new->v)->port = port == IPSEC_PORT_ANY ? 0 : port; memcpy(new->v + sizeof(struct ipsecdoi_id_b), (caddr_t)&((struct sockaddr_in *)(laddr))->sin_addr, len1); memcpy(new->v + sizeof(struct ipsecdoi_id_b) + len1, (caddr_t)&((struct sockaddr_in *)haddr)->sin_addr, len2); 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 = NULL; u_int plen = 0; if (buf == NULL) return ISAKMP_INTERNAL_ERROR; id_b = (struct ipsecdoi_id_b *)buf->v; /* * 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: #ifndef __linux__ saddr->sa_len = sizeof(struct sockaddr_in); #endif 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: #ifndef __linux__ saddr->sa_len = sizeof(struct sockaddr_in6); #endif 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)); ((struct sockaddr_in6 *)saddr)->sin6_scope_id = (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)saddr)->sin6_addr) ? ((struct sockaddr_in6 *)id_b)->sin6_scope_id : 0); 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 = (unsigned char *) buf->v + sizeof(struct ipsecdoi_id_b) + alen; for (; *p == 0xff; p++) { plen += 8; if (plen >= max) break; } 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. */ char * ipsecdoi_id2str(id) const vchar_t *id; { #define BUFLEN 512 char * ret = NULL; int len = 0; char *dat; static char buf[BUFLEN]; struct ipsecdoi_id_b *id_b = (struct ipsecdoi_id_b *)id->v; union sockaddr_any saddr; u_int plen = 0; switch (id_b->type) { case IPSECDOI_ID_IPV4_ADDR: case IPSECDOI_ID_IPV4_ADDR_SUBNET: case IPSECDOI_ID_IPV4_ADDR_RANGE: #ifndef __linux__ saddr.sa.sa_len = sizeof(struct sockaddr_in); #endif saddr.sa.sa_family = AF_INET; saddr.sin.sin_port = IPSEC_PORT_ANY; memcpy(&saddr.sin.sin_addr, id->v + sizeof(*id_b), sizeof(struct in_addr)); break; #ifdef INET6 case IPSECDOI_ID_IPV6_ADDR: case IPSECDOI_ID_IPV6_ADDR_SUBNET: case IPSECDOI_ID_IPV6_ADDR_RANGE: #ifndef __linux__ saddr.sa.sa_len = sizeof(struct sockaddr_in6); #endif saddr.sa.sa_family = AF_INET6; saddr.sin6.sin6_port = IPSEC_PORT_ANY; memcpy(&saddr.sin6.sin6_addr, id->v + sizeof(*id_b), sizeof(struct in6_addr)); saddr.sin6.sin6_scope_id = (IN6_IS_ADDR_LINKLOCAL(&saddr.sin6.sin6_addr) ? ((struct sockaddr_in6 *)id_b)->sin6_scope_id : 0); break; #endif } switch (id_b->type) { case IPSECDOI_ID_IPV4_ADDR: #ifdef INET6 case IPSECDOI_ID_IPV6_ADDR: #endif len = snprintf( buf, BUFLEN, "%s", saddrwop2str(&saddr.sa)); break; 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 (id->l < alen) { len = 0; break; } /* get subnet mask length */ plen = 0; max = alen <<3; p = (unsigned char *) id->v + sizeof(struct ipsecdoi_id_b) + alen; for (; *p == 0xff; p++) { plen += 8; if (plen >= max) break; } if (plen < max) { u_int l = 0; u_char b = ~(*p); while (b) { b >>= 1; l++; } l = 8 - l; plen += l; } len = snprintf( buf, BUFLEN, "%s/%i", saddrwop2str(&saddr.sa), plen); } break; case IPSECDOI_ID_IPV4_ADDR_RANGE: len = snprintf( buf, BUFLEN, "%s-", saddrwop2str(&saddr.sa)); #ifndef __linux__ saddr.sa.sa_len = sizeof(struct sockaddr_in); #endif saddr.sa.sa_family = AF_INET; saddr.sin.sin_port = IPSEC_PORT_ANY; memcpy(&saddr.sin.sin_addr, id->v + sizeof(*id_b) + sizeof(struct in_addr), sizeof(struct in_addr)); len += snprintf(buf + len, BUFLEN - len, "%s", saddrwop2str(&saddr.sa)); break; #ifdef INET6 case IPSECDOI_ID_IPV6_ADDR_RANGE: len = snprintf( buf, BUFLEN, "%s-", saddrwop2str(&saddr.sa)); #ifndef __linux__ saddr.sa.sa_len = sizeof(struct sockaddr_in6); #endif saddr.sa.sa_family = AF_INET6; saddr.sin6.sin6_port = IPSEC_PORT_ANY; memcpy(&saddr.sin6.sin6_addr, id->v + sizeof(*id_b) + sizeof(struct in6_addr), sizeof(struct in6_addr)); saddr.sin6.sin6_scope_id = (IN6_IS_ADDR_LINKLOCAL(&saddr.sin6.sin6_addr) ? ((struct sockaddr_in6 *)id_b)->sin6_scope_id : 0); len += snprintf(buf + len, BUFLEN - len, "%s", saddrwop2str(&saddr.sa)); break; #endif case IPSECDOI_ID_FQDN: case IPSECDOI_ID_USER_FQDN: len = id->l - sizeof(*id_b); if (len > BUFLEN) len = BUFLEN; memcpy(buf, id->v + sizeof(*id_b), len); break; case IPSECDOI_ID_DER_ASN1_DN: case IPSECDOI_ID_DER_ASN1_GN: { X509_NAME *xn = NULL; dat = id->v + sizeof(*id_b); len = id->l - sizeof(*id_b); if (d2i_X509_NAME(&xn, (void*) &dat, len) != NULL) { BIO *bio = BIO_new(BIO_s_mem()); X509_NAME_print_ex(bio, xn, 0, 0); len = BIO_get_mem_data(bio, &dat); if (len > BUFLEN) len = BUFLEN; memcpy(buf,dat,len); BIO_free(bio); X509_NAME_free(xn); } else { plog(LLV_ERROR, LOCATION, NULL, "unable to extract asn1dn from id\n"); len = sprintf(buf, ""); } break; } /* currently unhandled id types */ case IPSECDOI_ID_KEY_ID: len = sprintf( buf, ""); break; default: plog(LLV_ERROR, LOCATION, NULL, "unknown ID type %d\n", id_b->type); } if (!len) len = sprintf( buf, ""); ret = racoon_malloc(len+1); if (ret != NULL) { memcpy(ret,buf,len); ret[len]=0; } return ret; } /* * 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:%u.\n", (long)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:%d curr:%u.\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; #ifdef HAVE_SECCTX case IPSECDOI_ATTR_SECCTX: { int len = ntohs(d->lorv); memcpy(&pp->sctx, d + 1, len); pp->sctx.ctx_strlen = ntohs(pp->sctx.ctx_strlen); break; } #endif /* HAVE_SECCTX */ 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_HMAC_SHA2_256: return IPSECDOI_AH_SHA256; case IPSECDOI_ATTR_AUTH_HMAC_SHA2_384: return IPSECDOI_AH_SHA384; case IPSECDOI_ATTR_AUTH_HMAC_SHA2_512: return IPSECDOI_AH_SHA512; 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; } static int rm_idtype2doi[] = { 255, /* IDTYPE_UNDEFINED, 0 */ IPSECDOI_ID_FQDN, /* IDTYPE_FQDN, 1 */ IPSECDOI_ID_USER_FQDN, /* IDTYPE_USERFQDN, 2 */ IPSECDOI_ID_KEY_ID, /* IDTYPE_KEYID, 3 */ 255, /* IDTYPE_ADDRESS, 4 * it expands into 4 types by another function. */ IPSECDOI_ID_DER_ASN1_DN, /* IDTYPE_ASN1DN, 5 */ }; /* * 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:%s in this function.\n", s_ipsecdoi_ident(doi)); return(IDTYPE_ADDRESS); /* XXX */ } /*NOTREACHED*/ }