| /* $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 <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/socket.h> |
| |
| #include <netinet/in.h> |
| |
| #include PATH_IPSEC_H |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <netdb.h> |
| #if TIME_WITH_SYS_TIME |
| # include <sys/time.h> |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # 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 <iconv.h> |
| #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, "<ASN1-DN>"); |
| } |
| |
| break; |
| } |
| |
| /* currently unhandled id types */ |
| case IPSECDOI_ID_KEY_ID: |
| len = sprintf( buf, "<KEY-ID>"); |
| 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*/ |
| } |