/* $NetBSD: oakley.c,v 1.22.2.2 2012/08/29 11:35:09 tteras Exp $ */ /* Id: oakley.c,v 1.32 2006/05/26 12:19:46 manubsd Exp */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include #include #include /* XXX for subjectaltname */ #include /* XXX for subjectaltname */ #include #include #include #include #include #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #ifdef ENABLE_HYBRID #include #endif #include "var.h" #include "misc.h" #include "vmbuf.h" #include "str2val.h" #include "plog.h" #include "debug.h" #include "isakmp_var.h" #include "isakmp.h" #ifdef ENABLE_HYBRID #include "isakmp_xauth.h" #include "isakmp_cfg.h" #endif #include "oakley.h" #include "admin.h" #include "privsep.h" #include "localconf.h" #include "remoteconf.h" #include "policy.h" #include "handler.h" #include "ipsec_doi.h" #include "algorithm.h" #include "dhgroup.h" #include "sainfo.h" #include "proposal.h" #include "crypto_openssl.h" #include "dnssec.h" #include "sockmisc.h" #include "strnames.h" #include "gcmalloc.h" #include "rsalist.h" #ifdef HAVE_GSSAPI #include "gssapi.h" #endif #define OUTBOUND_SA 0 #define INBOUND_SA 1 #define INITDHVAL(a, s, d, t) \ do { \ vchar_t buf; \ buf.v = str2val((s), 16, &buf.l); \ memset(&a, 0, sizeof(struct dhgroup)); \ a.type = (t); \ a.prime = vdup(&buf); \ a.gen1 = 2; \ a.gen2 = 0; \ racoon_free(buf.v); \ } while(0); struct dhgroup dh_modp768; struct dhgroup dh_modp1024; struct dhgroup dh_modp1536; struct dhgroup dh_modp2048; struct dhgroup dh_modp3072; struct dhgroup dh_modp4096; struct dhgroup dh_modp6144; struct dhgroup dh_modp8192; static int oakley_check_dh_pub __P((vchar_t *, vchar_t **)); static int oakley_compute_keymat_x __P((struct ph2handle *, int, int)); static int oakley_check_certid __P((struct ph1handle *iph1)); static int check_typeofcertname __P((int, int)); static int oakley_padlen __P((int, int)); static int get_plainrsa_fromlocal __P((struct ph1handle *, int)); int oakley_get_certtype(cert) vchar_t *cert; { if (cert == NULL) return ISAKMP_CERT_NONE; return cert->v[0]; } static vchar_t * dump_isakmp_payload(gen) struct isakmp_gen *gen; { vchar_t p; if (ntohs(gen->len) <= sizeof(*gen)) { plog(LLV_ERROR, LOCATION, NULL, "Len is too small !!.\n"); return NULL; } p.v = (caddr_t) (gen + 1); p.l = ntohs(gen->len) - sizeof(*gen); return vdup(&p); } static vchar_t * dump_x509(cert) X509 *cert; { vchar_t *pl; u_char *bp; int len; len = i2d_X509(cert, NULL); pl = vmalloc(len + 1); if (pl == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Failed to copy CERT from packet.\n"); return NULL; } pl->v[0] = ISAKMP_CERT_X509SIGN; bp = (u_char *) &pl->v[1]; i2d_X509(cert, &bp); return pl; } int oakley_get_defaultlifetime() { return OAKLEY_ATTR_SA_LD_SEC_DEFAULT; } int oakley_dhinit() { /* set DH MODP */ INITDHVAL(dh_modp768, OAKLEY_PRIME_MODP768, OAKLEY_ATTR_GRP_DESC_MODP768, OAKLEY_ATTR_GRP_TYPE_MODP); INITDHVAL(dh_modp1024, OAKLEY_PRIME_MODP1024, OAKLEY_ATTR_GRP_DESC_MODP1024, OAKLEY_ATTR_GRP_TYPE_MODP); INITDHVAL(dh_modp1536, OAKLEY_PRIME_MODP1536, OAKLEY_ATTR_GRP_DESC_MODP1536, OAKLEY_ATTR_GRP_TYPE_MODP); INITDHVAL(dh_modp2048, OAKLEY_PRIME_MODP2048, OAKLEY_ATTR_GRP_DESC_MODP2048, OAKLEY_ATTR_GRP_TYPE_MODP); INITDHVAL(dh_modp3072, OAKLEY_PRIME_MODP3072, OAKLEY_ATTR_GRP_DESC_MODP3072, OAKLEY_ATTR_GRP_TYPE_MODP); INITDHVAL(dh_modp4096, OAKLEY_PRIME_MODP4096, OAKLEY_ATTR_GRP_DESC_MODP4096, OAKLEY_ATTR_GRP_TYPE_MODP); INITDHVAL(dh_modp6144, OAKLEY_PRIME_MODP6144, OAKLEY_ATTR_GRP_DESC_MODP6144, OAKLEY_ATTR_GRP_TYPE_MODP); INITDHVAL(dh_modp8192, OAKLEY_PRIME_MODP8192, OAKLEY_ATTR_GRP_DESC_MODP8192, OAKLEY_ATTR_GRP_TYPE_MODP); return 0; } void oakley_dhgrp_free(dhgrp) struct dhgroup *dhgrp; { if (dhgrp->prime) vfree(dhgrp->prime); if (dhgrp->curve_a) vfree(dhgrp->curve_a); if (dhgrp->curve_b) vfree(dhgrp->curve_b); if (dhgrp->order) vfree(dhgrp->order); racoon_free(dhgrp); } /* * RFC2409 5 * The length of the Diffie-Hellman public value MUST be equal to the * length of the prime modulus over which the exponentiation was * performed, prepending zero bits to the value if necessary. */ static int oakley_check_dh_pub(prime, pub0) vchar_t *prime, **pub0; { vchar_t *tmp; vchar_t *pub = *pub0; if (prime->l == pub->l) return 0; if (prime->l < pub->l) { /* what should i do ? */ plog(LLV_ERROR, LOCATION, NULL, "invalid public information was generated.\n"); return -1; } /* prime->l > pub->l */ tmp = vmalloc(prime->l); if (tmp == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get DH buffer.\n"); return -1; } memcpy(tmp->v + prime->l - pub->l, pub->v, pub->l); vfree(*pub0); *pub0 = tmp; return 0; } /* * compute sharing secret of DH * IN: *dh, *pub, *priv, *pub_p * OUT: **gxy */ int oakley_dh_compute(dh, pub, priv, pub_p, gxy) const struct dhgroup *dh; vchar_t *pub, *priv, *pub_p, **gxy; { #ifdef ENABLE_STATS struct timeval start, end; #endif if ((*gxy = vmalloc(dh->prime->l)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get DH buffer.\n"); return -1; } #ifdef ENABLE_STATS gettimeofday(&start, NULL); #endif switch (dh->type) { case OAKLEY_ATTR_GRP_TYPE_MODP: if (eay_dh_compute(dh->prime, dh->gen1, pub, priv, pub_p, gxy) < 0) { plog(LLV_ERROR, LOCATION, NULL, "failed to compute dh value.\n"); return -1; } break; case OAKLEY_ATTR_GRP_TYPE_ECP: case OAKLEY_ATTR_GRP_TYPE_EC2N: plog(LLV_ERROR, LOCATION, NULL, "dh type %d isn't supported.\n", dh->type); return -1; default: plog(LLV_ERROR, LOCATION, NULL, "invalid dh type %d.\n", dh->type); return -1; } #ifdef ENABLE_STATS gettimeofday(&end, NULL); syslog(LOG_NOTICE, "%s(%s%zu): %8.6f", __func__, s_attr_isakmp_group(dh->type), dh->prime->l << 3, timedelta(&start, &end)); #endif plog(LLV_DEBUG, LOCATION, NULL, "compute DH's shared.\n"); plogdump(LLV_DEBUG, (*gxy)->v, (*gxy)->l); return 0; } /* * generate values of DH * IN: *dh * OUT: **pub, **priv */ int oakley_dh_generate(dh, pub, priv) const struct dhgroup *dh; vchar_t **pub, **priv; { #ifdef ENABLE_STATS struct timeval start, end; gettimeofday(&start, NULL); #endif switch (dh->type) { case OAKLEY_ATTR_GRP_TYPE_MODP: if (eay_dh_generate(dh->prime, dh->gen1, dh->gen2, pub, priv) < 0) { plog(LLV_ERROR, LOCATION, NULL, "failed to compute dh value.\n"); return -1; } break; case OAKLEY_ATTR_GRP_TYPE_ECP: case OAKLEY_ATTR_GRP_TYPE_EC2N: plog(LLV_ERROR, LOCATION, NULL, "dh type %d isn't supported.\n", dh->type); return -1; default: plog(LLV_ERROR, LOCATION, NULL, "invalid dh type %d.\n", dh->type); return -1; } #ifdef ENABLE_STATS gettimeofday(&end, NULL); syslog(LOG_NOTICE, "%s(%s%zu): %8.6f", __func__, s_attr_isakmp_group(dh->type), dh->prime->l << 3, timedelta(&start, &end)); #endif if (oakley_check_dh_pub(dh->prime, pub) != 0) return -1; plog(LLV_DEBUG, LOCATION, NULL, "compute DH's private.\n"); plogdump(LLV_DEBUG, (*priv)->v, (*priv)->l); plog(LLV_DEBUG, LOCATION, NULL, "compute DH's public.\n"); plogdump(LLV_DEBUG, (*pub)->v, (*pub)->l); return 0; } /* * copy pre-defined dhgroup values. */ int oakley_setdhgroup(group, dhgrp) int group; struct dhgroup **dhgrp; { struct dhgroup *g; *dhgrp = NULL; /* just make sure, initialize */ g = alg_oakley_dhdef_group(group); if (g == NULL) { plog(LLV_ERROR, LOCATION, NULL, "invalid DH parameter grp=%d.\n", group); return -1; } if (!g->type || !g->prime || !g->gen1) { /* unsuported */ plog(LLV_ERROR, LOCATION, NULL, "unsupported DH parameters grp=%d.\n", group); return -1; } *dhgrp = racoon_calloc(1, sizeof(struct dhgroup)); if (*dhgrp == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get DH buffer.\n"); return 0; } /* set defined dh vlaues */ memcpy(*dhgrp, g, sizeof(*g)); (*dhgrp)->prime = vdup(g->prime); return 0; } /* * PRF * * NOTE: we do not support prf with different input/output bitwidth, * so we do not implement RFC2409 Appendix B (DOORAK-MAC example) in * oakley_compute_keymat(). If you add support for such prf function, * modify oakley_compute_keymat() accordingly. */ vchar_t * oakley_prf(key, buf, iph1) vchar_t *key, *buf; struct ph1handle *iph1; { vchar_t *res = NULL; int type; if (iph1->approval == NULL) { /* * it's before negotiating hash algorithm. * We use md5 as default. */ type = OAKLEY_ATTR_HASH_ALG_MD5; } else type = iph1->approval->hashtype; res = alg_oakley_hmacdef_one(type, key, buf); if (res == NULL) { plog(LLV_ERROR, LOCATION, NULL, "invalid hmac algorithm %d.\n", type); return NULL; } return res; } /* * hash */ vchar_t * oakley_hash(buf, iph1) vchar_t *buf; struct ph1handle *iph1; { vchar_t *res = NULL; int type; if (iph1->approval == NULL) { /* * it's before negotiating hash algorithm. * We use md5 as default. */ type = OAKLEY_ATTR_HASH_ALG_MD5; } else type = iph1->approval->hashtype; res = alg_oakley_hashdef_one(type, buf); if (res == NULL) { plog(LLV_ERROR, LOCATION, NULL, "invalid hash algorithm %d.\n", type); return NULL; } return res; } /* * compute KEYMAT * see seciton 5.5 Phase 2 - Quick Mode in isakmp-oakley-05. */ int oakley_compute_keymat(iph2, side) struct ph2handle *iph2; int side; { int error = -1; /* compute sharing secret of DH when PFS */ if (iph2->approval->pfs_group && iph2->dhpub_p) { if (oakley_dh_compute(iph2->pfsgrp, iph2->dhpub, iph2->dhpriv, iph2->dhpub_p, &iph2->dhgxy) < 0) goto end; } /* compute keymat */ if (oakley_compute_keymat_x(iph2, side, INBOUND_SA) < 0 || oakley_compute_keymat_x(iph2, side, OUTBOUND_SA) < 0) goto end; plog(LLV_DEBUG, LOCATION, NULL, "KEYMAT computed.\n"); error = 0; end: return error; } /* * compute KEYMAT. * KEYMAT = prf(SKEYID_d, protocol | SPI | Ni_b | Nr_b). * If PFS is desired and KE payloads were exchanged, * KEYMAT = prf(SKEYID_d, g(qm)^xy | protocol | SPI | Ni_b | Nr_b) * * NOTE: we do not support prf with different input/output bitwidth, * so we do not implement RFC2409 Appendix B (DOORAK-MAC example). */ static int oakley_compute_keymat_x(iph2, side, sa_dir) struct ph2handle *iph2; int side; int sa_dir; { vchar_t *buf = NULL, *res = NULL, *bp; char *p; int len; int error = -1; int pfs = 0; int dupkeymat; /* generate K[1-dupkeymat] */ struct saproto *pr; struct satrns *tr; int encklen, authklen, l; pfs = ((iph2->approval->pfs_group && iph2->dhgxy) ? 1 : 0); len = pfs ? iph2->dhgxy->l : 0; len += (1 + sizeof(u_int32_t) /* XXX SPI size */ + iph2->nonce->l + iph2->nonce_p->l); buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get keymat buffer.\n"); goto end; } for (pr = iph2->approval->head; pr != NULL; pr = pr->next) { p = buf->v; /* if PFS */ if (pfs) { memcpy(p, iph2->dhgxy->v, iph2->dhgxy->l); p += iph2->dhgxy->l; } p[0] = pr->proto_id; p += 1; memcpy(p, (sa_dir == INBOUND_SA ? &pr->spi : &pr->spi_p), sizeof(pr->spi)); p += sizeof(pr->spi); bp = (side == INITIATOR ? iph2->nonce : iph2->nonce_p); memcpy(p, bp->v, bp->l); p += bp->l; bp = (side == INITIATOR ? iph2->nonce_p : iph2->nonce); memcpy(p, bp->v, bp->l); p += bp->l; /* compute IV */ plog(LLV_DEBUG, LOCATION, NULL, "KEYMAT compute with\n"); plogdump(LLV_DEBUG, buf->v, buf->l); /* res = K1 */ res = oakley_prf(iph2->ph1->skeyid_d, buf, iph2->ph1); if (res == NULL) goto end; /* compute key length needed */ encklen = authklen = 0; switch (pr->proto_id) { case IPSECDOI_PROTO_IPSEC_ESP: for (tr = pr->head; tr; tr = tr->next) { l = alg_ipsec_encdef_keylen(tr->trns_id, tr->encklen); if (l > encklen) encklen = l; l = alg_ipsec_hmacdef_hashlen(tr->authtype); if (l > authklen) authklen = l; } break; case IPSECDOI_PROTO_IPSEC_AH: for (tr = pr->head; tr; tr = tr->next) { l = alg_ipsec_hmacdef_hashlen(tr->trns_id); if (l > authklen) authklen = l; } break; default: break; } plog(LLV_DEBUG, LOCATION, NULL, "encklen=%d authklen=%d\n", encklen, authklen); dupkeymat = (encklen + authklen) / 8 / res->l; dupkeymat += 2; /* safety mergin */ if (dupkeymat < 3) dupkeymat = 3; plog(LLV_DEBUG, LOCATION, NULL, "generating %zu bits of key (dupkeymat=%d)\n", dupkeymat * 8 * res->l, dupkeymat); if (0 < --dupkeymat) { vchar_t *prev = res; /* K(n-1) */ vchar_t *seed = NULL; /* seed for Kn */ size_t l; /* * generating long key (isakmp-oakley-08 5.5) * KEYMAT = K1 | K2 | K3 | ... * where * src = [ g(qm)^xy | ] protocol | SPI | Ni_b | Nr_b * K1 = prf(SKEYID_d, src) * K2 = prf(SKEYID_d, K1 | src) * K3 = prf(SKEYID_d, K2 | src) * Kn = prf(SKEYID_d, K(n-1) | src) */ plog(LLV_DEBUG, LOCATION, NULL, "generating K1...K%d for KEYMAT.\n", dupkeymat + 1); seed = vmalloc(prev->l + buf->l); if (seed == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get keymat buffer.\n"); if (prev && prev != res) vfree(prev); goto end; } while (dupkeymat--) { vchar_t *this = NULL; /* Kn */ int update_prev; memcpy(seed->v, prev->v, prev->l); memcpy(seed->v + prev->l, buf->v, buf->l); this = oakley_prf(iph2->ph1->skeyid_d, seed, iph2->ph1); if (!this) { plog(LLV_ERROR, LOCATION, NULL, "oakley_prf memory overflow\n"); if (prev && prev != res) vfree(prev); vfree(this); vfree(seed); goto end; } update_prev = (prev && prev == res) ? 1 : 0; l = res->l; res = vrealloc(res, l + this->l); if (update_prev) prev = res; if (res == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get keymat buffer.\n"); if (prev && prev != res) vfree(prev); vfree(this); vfree(seed); goto end; } memcpy(res->v + l, this->v, this->l); if (prev && prev != res) vfree(prev); prev = this; this = NULL; } if (prev && prev != res) vfree(prev); vfree(seed); } plogdump(LLV_DEBUG, res->v, res->l); if (sa_dir == INBOUND_SA) pr->keymat = res; else pr->keymat_p = res; res = NULL; } error = 0; end: if (error) { for (pr = iph2->approval->head; pr != NULL; pr = pr->next) { if (pr->keymat) { vfree(pr->keymat); pr->keymat = NULL; } if (pr->keymat_p) { vfree(pr->keymat_p); pr->keymat_p = NULL; } } } if (buf != NULL) vfree(buf); if (res) vfree(res); return error; } #if notyet /* * NOTE: Must terminate by NULL. */ vchar_t * oakley_compute_hashx(struct ph1handle *iph1, ...) { vchar_t *buf, *res; vchar_t *s; caddr_t p; int len; va_list ap; /* get buffer length */ va_start(ap, iph1); len = 0; while ((s = va_arg(ap, vchar_t *)) != NULL) { len += s->l } va_end(ap); buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get hash buffer\n"); return NULL; } /* set buffer */ va_start(ap, iph1); p = buf->v; while ((s = va_arg(ap, char *)) != NULL) { memcpy(p, s->v, s->l); p += s->l; } va_end(ap); plog(LLV_DEBUG, LOCATION, NULL, "HASH with: \n"); plogdump(LLV_DEBUG, buf->v, buf->l); /* compute HASH */ res = oakley_prf(iph1->skeyid_a, buf, iph1); vfree(buf); if (res == NULL) return NULL; plog(LLV_DEBUG, LOCATION, NULL, "HASH computed:\n"); plogdump(LLV_DEBUG, res->v, res->l); return res; } #endif /* * compute HASH(3) prf(SKEYID_a, 0 | M-ID | Ni_b | Nr_b) * see seciton 5.5 Phase 2 - Quick Mode in isakmp-oakley-05. */ vchar_t * oakley_compute_hash3(iph1, msgid, body) struct ph1handle *iph1; u_int32_t msgid; vchar_t *body; { vchar_t *buf = 0, *res = 0; int len; int error = -1; /* create buffer */ len = 1 + sizeof(u_int32_t) + body->l; buf = vmalloc(len); if (buf == NULL) { plog(LLV_DEBUG, LOCATION, NULL, "failed to get hash buffer\n"); goto end; } buf->v[0] = 0; memcpy(buf->v + 1, (char *)&msgid, sizeof(msgid)); memcpy(buf->v + 1 + sizeof(u_int32_t), body->v, body->l); plog(LLV_DEBUG, LOCATION, NULL, "HASH with: \n"); plogdump(LLV_DEBUG, buf->v, buf->l); /* compute HASH */ res = oakley_prf(iph1->skeyid_a, buf, iph1); if (res == NULL) goto end; error = 0; plog(LLV_DEBUG, LOCATION, NULL, "HASH computed:\n"); plogdump(LLV_DEBUG, res->v, res->l); end: if (buf != NULL) vfree(buf); return res; } /* * compute HASH type of prf(SKEYID_a, M-ID | buffer) * e.g. * for quick mode HASH(1): * prf(SKEYID_a, M-ID | SA | Ni [ | KE ] [ | IDci | IDcr ]) * for quick mode HASH(2): * prf(SKEYID_a, M-ID | Ni_b | SA | Nr [ | KE ] [ | IDci | IDcr ]) * for Informational exchange: * prf(SKEYID_a, M-ID | N/D) */ vchar_t * oakley_compute_hash1(iph1, msgid, body) struct ph1handle *iph1; u_int32_t msgid; vchar_t *body; { vchar_t *buf = NULL, *res = NULL; char *p; int len; int error = -1; /* create buffer */ len = sizeof(u_int32_t) + body->l; buf = vmalloc(len); if (buf == NULL) { plog(LLV_DEBUG, LOCATION, NULL, "failed to get hash buffer\n"); goto end; } p = buf->v; memcpy(buf->v, (char *)&msgid, sizeof(msgid)); p += sizeof(u_int32_t); memcpy(p, body->v, body->l); plog(LLV_DEBUG, LOCATION, NULL, "HASH with:\n"); plogdump(LLV_DEBUG, buf->v, buf->l); /* compute HASH */ res = oakley_prf(iph1->skeyid_a, buf, iph1); if (res == NULL) goto end; error = 0; plog(LLV_DEBUG, LOCATION, NULL, "HASH computed:\n"); plogdump(LLV_DEBUG, res->v, res->l); end: if (buf != NULL) vfree(buf); return res; } /* * compute phase1 HASH * main/aggressive * I-digest = prf(SKEYID, g^i | g^r | CKY-I | CKY-R | SAi_b | ID_i1_b) * R-digest = prf(SKEYID, g^r | g^i | CKY-R | CKY-I | SAi_b | ID_r1_b) * for gssapi, also include all GSS tokens, and call gss_wrap on the result */ vchar_t * oakley_ph1hash_common(iph1, sw) struct ph1handle *iph1; int sw; { vchar_t *buf = NULL, *res = NULL, *bp; char *p, *bp2; int len, bl; int error = -1; #ifdef HAVE_GSSAPI vchar_t *gsstokens = NULL; #endif /* create buffer */ len = iph1->dhpub->l + iph1->dhpub_p->l + sizeof(cookie_t) * 2 + iph1->sa->l + (sw == GENERATE ? iph1->id->l : iph1->id_p->l); #ifdef HAVE_GSSAPI if (iph1->approval->authmethod == OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB) { if (iph1->gi_i != NULL && iph1->gi_r != NULL) { bp = (sw == GENERATE ? iph1->gi_i : iph1->gi_r); len += bp->l; } if (sw == GENERATE) gssapi_get_itokens(iph1, &gsstokens); else gssapi_get_rtokens(iph1, &gsstokens); if (gsstokens == NULL) return NULL; len += gsstokens->l; } #endif buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get hash buffer\n"); goto end; } p = buf->v; bp = (sw == GENERATE ? iph1->dhpub : iph1->dhpub_p); memcpy(p, bp->v, bp->l); p += bp->l; bp = (sw == GENERATE ? iph1->dhpub_p : iph1->dhpub); memcpy(p, bp->v, bp->l); p += bp->l; if (iph1->side == INITIATOR) bp2 = (sw == GENERATE ? (char *)&iph1->index.i_ck : (char *)&iph1->index.r_ck); else bp2 = (sw == GENERATE ? (char *)&iph1->index.r_ck : (char *)&iph1->index.i_ck); bl = sizeof(cookie_t); memcpy(p, bp2, bl); p += bl; if (iph1->side == INITIATOR) bp2 = (sw == GENERATE ? (char *)&iph1->index.r_ck : (char *)&iph1->index.i_ck); else bp2 = (sw == GENERATE ? (char *)&iph1->index.i_ck : (char *)&iph1->index.r_ck); bl = sizeof(cookie_t); memcpy(p, bp2, bl); p += bl; bp = iph1->sa; memcpy(p, bp->v, bp->l); p += bp->l; bp = (sw == GENERATE ? iph1->id : iph1->id_p); memcpy(p, bp->v, bp->l); p += bp->l; #ifdef HAVE_GSSAPI if (iph1->approval->authmethod == OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB) { if (iph1->gi_i != NULL && iph1->gi_r != NULL) { bp = (sw == GENERATE ? iph1->gi_i : iph1->gi_r); memcpy(p, bp->v, bp->l); p += bp->l; } memcpy(p, gsstokens->v, gsstokens->l); p += gsstokens->l; } #endif plog(LLV_DEBUG, LOCATION, NULL, "HASH with:\n"); plogdump(LLV_DEBUG, buf->v, buf->l); /* compute HASH */ res = oakley_prf(iph1->skeyid, buf, iph1); if (res == NULL) goto end; error = 0; plog(LLV_DEBUG, LOCATION, NULL, "HASH (%s) computed:\n", iph1->side == INITIATOR ? "init" : "resp"); plogdump(LLV_DEBUG, res->v, res->l); end: if (buf != NULL) vfree(buf); #ifdef HAVE_GSSAPI if (gsstokens != NULL) vfree(gsstokens); #endif return res; } /* * compute HASH_I on base mode. * base:psk,rsa * HASH_I = prf(SKEYID, g^xi | CKY-I | CKY-R | SAi_b | IDii_b) * base:sig * HASH_I = prf(hash(Ni_b | Nr_b), g^xi | CKY-I | CKY-R | SAi_b | IDii_b) */ vchar_t * oakley_ph1hash_base_i(iph1, sw) struct ph1handle *iph1; int sw; { vchar_t *buf = NULL, *res = NULL, *bp; vchar_t *hashkey = NULL; vchar_t *hash = NULL; /* for signature mode */ char *p; int len; int error = -1; /* sanity check */ if (iph1->etype != ISAKMP_ETYPE_BASE) { plog(LLV_ERROR, LOCATION, NULL, "invalid etype for this hash function\n"); return NULL; } switch (iph1->approval->authmethod) { case OAKLEY_ATTR_AUTH_METHOD_PSKEY: case OAKLEY_ATTR_AUTH_METHOD_RSAENC: case OAKLEY_ATTR_AUTH_METHOD_RSAREV: #ifdef ENABLE_HYBRID 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: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R: #endif if (iph1->skeyid == NULL) { plog(LLV_ERROR, LOCATION, NULL, "no SKEYID found.\n"); return NULL; } hashkey = iph1->skeyid; break; case OAKLEY_ATTR_AUTH_METHOD_DSSSIG: case OAKLEY_ATTR_AUTH_METHOD_RSASIG: #ifdef HAVE_GSSAPI case OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB: #endif #ifdef ENABLE_HYBRID case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R: #endif /* make hash for seed */ len = iph1->nonce->l + iph1->nonce_p->l; buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get hash buffer\n"); goto end; } p = buf->v; bp = (sw == GENERATE ? iph1->nonce_p : iph1->nonce); memcpy(p, bp->v, bp->l); p += bp->l; bp = (sw == GENERATE ? iph1->nonce : iph1->nonce_p); memcpy(p, bp->v, bp->l); p += bp->l; hash = oakley_hash(buf, iph1); if (hash == NULL) goto end; vfree(buf); buf = NULL; hashkey = hash; break; default: plog(LLV_ERROR, LOCATION, NULL, "not supported authentication method %d\n", iph1->approval->authmethod); return NULL; } len = (sw == GENERATE ? iph1->dhpub->l : iph1->dhpub_p->l) + sizeof(cookie_t) * 2 + iph1->sa->l + (sw == GENERATE ? iph1->id->l : iph1->id_p->l); buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get hash buffer\n"); goto end; } p = buf->v; bp = (sw == GENERATE ? iph1->dhpub : iph1->dhpub_p); memcpy(p, bp->v, bp->l); p += bp->l; memcpy(p, &iph1->index.i_ck, sizeof(cookie_t)); p += sizeof(cookie_t); memcpy(p, &iph1->index.r_ck, sizeof(cookie_t)); p += sizeof(cookie_t); memcpy(p, iph1->sa->v, iph1->sa->l); p += iph1->sa->l; bp = (sw == GENERATE ? iph1->id : iph1->id_p); memcpy(p, bp->v, bp->l); p += bp->l; plog(LLV_DEBUG, LOCATION, NULL, "HASH_I with:\n"); plogdump(LLV_DEBUG, buf->v, buf->l); /* compute HASH */ res = oakley_prf(hashkey, buf, iph1); if (res == NULL) goto end; error = 0; plog(LLV_DEBUG, LOCATION, NULL, "HASH_I computed:\n"); plogdump(LLV_DEBUG, res->v, res->l); end: if (hash != NULL) vfree(hash); if (buf != NULL) vfree(buf); return res; } /* * compute HASH_R on base mode for signature method. * base: * HASH_R = prf(hash(Ni_b | Nr_b), g^xi | g^xr | CKY-I | CKY-R | SAi_b | IDii_b) */ vchar_t * oakley_ph1hash_base_r(iph1, sw) struct ph1handle *iph1; int sw; { vchar_t *buf = NULL, *res = NULL, *bp; vchar_t *hash = NULL; char *p; int len; int error = -1; /* sanity check */ if (iph1->etype != ISAKMP_ETYPE_BASE) { plog(LLV_ERROR, LOCATION, NULL, "invalid etype for this hash function\n"); return NULL; } switch (iph1->approval->authmethod) { case OAKLEY_ATTR_AUTH_METHOD_DSSSIG: case OAKLEY_ATTR_AUTH_METHOD_RSASIG: #ifdef ENABLE_HYBRID case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I: #endif break; default: plog(LLV_ERROR, LOCATION, NULL, "not supported authentication method %d\n", iph1->approval->authmethod); return NULL; break; } /* make hash for seed */ len = iph1->nonce->l + iph1->nonce_p->l; buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get hash buffer\n"); goto end; } p = buf->v; bp = (sw == GENERATE ? iph1->nonce_p : iph1->nonce); memcpy(p, bp->v, bp->l); p += bp->l; bp = (sw == GENERATE ? iph1->nonce : iph1->nonce_p); memcpy(p, bp->v, bp->l); p += bp->l; hash = oakley_hash(buf, iph1); if (hash == NULL) goto end; vfree(buf); buf = NULL; /* make really hash */ len = (sw == GENERATE ? iph1->dhpub_p->l : iph1->dhpub->l) + (sw == GENERATE ? iph1->dhpub->l : iph1->dhpub_p->l) + sizeof(cookie_t) * 2 + iph1->sa->l + (sw == GENERATE ? iph1->id_p->l : iph1->id->l); buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get hash buffer\n"); goto end; } p = buf->v; bp = (sw == GENERATE ? iph1->dhpub_p : iph1->dhpub); memcpy(p, bp->v, bp->l); p += bp->l; bp = (sw == GENERATE ? iph1->dhpub : iph1->dhpub_p); memcpy(p, bp->v, bp->l); p += bp->l; memcpy(p, &iph1->index.i_ck, sizeof(cookie_t)); p += sizeof(cookie_t); memcpy(p, &iph1->index.r_ck, sizeof(cookie_t)); p += sizeof(cookie_t); memcpy(p, iph1->sa->v, iph1->sa->l); p += iph1->sa->l; bp = (sw == GENERATE ? iph1->id_p : iph1->id); memcpy(p, bp->v, bp->l); p += bp->l; plog(LLV_DEBUG, LOCATION, NULL, "HASH_R with:\n"); plogdump(LLV_DEBUG, buf->v, buf->l); /* compute HASH */ res = oakley_prf(hash, buf, iph1); if (res == NULL) goto end; error = 0; plog(LLV_DEBUG, LOCATION, NULL, "HASH_R computed:\n"); plogdump(LLV_DEBUG, res->v, res->l); end: if (buf != NULL) vfree(buf); if (hash) vfree(hash); return res; } /* * compute each authentication method in phase 1. * OUT: * 0: OK * -1: error * other: error to be reply with notification. * the value is notification type. */ int oakley_validate_auth(iph1) struct ph1handle *iph1; { vchar_t *my_hash = NULL; int result; int no_verify_needed = -1; #ifdef HAVE_GSSAPI vchar_t *gsshash = NULL; #endif #ifdef ENABLE_STATS struct timeval start, end; #endif #ifdef ENABLE_STATS gettimeofday(&start, NULL); #endif switch (iph1->approval->authmethod) { case OAKLEY_ATTR_AUTH_METHOD_PSKEY: #ifdef ENABLE_HYBRID case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R: #endif /* validate HASH */ { char *r_hash; if (iph1->id_p == NULL || iph1->pl_hash == NULL) { plog(LLV_ERROR, LOCATION, iph1->remote, "few isakmp message received.\n"); return ISAKMP_NTYPE_PAYLOAD_MALFORMED; } #ifdef ENABLE_HYBRID if (iph1->approval->authmethod == OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I && ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0)) { plog(LLV_ERROR, LOCATION, NULL, "No SIG was passed, " "hybrid auth is enabled, " "but peer is no Xauth compliant\n"); return ISAKMP_NTYPE_SITUATION_NOT_SUPPORTED; break; } #endif r_hash = (caddr_t)(iph1->pl_hash + 1); plog(LLV_DEBUG, LOCATION, NULL, "HASH received:\n"); plogdump(LLV_DEBUG, r_hash, ntohs(iph1->pl_hash->h.len) - sizeof(*iph1->pl_hash)); switch (iph1->etype) { case ISAKMP_ETYPE_IDENT: case ISAKMP_ETYPE_AGG: my_hash = oakley_ph1hash_common(iph1, VALIDATE); break; case ISAKMP_ETYPE_BASE: if (iph1->side == INITIATOR) my_hash = oakley_ph1hash_common(iph1, VALIDATE); else my_hash = oakley_ph1hash_base_i(iph1, VALIDATE); break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid etype %d\n", iph1->etype); return ISAKMP_NTYPE_INVALID_EXCHANGE_TYPE; } if (my_hash == NULL) return ISAKMP_INTERNAL_ERROR; result = memcmp(my_hash->v, r_hash, my_hash->l); vfree(my_hash); if (result) { plog(LLV_ERROR, LOCATION, NULL, "HASH mismatched\n"); return ISAKMP_NTYPE_INVALID_HASH_INFORMATION; } plog(LLV_DEBUG, LOCATION, NULL, "HASH for PSK validated.\n"); } break; #ifdef ENABLE_HYBRID case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R: no_verify_needed = 0; #endif case OAKLEY_ATTR_AUTH_METHOD_DSSSIG: case OAKLEY_ATTR_AUTH_METHOD_RSASIG: { int error = 0; int certtype; /* validation */ if (iph1->id_p == NULL) { plog(LLV_ERROR, LOCATION, iph1->remote, "no ID payload was passed.\n"); return ISAKMP_NTYPE_PAYLOAD_MALFORMED; } if (iph1->sig_p == NULL) { plog(LLV_ERROR, LOCATION, iph1->remote, "no SIG payload was passed.\n"); return ISAKMP_NTYPE_PAYLOAD_MALFORMED; } plog(LLV_DEBUG, LOCATION, NULL, "SIGN passed:\n"); plogdump(LLV_DEBUG, iph1->sig_p->v, iph1->sig_p->l); /* get peer's cert */ certtype = oakley_get_certtype(iph1->rmconf->peerscert); switch (certtype) { case ISAKMP_CERT_NONE: /* expect to receive one from peer */ if (iph1->cert_p == NULL) { plog(LLV_ERROR, LOCATION, NULL, "no peer's CERT payload found.\n"); return ISAKMP_INTERNAL_ERROR; } /* verify the cert if needed */ if (!iph1->rmconf->verify_cert) break; switch (oakley_get_certtype(iph1->cert_p)) { case ISAKMP_CERT_X509SIGN: { char path[MAXPATHLEN]; char *ca; if (iph1->rmconf->cacertfile != NULL) { getpathname(path, sizeof(path), LC_PATHTYPE_CERT, iph1->rmconf->cacertfile); ca = path; } else { ca = NULL; } error = eay_check_x509cert( iph1->cert_p, lcconf->pathinfo[LC_PATHTYPE_CERT], ca, 0); break; } default: plog(LLV_ERROR, LOCATION, NULL, "peers_cert certtype %d was not expected\n", certtype); return ISAKMP_INTERNAL_ERROR; } if (error != 0) { plog(LLV_ERROR, LOCATION, iph1->remote, "the peer's certificate is not verified.\n"); return ISAKMP_NTYPE_INVALID_CERT_AUTHORITY; } break; case ISAKMP_CERT_X509SIGN: if (iph1->rmconf->peerscert == NULL) { plog(LLV_ERROR, LOCATION, NULL, "no peer's CERT file found.\n"); return ISAKMP_INTERNAL_ERROR; } /* don't use received cert */ if (iph1->cert_p != NULL) { vfree(iph1->cert_p); iph1->cert_p = NULL; } /* copy from remoteconf instead */ iph1->cert_p = vdup(iph1->rmconf->peerscert); break; case ISAKMP_CERT_PLAINRSA: if (get_plainrsa_fromlocal(iph1, 0)) return ISAKMP_INTERNAL_ERROR; /* suppress CERT validation warning, unless hybrid mode in use */ if (no_verify_needed == -1) no_verify_needed = 1; break; case ISAKMP_CERT_DNS: /* don't use received cert */ if (iph1->cert_p != NULL) { vfree(iph1->cert_p); iph1->cert_p = NULL; } iph1->cert_p = dnssec_getcert(iph1->id_p); if (iph1->cert_p == NULL) { plog(LLV_ERROR, LOCATION, NULL, "no CERT RR found.\n"); return ISAKMP_INTERNAL_ERROR; } break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid certificate type: %d\n", oakley_get_certtype(iph1->rmconf->peerscert)); return ISAKMP_INTERNAL_ERROR; } /* compare ID payload and certificate name */ if ((error = oakley_check_certid(iph1)) != 0) return error; /* Generate a warning unless verify_cert */ if (iph1->rmconf->verify_cert) { plog(LLV_DEBUG, LOCATION, iph1->remote, "CERT validated\n"); } else if (no_verify_needed != 1) { plog(LLV_WARNING, LOCATION, iph1->remote, "CERT validation disabled by configuration\n"); } /* compute hash */ switch (iph1->etype) { case ISAKMP_ETYPE_IDENT: case ISAKMP_ETYPE_AGG: my_hash = oakley_ph1hash_common(iph1, VALIDATE); break; case ISAKMP_ETYPE_BASE: if (iph1->side == INITIATOR) my_hash = oakley_ph1hash_base_r(iph1, VALIDATE); else my_hash = oakley_ph1hash_base_i(iph1, VALIDATE); break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid etype %d\n", iph1->etype); return ISAKMP_NTYPE_INVALID_EXCHANGE_TYPE; } if (my_hash == NULL) return ISAKMP_INTERNAL_ERROR; /* check signature */ certtype = oakley_get_certtype(iph1->cert_p); if (certtype == ISAKMP_CERT_NONE) certtype = oakley_get_certtype(iph1->rmconf->peerscert); switch (certtype) { case ISAKMP_CERT_X509SIGN: case ISAKMP_CERT_DNS: error = eay_check_x509sign(my_hash, iph1->sig_p, iph1->cert_p); break; case ISAKMP_CERT_PLAINRSA: iph1->rsa_p = rsa_try_check_rsasign(my_hash, iph1->sig_p, iph1->rsa_candidates); error = iph1->rsa_p ? 0 : -1; genlist_free(iph1->rsa_candidates, NULL); iph1->rsa_candidates = NULL; break; default: plog(LLV_ERROR, LOCATION, NULL, "cannot check signature for certtype %d\n", certtype); vfree(my_hash); return ISAKMP_INTERNAL_ERROR; } vfree(my_hash); if (error != 0) { plog(LLV_ERROR, LOCATION, NULL, "Invalid SIG.\n"); return ISAKMP_NTYPE_INVALID_SIGNATURE; } plog(LLV_DEBUG, LOCATION, NULL, "SIG authenticated\n"); } break; #ifdef ENABLE_HYBRID case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R: { if ((iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) == 0) { plog(LLV_ERROR, LOCATION, NULL, "No SIG was passed, " "hybrid auth is enabled, " "but peer is no Xauth compliant\n"); return ISAKMP_NTYPE_SITUATION_NOT_SUPPORTED; break; } plog(LLV_INFO, LOCATION, NULL, "No SIG was passed, " "but hybrid auth is enabled\n"); return 0; break; } #endif #ifdef HAVE_GSSAPI case OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB: /* check if we're not into XAUTH_PSKEY_I instead */ #ifdef ENABLE_HYBRID if (iph1->rmconf->xauth) break; #endif switch (iph1->etype) { case ISAKMP_ETYPE_IDENT: case ISAKMP_ETYPE_AGG: my_hash = oakley_ph1hash_common(iph1, VALIDATE); break; default: plog(LLV_ERROR, LOCATION, NULL, "invalid etype %d\n", iph1->etype); return ISAKMP_NTYPE_INVALID_EXCHANGE_TYPE; } if (my_hash == NULL) { if (gssapi_more_tokens(iph1)) return ISAKMP_NTYPE_INVALID_EXCHANGE_TYPE; else return ISAKMP_NTYPE_INVALID_HASH_INFORMATION; } gsshash = gssapi_unwraphash(iph1); if (gsshash == NULL) { vfree(my_hash); return ISAKMP_NTYPE_INVALID_HASH_INFORMATION; } result = memcmp(my_hash->v, gsshash->v, my_hash->l); vfree(my_hash); vfree(gsshash); if (result) { plog(LLV_ERROR, LOCATION, NULL, "HASH mismatched\n"); return ISAKMP_NTYPE_INVALID_HASH_INFORMATION; } plog(LLV_DEBUG, LOCATION, NULL, "hash compared OK\n"); break; #endif case OAKLEY_ATTR_AUTH_METHOD_RSAENC: case OAKLEY_ATTR_AUTH_METHOD_RSAREV: #ifdef ENABLE_HYBRID 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 if (iph1->id_p == NULL || iph1->pl_hash == NULL) { plog(LLV_ERROR, LOCATION, iph1->remote, "few isakmp message received.\n"); return ISAKMP_NTYPE_PAYLOAD_MALFORMED; } plog(LLV_ERROR, LOCATION, iph1->remote, "not supported authmethod type %s\n", s_oakley_attr_method(iph1->approval->authmethod)); return ISAKMP_INTERNAL_ERROR; default: plog(LLV_ERROR, LOCATION, iph1->remote, "invalid authmethod %d why ?\n", iph1->approval->authmethod); return ISAKMP_INTERNAL_ERROR; } #ifdef ENABLE_STATS gettimeofday(&end, NULL); syslog(LOG_NOTICE, "%s(%s): %8.6f", __func__, s_oakley_attr_method(iph1->approval->authmethod), timedelta(&start, &end)); #endif return 0; } /* get my certificate * NOTE: include certificate type. */ int oakley_getmycert(iph1) struct ph1handle *iph1; { switch (oakley_get_certtype(iph1->rmconf->mycert)) { case ISAKMP_CERT_X509SIGN: if (iph1->cert) return 0; iph1->cert = vdup(iph1->rmconf->mycert); break; case ISAKMP_CERT_PLAINRSA: if (iph1->rsa) return 0; return get_plainrsa_fromlocal(iph1, 1); default: plog(LLV_ERROR, LOCATION, NULL, "Unknown certtype #%d\n", oakley_get_certtype(iph1->rmconf->mycert)); return -1; } return 0; } static int get_plainrsa_fromlocal(iph1, my) struct ph1handle *iph1; int my; { char path[MAXPATHLEN]; vchar_t *cert = NULL; char *certfile; int error = -1; iph1->rsa_candidates = rsa_lookup_keys(iph1, my); if (!iph1->rsa_candidates || rsa_list_count(iph1->rsa_candidates) == 0) { plog(LLV_ERROR, LOCATION, NULL, "%s RSA key not found for %s\n", my ? "Private" : "Public", saddr2str_fromto("%s <-> %s", iph1->local, iph1->remote)); goto end; } if (my && rsa_list_count(iph1->rsa_candidates) > 1) { plog(LLV_WARNING, LOCATION, NULL, "More than one (=%lu) private " "PlainRSA key found for %s\n", rsa_list_count(iph1->rsa_candidates), saddr2str_fromto("%s <-> %s", iph1->local, iph1->remote)); plog(LLV_WARNING, LOCATION, NULL, "This may have unpredictable results, " "i.e. wrong key could be used!\n"); plog(LLV_WARNING, LOCATION, NULL, "Consider using only one single private " "key for all peers...\n"); } if (my) { iph1->rsa = ((struct rsa_key *) genlist_next(iph1->rsa_candidates, NULL))->rsa; genlist_free(iph1->rsa_candidates, NULL); iph1->rsa_candidates = NULL; if (iph1->rsa == NULL) goto end; } error = 0; end: return error; } /* get signature */ int oakley_getsign(iph1) struct ph1handle *iph1; { char path[MAXPATHLEN]; vchar_t *privkey = NULL; int error = -1; switch (oakley_get_certtype(iph1->rmconf->mycert)) { case ISAKMP_CERT_X509SIGN: case ISAKMP_CERT_DNS: if (iph1->rmconf->myprivfile == NULL) { plog(LLV_ERROR, LOCATION, NULL, "no cert defined.\n"); goto end; } /* make private file name */ getpathname(path, sizeof(path), LC_PATHTYPE_CERT, iph1->rmconf->myprivfile); privkey = privsep_eay_get_pkcs1privkey(path); if (privkey == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get private key.\n"); goto end; } plog(LLV_DEBUG2, LOCATION, NULL, "private key:\n"); plogdump(LLV_DEBUG2, privkey->v, privkey->l); iph1->sig = eay_get_x509sign(iph1->hash, privkey); break; case ISAKMP_CERT_PLAINRSA: iph1->sig = eay_get_rsasign(iph1->hash, iph1->rsa); break; default: plog(LLV_ERROR, LOCATION, NULL, "Unknown certtype #%d\n", oakley_get_certtype(iph1->rmconf->mycert)); goto end; } if (iph1->sig == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to sign.\n"); goto end; } plog(LLV_DEBUG, LOCATION, NULL, "SIGN computed:\n"); plogdump(LLV_DEBUG, iph1->sig->v, iph1->sig->l); error = 0; end: if (privkey != NULL) vfree(privkey); return error; } /* * compare certificate name and ID value. */ static int oakley_check_certid(iph1) struct ph1handle *iph1; { struct ipsecdoi_id_b *id_b; vchar_t *name = NULL; char *altname = NULL; int idlen, type; int error; if (iph1->rmconf == NULL || iph1->rmconf->verify_cert == FALSE) return 0; if (iph1->id_p == NULL || iph1->cert_p == NULL) { plog(LLV_ERROR, LOCATION, iph1->remote, "no ID nor CERT found.\n"); return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } id_b = (struct ipsecdoi_id_b *)iph1->id_p->v; idlen = iph1->id_p->l - sizeof(*id_b); switch (id_b->type) { case IPSECDOI_ID_DER_ASN1_DN: name = eay_get_x509asn1subjectname(iph1->cert_p); if (!name) { plog(LLV_ERROR, LOCATION, iph1->remote, "failed to get subjectName\n"); return ISAKMP_NTYPE_INVALID_CERTIFICATE; } if (idlen != name->l) { plog(LLV_ERROR, LOCATION, iph1->remote, "Invalid ID length in phase 1.\n"); vfree(name); return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } error = memcmp(id_b + 1, name->v, idlen); if (error != 0) { plog(LLV_ERROR, LOCATION, iph1->remote, "ID mismatched with ASN1 SubjectName.\n"); plogdump(LLV_DEBUG, id_b + 1, idlen); plogdump(LLV_DEBUG, name->v, idlen); if (iph1->rmconf->verify_identifier) { vfree(name); return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } } vfree(name); return 0; case IPSECDOI_ID_IPV4_ADDR: case IPSECDOI_ID_IPV6_ADDR: { /* * converting to binary from string because openssl return * a string even if object is a binary. * XXX fix it ! access by ASN.1 directly without. */ struct addrinfo hints, *res; caddr_t a = NULL; int pos; for (pos = 1; ; pos++) { if (eay_get_x509subjectaltname(iph1->cert_p, &altname, &type, pos) !=0) { plog(LLV_ERROR, LOCATION, NULL, "failed to get subjectAltName\n"); return ISAKMP_NTYPE_INVALID_CERTIFICATE; } /* it's the end condition of the loop. */ if (!altname) { plog(LLV_ERROR, LOCATION, NULL, "no proper subjectAltName.\n"); return ISAKMP_NTYPE_INVALID_CERTIFICATE; } if (check_typeofcertname(id_b->type, type) == 0) break; /* next name */ racoon_free(altname); altname = NULL; } memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_RAW; hints.ai_flags = AI_NUMERICHOST; error = getaddrinfo(altname, NULL, &hints, &res); racoon_free(altname); altname = NULL; if (error != 0) { plog(LLV_ERROR, LOCATION, NULL, "no proper subjectAltName.\n"); return ISAKMP_NTYPE_INVALID_CERTIFICATE; } switch (res->ai_family) { case AF_INET: a = (caddr_t)&((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr; break; #ifdef INET6 case AF_INET6: a = (caddr_t)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr.s6_addr; break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "family not supported: %d.\n", res->ai_family); freeaddrinfo(res); return ISAKMP_NTYPE_INVALID_CERTIFICATE; } error = memcmp(id_b + 1, a, idlen); freeaddrinfo(res); vfree(name); if (error != 0) { plog(LLV_ERROR, LOCATION, NULL, "ID mismatched with subjectAltName.\n"); plogdump(LLV_DEBUG, id_b + 1, idlen); plogdump(LLV_DEBUG, a, idlen); if (iph1->rmconf->verify_identifier) return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } return 0; } case IPSECDOI_ID_FQDN: case IPSECDOI_ID_USER_FQDN: { int pos; for (pos = 1; ; pos++) { if (eay_get_x509subjectaltname(iph1->cert_p, &altname, &type, pos) != 0){ plog(LLV_ERROR, LOCATION, NULL, "failed to get subjectAltName\n"); return ISAKMP_NTYPE_INVALID_CERTIFICATE; } /* it's the end condition of the loop. */ if (!altname) { plog(LLV_ERROR, LOCATION, NULL, "no proper subjectAltName.\n"); return ISAKMP_NTYPE_INVALID_CERTIFICATE; } if (check_typeofcertname(id_b->type, type) == 0) break; /* next name */ racoon_free(altname); altname = NULL; } if (idlen != strlen(altname)) { plog(LLV_ERROR, LOCATION, NULL, "Invalid ID length in phase 1.\n"); racoon_free(altname); return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } if (check_typeofcertname(id_b->type, type) != 0) { plog(LLV_ERROR, LOCATION, NULL, "ID type mismatched. ID: %s CERT: %s.\n", s_ipsecdoi_ident(id_b->type), s_ipsecdoi_ident(type)); racoon_free(altname); return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } error = memcmp(id_b + 1, altname, idlen); if (error) { plog(LLV_ERROR, LOCATION, NULL, "ID mismatched.\n"); plogdump(LLV_DEBUG, id_b + 1, idlen); plogdump(LLV_DEBUG, altname, idlen); racoon_free(altname); return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } racoon_free(altname); return 0; } default: plog(LLV_ERROR, LOCATION, NULL, "Inpropper ID type passed: %s.\n", s_ipsecdoi_ident(id_b->type)); return ISAKMP_NTYPE_INVALID_ID_INFORMATION; } /*NOTREACHED*/ } static int check_typeofcertname(doi, genid) int doi, genid; { switch (doi) { case IPSECDOI_ID_IPV4_ADDR: case IPSECDOI_ID_IPV4_ADDR_SUBNET: case IPSECDOI_ID_IPV6_ADDR: case IPSECDOI_ID_IPV6_ADDR_SUBNET: case IPSECDOI_ID_IPV4_ADDR_RANGE: case IPSECDOI_ID_IPV6_ADDR_RANGE: if (genid != GENT_IPADD) return -1; return 0; case IPSECDOI_ID_FQDN: if (genid != GENT_DNS) return -1; return 0; case IPSECDOI_ID_USER_FQDN: if (genid != GENT_EMAIL) return -1; return 0; case IPSECDOI_ID_DER_ASN1_DN: /* should not be passed to this function*/ case IPSECDOI_ID_DER_ASN1_GN: case IPSECDOI_ID_KEY_ID: default: return -1; } /*NOTREACHED*/ } /* * save certificate including certificate type. */ int oakley_savecert(iph1, gen) struct ph1handle *iph1; struct isakmp_gen *gen; { vchar_t **c; u_int8_t type; STACK_OF(X509) *certs=NULL; PKCS7 *p7; type = *(u_int8_t *)(gen + 1) & 0xff; switch (type) { case ISAKMP_CERT_DNS: plog(LLV_WARNING, LOCATION, NULL, "CERT payload is unnecessary in DNSSEC. " "ignore this CERT payload.\n"); return 0; case ISAKMP_CERT_PKCS7: case ISAKMP_CERT_PGP: case ISAKMP_CERT_X509SIGN: case ISAKMP_CERT_KERBEROS: case ISAKMP_CERT_SPKI: c = &iph1->cert_p; break; case ISAKMP_CERT_CRL: c = &iph1->crl_p; break; case ISAKMP_CERT_X509KE: case ISAKMP_CERT_X509ATTR: case ISAKMP_CERT_ARL: plog(LLV_ERROR, LOCATION, NULL, "No supported such CERT type %d\n", type); return -1; default: plog(LLV_ERROR, LOCATION, NULL, "Invalid CERT type %d\n", type); return -1; } /* XXX choice the 1th cert, ignore after the cert. */ /* XXX should be processed. */ if (*c) { plog(LLV_WARNING, LOCATION, NULL, "ignore 2nd CERT payload.\n"); return 0; } if (type == ISAKMP_CERT_PKCS7) { u_char *bp; int i; /* Skip the header */ bp = (u_char *)(gen + 1); /* And the first byte is the certificate type, * we know that already */ bp++; p7 = d2i_PKCS7(NULL, (void *)&bp, ntohs(gen->len) - sizeof(*gen) - 1); if (!p7) { plog(LLV_ERROR, LOCATION, NULL, "Failed to parse PKCS#7 CERT.\n"); return -1; } /* Copied this from the openssl pkcs7 application; * there"s little by way of documentation for any of * it. I can only presume it"s correct. */ i = OBJ_obj2nid(p7->type); switch (i) { case NID_pkcs7_signed: certs=p7->d.sign->cert; break; case NID_pkcs7_signedAndEnveloped: certs=p7->d.signed_and_enveloped->cert; break; default: break; } if (!certs) { plog(LLV_ERROR, LOCATION, NULL, "CERT PKCS#7 bundle contains no certs.\n"); PKCS7_free(p7); return -1; } for (i = 0; i < sk_X509_num(certs); i++) { int len; u_char *bp; X509 *cert = sk_X509_value(certs,i); plog(LLV_DEBUG, LOCATION, NULL, "Trying PKCS#7 cert %d.\n", i); /* We'll just try each cert in turn */ *c = dump_x509(cert); if (!*c) { plog(LLV_ERROR, LOCATION, NULL, "Failed to get CERT buffer.\n"); continue; } /* Ignore cert if it doesn't match identity * XXX If verify cert is disabled, we still just take * the first certificate.... */ if (oakley_check_certid(iph1)) { plog(LLV_DEBUG, LOCATION, NULL, "Discarding CERT: does not match ID.\n"); vfree((*c)); *c = NULL; continue; } { char *p = eay_get_x509text(*c); plog(LLV_DEBUG, LOCATION, NULL, "CERT saved:\n"); plogdump(LLV_DEBUG, (*c)->v, (*c)->l); plog(LLV_DEBUG, LOCATION, NULL, "%s", p ? p : "\n"); racoon_free(p); } break; } PKCS7_free(p7); } else { *c = dump_isakmp_payload(gen); if (!*c) { plog(LLV_ERROR, LOCATION, NULL, "Failed to get CERT buffer.\n"); return -1; } switch (type) { case ISAKMP_CERT_PGP: case ISAKMP_CERT_X509SIGN: case ISAKMP_CERT_KERBEROS: case ISAKMP_CERT_SPKI: /* Ignore cert if it doesn't match identity * XXX If verify cert is disabled, we still just take * the first certificate.... */ if (oakley_check_certid(iph1)){ plog(LLV_DEBUG, LOCATION, NULL, "Discarding CERT: does not match ID.\n"); vfree((*c)); *c = NULL; return 0; } { char *p = eay_get_x509text(*c); plog(LLV_DEBUG, LOCATION, NULL, "CERT saved:\n"); plogdump(LLV_DEBUG, (*c)->v, (*c)->l); plog(LLV_DEBUG, LOCATION, NULL, "%s", p ? p : "\n"); racoon_free(p); } break; case ISAKMP_CERT_CRL: plog(LLV_DEBUG, LOCATION, NULL, "CRL saved:\n"); plogdump(LLV_DEBUG, (*c)->v, (*c)->l); break; case ISAKMP_CERT_X509KE: case ISAKMP_CERT_X509ATTR: case ISAKMP_CERT_ARL: default: /* XXX */ vfree(*c); *c = NULL; return 0; } } return 0; } /* * save certificate including certificate type. */ int oakley_savecr(iph1, gen) struct ph1handle *iph1; struct isakmp_gen *gen; { vchar_t *cert; vchar_t **c; u_int8_t type; type = *(u_int8_t *)(gen + 1) & 0xff; switch (type) { case ISAKMP_CERT_DNS: plog(LLV_WARNING, LOCATION, NULL, "CERT payload is unnecessary in DNSSEC\n"); /*FALLTHRU*/ case ISAKMP_CERT_PKCS7: case ISAKMP_CERT_PGP: case ISAKMP_CERT_X509SIGN: case ISAKMP_CERT_KERBEROS: case ISAKMP_CERT_SPKI: c = &iph1->cr_p; break; case ISAKMP_CERT_X509KE: case ISAKMP_CERT_X509ATTR: case ISAKMP_CERT_ARL: plog(LLV_ERROR, LOCATION, NULL, "No supported such CR type %d\n", type); return -1; case ISAKMP_CERT_CRL: default: plog(LLV_ERROR, LOCATION, NULL, "Invalid CR type %d\n", type); return -1; } /* Already found an acceptable CR? */ if (*c != NULL) return 0; cert = dump_isakmp_payload(gen); if (cert == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Failed to get CR buffer.\n"); return -1; } plog(LLV_DEBUG, LOCATION, NULL, "CR received:\n"); plogdump(LLV_DEBUG, cert->v, cert->l); *c = cert; if (resolveph1rmconf(iph1) == 0) { /* Found unique match */ plog(LLV_DEBUG, LOCATION, NULL, "CR saved.\n"); } else { /* Still ambiguous or matches nothing, ignore this CR */ *c = NULL; vfree(cert); } return 0; } /* * Add a single CR. */ struct append_cr_ctx { struct ph1handle *iph1; struct payload_list *plist; }; static int oakley_append_rmconf_cr(rmconf, ctx) struct remoteconf *rmconf; void *ctx; { struct append_cr_ctx *actx = (struct append_cr_ctx *) ctx; vchar_t *buf, *asn1dn = NULL; int type; /* Do we want to send CR about this? */ if (rmconf->send_cr == FALSE) return 0; if (rmconf->peerscert != NULL) { type = oakley_get_certtype(rmconf->peerscert); asn1dn = eay_get_x509asn1issuername(rmconf->peerscert); } else if (rmconf->cacert != NULL) { type = oakley_get_certtype(rmconf->cacert); asn1dn = eay_get_x509asn1subjectname(rmconf->cacert); } else return 0; if (asn1dn == NULL) { plog(LLV_ERROR, LOCATION, actx->iph1->remote, "Failed to get CR ASN1 DN from certificate\n"); return 0; } buf = vmalloc(1 + asn1dn->l); if (buf == NULL) goto err; buf->v[0] = type; memcpy(&buf->v[1], asn1dn->v, asn1dn->l); plog(LLV_DEBUG, LOCATION, actx->iph1->remote, "appending CR: %s\n", s_isakmp_certtype(buf->v[0])); plogdump(LLV_DEBUG, buf->v, buf->l); actx->plist = isakmp_plist_append_full(actx->plist, buf, ISAKMP_NPTYPE_CR, 1); err: vfree(asn1dn); return 0; } /* * Append list of acceptable CRs. * RFC2048 3.10 */ struct payload_list * oakley_append_cr(plist, iph1) struct payload_list *plist; struct ph1handle *iph1; { struct append_cr_ctx ctx; struct rmconfselector sel; ctx.iph1 = iph1; ctx.plist = plist; if (iph1->rmconf == NULL) { rmconf_selector_from_ph1(&sel, iph1); enumrmconf(&sel, oakley_append_rmconf_cr, &ctx); } else { oakley_append_rmconf_cr(iph1->rmconf, &ctx); } return ctx.plist; } /* * check peer's CR. */ int oakley_checkcr(iph1) struct ph1handle *iph1; { int type; if (iph1->cr_p == NULL) return 0; plog(LLV_DEBUG, LOCATION, iph1->remote, "peer transmitted CR: %s\n", s_isakmp_certtype(oakley_get_certtype(iph1->cr_p))); type = oakley_get_certtype(iph1->cr_p); if (type != oakley_get_certtype(iph1->rmconf->mycert)) { plog(LLV_ERROR, LOCATION, iph1->remote, "such a cert type isn't supported: %d\n", type); return -1; } return 0; } /* * check to need CR payload. */ int oakley_needcr(type) int type; { switch (type) { case OAKLEY_ATTR_AUTH_METHOD_DSSSIG: case OAKLEY_ATTR_AUTH_METHOD_RSASIG: #ifdef ENABLE_HYBRID case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R: #endif return 1; default: return 0; } /*NOTREACHED*/ } /* * compute SKEYID * see seciton 5. Exchanges in RFC 2409 * psk: SKEYID = prf(pre-shared-key, Ni_b | Nr_b) * sig: SKEYID = prf(Ni_b | Nr_b, g^ir) * enc: SKEYID = prf(H(Ni_b | Nr_b), CKY-I | CKY-R) */ int oakley_skeyid(iph1) struct ph1handle *iph1; { vchar_t *buf = NULL, *bp; char *p; int len; int error = -1; /* SKEYID */ switch (iph1->approval->authmethod) { case OAKLEY_ATTR_AUTH_METHOD_PSKEY: #ifdef ENABLE_HYBRID case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R: #endif if (iph1->etype != ISAKMP_ETYPE_IDENT) { iph1->authstr = getpskbyname(iph1->id_p); if (iph1->authstr == NULL) { if (iph1->rmconf->verify_identifier) { plog(LLV_ERROR, LOCATION, iph1->remote, "couldn't find the pskey.\n"); goto end; } plog(LLV_NOTIFY, LOCATION, iph1->remote, "couldn't find the proper pskey, " "try to get one by the peer's address.\n"); } } if (iph1->authstr == NULL) { /* * If the exchange type is the main mode or if it's * failed to get the psk by ID, racoon try to get * the psk by remote IP address. * It may be nonsense. */ iph1->authstr = getpskbyaddr(iph1->remote); if (iph1->authstr == NULL) { plog(LLV_ERROR, LOCATION, iph1->remote, "couldn't find the pskey for %s.\n", saddrwop2str(iph1->remote)); goto end; } } plog(LLV_DEBUG, LOCATION, NULL, "the psk found.\n"); /* should be secret PSK */ plog(LLV_DEBUG2, LOCATION, NULL, "psk: "); plogdump(LLV_DEBUG2, iph1->authstr->v, iph1->authstr->l); len = iph1->nonce->l + iph1->nonce_p->l; buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get skeyid buffer\n"); goto end; } p = buf->v; bp = (iph1->side == INITIATOR ? iph1->nonce : iph1->nonce_p); plog(LLV_DEBUG, LOCATION, NULL, "nonce 1: "); plogdump(LLV_DEBUG, bp->v, bp->l); memcpy(p, bp->v, bp->l); p += bp->l; bp = (iph1->side == INITIATOR ? iph1->nonce_p : iph1->nonce); plog(LLV_DEBUG, LOCATION, NULL, "nonce 2: "); plogdump(LLV_DEBUG, bp->v, bp->l); memcpy(p, bp->v, bp->l); p += bp->l; iph1->skeyid = oakley_prf(iph1->authstr, buf, iph1); if (iph1->skeyid == NULL) goto end; break; case OAKLEY_ATTR_AUTH_METHOD_DSSSIG: case OAKLEY_ATTR_AUTH_METHOD_RSASIG: #ifdef ENABLE_HYBRID case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R: case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_I: case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R: #endif #ifdef HAVE_GSSAPI case OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB: #endif len = iph1->nonce->l + iph1->nonce_p->l; buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get nonce buffer\n"); goto end; } p = buf->v; bp = (iph1->side == INITIATOR ? iph1->nonce : iph1->nonce_p); plog(LLV_DEBUG, LOCATION, NULL, "nonce1: "); plogdump(LLV_DEBUG, bp->v, bp->l); memcpy(p, bp->v, bp->l); p += bp->l; bp = (iph1->side == INITIATOR ? iph1->nonce_p : iph1->nonce); plog(LLV_DEBUG, LOCATION, NULL, "nonce2: "); plogdump(LLV_DEBUG, bp->v, bp->l); memcpy(p, bp->v, bp->l); p += bp->l; iph1->skeyid = oakley_prf(buf, iph1->dhgxy, iph1); if (iph1->skeyid == NULL) goto end; break; case OAKLEY_ATTR_AUTH_METHOD_RSAENC: case OAKLEY_ATTR_AUTH_METHOD_RSAREV: #ifdef ENABLE_HYBRID 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 plog(LLV_WARNING, LOCATION, NULL, "not supported authentication method %s\n", s_oakley_attr_method(iph1->approval->authmethod)); goto end; default: plog(LLV_ERROR, LOCATION, NULL, "invalid authentication method %d\n", iph1->approval->authmethod); goto end; } plog(LLV_DEBUG, LOCATION, NULL, "SKEYID computed:\n"); plogdump(LLV_DEBUG, iph1->skeyid->v, iph1->skeyid->l); error = 0; end: if (buf != NULL) vfree(buf); return error; } /* * compute SKEYID_[dae] * see seciton 5. Exchanges in RFC 2409 * SKEYID_d = prf(SKEYID, g^ir | CKY-I | CKY-R | 0) * SKEYID_a = prf(SKEYID, SKEYID_d | g^ir | CKY-I | CKY-R | 1) * SKEYID_e = prf(SKEYID, SKEYID_a | g^ir | CKY-I | CKY-R | 2) */ int oakley_skeyid_dae(iph1) struct ph1handle *iph1; { vchar_t *buf = NULL; char *p; int len; int error = -1; if (iph1->skeyid == NULL) { plog(LLV_ERROR, LOCATION, NULL, "no SKEYID found.\n"); goto end; } /* SKEYID D */ /* SKEYID_d = prf(SKEYID, g^xy | CKY-I | CKY-R | 0) */ len = iph1->dhgxy->l + sizeof(cookie_t) * 2 + 1; buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get skeyid buffer\n"); goto end; } p = buf->v; memcpy(p, iph1->dhgxy->v, iph1->dhgxy->l); p += iph1->dhgxy->l; memcpy(p, (caddr_t)&iph1->index.i_ck, sizeof(cookie_t)); p += sizeof(cookie_t); memcpy(p, (caddr_t)&iph1->index.r_ck, sizeof(cookie_t)); p += sizeof(cookie_t); *p = 0; iph1->skeyid_d = oakley_prf(iph1->skeyid, buf, iph1); if (iph1->skeyid_d == NULL) goto end; vfree(buf); buf = NULL; plog(LLV_DEBUG, LOCATION, NULL, "SKEYID_d computed:\n"); plogdump(LLV_DEBUG, iph1->skeyid_d->v, iph1->skeyid_d->l); /* SKEYID A */ /* SKEYID_a = prf(SKEYID, SKEYID_d | g^xy | CKY-I | CKY-R | 1) */ len = iph1->skeyid_d->l + iph1->dhgxy->l + sizeof(cookie_t) * 2 + 1; buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get skeyid buffer\n"); goto end; } p = buf->v; memcpy(p, iph1->skeyid_d->v, iph1->skeyid_d->l); p += iph1->skeyid_d->l; memcpy(p, iph1->dhgxy->v, iph1->dhgxy->l); p += iph1->dhgxy->l; memcpy(p, (caddr_t)&iph1->index.i_ck, sizeof(cookie_t)); p += sizeof(cookie_t); memcpy(p, (caddr_t)&iph1->index.r_ck, sizeof(cookie_t)); p += sizeof(cookie_t); *p = 1; iph1->skeyid_a = oakley_prf(iph1->skeyid, buf, iph1); if (iph1->skeyid_a == NULL) goto end; vfree(buf); buf = NULL; plog(LLV_DEBUG, LOCATION, NULL, "SKEYID_a computed:\n"); plogdump(LLV_DEBUG, iph1->skeyid_a->v, iph1->skeyid_a->l); /* SKEYID E */ /* SKEYID_e = prf(SKEYID, SKEYID_a | g^xy | CKY-I | CKY-R | 2) */ len = iph1->skeyid_a->l + iph1->dhgxy->l + sizeof(cookie_t) * 2 + 1; buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get skeyid buffer\n"); goto end; } p = buf->v; memcpy(p, iph1->skeyid_a->v, iph1->skeyid_a->l); p += iph1->skeyid_a->l; memcpy(p, iph1->dhgxy->v, iph1->dhgxy->l); p += iph1->dhgxy->l; memcpy(p, (caddr_t)&iph1->index.i_ck, sizeof(cookie_t)); p += sizeof(cookie_t); memcpy(p, (caddr_t)&iph1->index.r_ck, sizeof(cookie_t)); p += sizeof(cookie_t); *p = 2; iph1->skeyid_e = oakley_prf(iph1->skeyid, buf, iph1); if (iph1->skeyid_e == NULL) goto end; vfree(buf); buf = NULL; plog(LLV_DEBUG, LOCATION, NULL, "SKEYID_e computed:\n"); plogdump(LLV_DEBUG, iph1->skeyid_e->v, iph1->skeyid_e->l); error = 0; end: if (buf != NULL) vfree(buf); return error; } /* * compute final encryption key. * see Appendix B. */ int oakley_compute_enckey(iph1) struct ph1handle *iph1; { u_int keylen, prflen; int error = -1; /* RFC2409 p39 */ keylen = alg_oakley_encdef_keylen(iph1->approval->enctype, iph1->approval->encklen); if (keylen == -1) { plog(LLV_ERROR, LOCATION, NULL, "invalid encryption algorithm %d, " "or invalid key length %d.\n", iph1->approval->enctype, iph1->approval->encklen); goto end; } iph1->key = vmalloc(keylen >> 3); if (iph1->key == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get key buffer\n"); goto end; } /* set prf length */ prflen = alg_oakley_hashdef_hashlen(iph1->approval->hashtype); if (prflen == -1) { plog(LLV_ERROR, LOCATION, NULL, "invalid hash type %d.\n", iph1->approval->hashtype); goto end; } /* see isakmp-oakley-08 5.3. */ if (iph1->key->l <= iph1->skeyid_e->l) { /* * if length(Ka) <= length(SKEYID_e) * Ka = first length(K) bit of SKEYID_e */ memcpy(iph1->key->v, iph1->skeyid_e->v, iph1->key->l); } else { vchar_t *buf = NULL, *res = NULL; u_char *p, *ep; int cplen; int subkey; /* * otherwise, * Ka = K1 | K2 | K3 * where * K1 = prf(SKEYID_e, 0) * K2 = prf(SKEYID_e, K1) * K3 = prf(SKEYID_e, K2) */ plog(LLV_DEBUG, LOCATION, NULL, "len(SKEYID_e) < len(Ka) (%zu < %zu), " "generating long key (Ka = K1 | K2 | ...)\n", iph1->skeyid_e->l, iph1->key->l); if ((buf = vmalloc(prflen >> 3)) == 0) { plog(LLV_ERROR, LOCATION, NULL, "failed to get key buffer\n"); goto end; } p = (u_char *)iph1->key->v; ep = p + iph1->key->l; subkey = 1; while (p < ep) { if (p == (u_char *)iph1->key->v) { /* just for computing K1 */ buf->v[0] = 0; buf->l = 1; } res = oakley_prf(iph1->skeyid_e, buf, iph1); if (res == NULL) { vfree(buf); goto end; } plog(LLV_DEBUG, LOCATION, NULL, "compute intermediate encryption key K%d\n", subkey); plogdump(LLV_DEBUG, buf->v, buf->l); plogdump(LLV_DEBUG, res->v, res->l); cplen = (res->l < ep - p) ? res->l : ep - p; memcpy(p, res->v, cplen); p += cplen; buf->l = prflen >> 3; /* to cancel K1 speciality */ if (res->l != buf->l) { plog(LLV_ERROR, LOCATION, NULL, "internal error: res->l=%zu buf->l=%zu\n", res->l, buf->l); vfree(res); vfree(buf); goto end; } memcpy(buf->v, res->v, res->l); vfree(res); subkey++; } vfree(buf); } /* * don't check any weak key or not. * draft-ietf-ipsec-ike-01.txt Appendix B. * draft-ietf-ipsec-ciph-aes-cbc-00.txt Section 2.3. */ #if 0 /* weakkey check */ if (iph1->approval->enctype > ARRAYLEN(oakley_encdef) || oakley_encdef[iph1->approval->enctype].weakkey == NULL) { plog(LLV_ERROR, LOCATION, NULL, "encryption algorithm %d isn't supported.\n", iph1->approval->enctype); goto end; } if ((oakley_encdef[iph1->approval->enctype].weakkey)(iph1->key)) { plog(LLV_ERROR, LOCATION, NULL, "weakkey was generated.\n"); goto end; } #endif plog(LLV_DEBUG, LOCATION, NULL, "final encryption key computed:\n"); plogdump(LLV_DEBUG, iph1->key->v, iph1->key->l); error = 0; end: return error; } /* * compute IV and set to ph1handle * IV = hash(g^xi | g^xr) * see 4.1 Phase 1 state in draft-ietf-ipsec-ike. */ int oakley_newiv(iph1) struct ph1handle *iph1; { struct isakmp_ivm *newivm = NULL; vchar_t *buf = NULL, *bp; char *p; int len; /* create buffer */ len = iph1->dhpub->l + iph1->dhpub_p->l; buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get iv buffer\n"); return -1; } p = buf->v; bp = (iph1->side == INITIATOR ? iph1->dhpub : iph1->dhpub_p); memcpy(p, bp->v, bp->l); p += bp->l; bp = (iph1->side == INITIATOR ? iph1->dhpub_p : iph1->dhpub); memcpy(p, bp->v, bp->l); p += bp->l; /* allocate IVm */ newivm = racoon_calloc(1, sizeof(struct isakmp_ivm)); if (newivm == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get iv buffer\n"); vfree(buf); return -1; } /* compute IV */ newivm->iv = oakley_hash(buf, iph1); if (newivm->iv == NULL) { vfree(buf); oakley_delivm(newivm); return -1; } /* adjust length of iv */ newivm->iv->l = alg_oakley_encdef_blocklen(iph1->approval->enctype); if (newivm->iv->l == -1) { plog(LLV_ERROR, LOCATION, NULL, "invalid encryption algorithm %d.\n", iph1->approval->enctype); vfree(buf); oakley_delivm(newivm); return -1; } /* create buffer to save iv */ if ((newivm->ive = vdup(newivm->iv)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "vdup (%s)\n", strerror(errno)); vfree(buf); oakley_delivm(newivm); return -1; } vfree(buf); plog(LLV_DEBUG, LOCATION, NULL, "IV computed:\n"); plogdump(LLV_DEBUG, newivm->iv->v, newivm->iv->l); iph1->ivm = newivm; return 0; } /* * compute IV for the payload after phase 1. * It's not limited for phase 2. * if pahse 1 was encrypted. * IV = hash(last CBC block of Phase 1 | M-ID) * if phase 1 was not encrypted. * IV = hash(phase 1 IV | M-ID) * see 4.2 Phase 2 state in draft-ietf-ipsec-ike. */ struct isakmp_ivm * oakley_newiv2(iph1, msgid) struct ph1handle *iph1; u_int32_t msgid; { struct isakmp_ivm *newivm = NULL; vchar_t *buf = NULL; char *p; int len; int error = -1; /* create buffer */ len = iph1->ivm->iv->l + sizeof(msgid_t); buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get iv buffer\n"); goto end; } p = buf->v; memcpy(p, iph1->ivm->iv->v, iph1->ivm->iv->l); p += iph1->ivm->iv->l; memcpy(p, &msgid, sizeof(msgid)); plog(LLV_DEBUG, LOCATION, NULL, "compute IV for phase2\n"); plog(LLV_DEBUG, LOCATION, NULL, "phase1 last IV:\n"); plogdump(LLV_DEBUG, buf->v, buf->l); /* allocate IVm */ newivm = racoon_calloc(1, sizeof(struct isakmp_ivm)); if (newivm == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get iv buffer\n"); goto end; } /* compute IV */ if ((newivm->iv = oakley_hash(buf, iph1)) == NULL) goto end; /* adjust length of iv */ newivm->iv->l = alg_oakley_encdef_blocklen(iph1->approval->enctype); if (newivm->iv->l == -1) { plog(LLV_ERROR, LOCATION, NULL, "invalid encryption algorithm %d.\n", iph1->approval->enctype); goto end; } /* create buffer to save new iv */ if ((newivm->ive = vdup(newivm->iv)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "vdup (%s)\n", strerror(errno)); goto end; } error = 0; plog(LLV_DEBUG, LOCATION, NULL, "phase2 IV computed:\n"); plogdump(LLV_DEBUG, newivm->iv->v, newivm->iv->l); end: if (error && newivm != NULL){ oakley_delivm(newivm); newivm=NULL; } if (buf != NULL) vfree(buf); return newivm; } void oakley_delivm(ivm) struct isakmp_ivm *ivm; { if (ivm == NULL) return; if (ivm->iv != NULL) vfree(ivm->iv); if (ivm->ive != NULL) vfree(ivm->ive); racoon_free(ivm); plog(LLV_DEBUG, LOCATION, NULL, "IV freed\n"); return; } /* * decrypt packet. * save new iv and old iv. */ vchar_t * oakley_do_decrypt(iph1, msg, ivdp, ivep) struct ph1handle *iph1; vchar_t *msg, *ivdp, *ivep; { vchar_t *buf = NULL, *new = NULL; char *pl; int len; u_int8_t padlen; int blen; int error = -1; plog(LLV_DEBUG, LOCATION, NULL, "begin decryption.\n"); blen = alg_oakley_encdef_blocklen(iph1->approval->enctype); if (blen == -1) { plog(LLV_ERROR, LOCATION, NULL, "invalid encryption algorithm %d.\n", iph1->approval->enctype); goto end; } /* save IV for next, but not sync. */ memset(ivep->v, 0, ivep->l); memcpy(ivep->v, (caddr_t)&msg->v[msg->l - blen], blen); plog(LLV_DEBUG, LOCATION, NULL, "IV was saved for next processing:\n"); plogdump(LLV_DEBUG, ivep->v, ivep->l); pl = msg->v + sizeof(struct isakmp); len = msg->l - sizeof(struct isakmp); /* create buffer */ buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get buffer to decrypt.\n"); goto end; } memcpy(buf->v, pl, len); /* do decrypt */ new = alg_oakley_encdef_decrypt(iph1->approval->enctype, buf, iph1->key, ivdp); if (new == NULL || new->v == NULL || new->l == 0) { plog(LLV_ERROR, LOCATION, NULL, "decryption %d failed.\n", iph1->approval->enctype); goto end; } plog(LLV_DEBUG, LOCATION, NULL, "with key:\n"); plogdump(LLV_DEBUG, iph1->key->v, iph1->key->l); vfree(buf); buf = NULL; plog(LLV_DEBUG, LOCATION, NULL, "decrypted payload by IV:\n"); plogdump(LLV_DEBUG, ivdp->v, ivdp->l); plog(LLV_DEBUG, LOCATION, NULL, "decrypted payload, but not trimed.\n"); plogdump(LLV_DEBUG, new->v, new->l); /* get padding length */ if (lcconf->pad_excltail) padlen = new->v[new->l - 1] + 1; else padlen = new->v[new->l - 1]; plog(LLV_DEBUG, LOCATION, NULL, "padding len=%u\n", padlen); /* trim padding */ if (lcconf->pad_strict) { if (padlen > new->l) { plog(LLV_ERROR, LOCATION, NULL, "invalied padding len=%u, buflen=%zu.\n", padlen, new->l); plogdump(LLV_ERROR, new->v, new->l); goto end; } new->l -= padlen; plog(LLV_DEBUG, LOCATION, NULL, "trimmed padding\n"); } else { plog(LLV_DEBUG, LOCATION, NULL, "skip to trim padding.\n"); } /* create new buffer */ len = sizeof(struct isakmp) + new->l; buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get buffer to decrypt.\n"); goto end; } memcpy(buf->v, msg->v, sizeof(struct isakmp)); memcpy(buf->v + sizeof(struct isakmp), new->v, new->l); ((struct isakmp *)buf->v)->len = htonl(buf->l); plog(LLV_DEBUG, LOCATION, NULL, "decrypted.\n"); plogdump(LLV_DEBUG, buf->v, buf->l); #ifdef HAVE_PRINT_ISAKMP_C isakmp_printpacket(buf, iph1->remote, iph1->local, 1); #endif error = 0; end: if (error && buf != NULL) { vfree(buf); buf = NULL; } if (new != NULL) vfree(new); return buf; } /* * encrypt packet. */ vchar_t * oakley_do_encrypt(iph1, msg, ivep, ivp) struct ph1handle *iph1; vchar_t *msg, *ivep, *ivp; { vchar_t *buf = 0, *new = 0; char *pl; int len; u_int padlen; int blen; int error = -1; plog(LLV_DEBUG, LOCATION, NULL, "begin encryption.\n"); /* set cbc block length */ blen = alg_oakley_encdef_blocklen(iph1->approval->enctype); if (blen == -1) { plog(LLV_ERROR, LOCATION, NULL, "invalid encryption algorithm %d.\n", iph1->approval->enctype); goto end; } pl = msg->v + sizeof(struct isakmp); len = msg->l - sizeof(struct isakmp); /* add padding */ padlen = oakley_padlen(len, blen); plog(LLV_DEBUG, LOCATION, NULL, "pad length = %u\n", padlen); /* create buffer */ buf = vmalloc(len + padlen); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get buffer to encrypt.\n"); goto end; } if (padlen) { int i; char *p = &buf->v[len]; if (lcconf->pad_random) { for (i = 0; i < padlen; i++) *p++ = eay_random() & 0xff; } } memcpy(buf->v, pl, len); /* make pad into tail */ if (lcconf->pad_excltail) buf->v[len + padlen - 1] = padlen - 1; else buf->v[len + padlen - 1] = padlen; plogdump(LLV_DEBUG, buf->v, buf->l); /* do encrypt */ new = alg_oakley_encdef_encrypt(iph1->approval->enctype, buf, iph1->key, ivep); if (new == NULL) { plog(LLV_ERROR, LOCATION, NULL, "encryption %d failed.\n", iph1->approval->enctype); goto end; } plog(LLV_DEBUG, LOCATION, NULL, "with key:\n"); plogdump(LLV_DEBUG, iph1->key->v, iph1->key->l); vfree(buf); buf = NULL; plog(LLV_DEBUG, LOCATION, NULL, "encrypted payload by IV:\n"); plogdump(LLV_DEBUG, ivep->v, ivep->l); /* save IV for next */ memset(ivp->v, 0, ivp->l); memcpy(ivp->v, (caddr_t)&new->v[new->l - blen], blen); plog(LLV_DEBUG, LOCATION, NULL, "save IV for next:\n"); plogdump(LLV_DEBUG, ivp->v, ivp->l); /* create new buffer */ len = sizeof(struct isakmp) + new->l; buf = vmalloc(len); if (buf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to get buffer to encrypt.\n"); goto end; } memcpy(buf->v, msg->v, sizeof(struct isakmp)); memcpy(buf->v + sizeof(struct isakmp), new->v, new->l); ((struct isakmp *)buf->v)->len = htonl(buf->l); error = 0; plog(LLV_DEBUG, LOCATION, NULL, "encrypted.\n"); end: if (error && buf != NULL) { vfree(buf); buf = NULL; } if (new != NULL) vfree(new); return buf; } /* culculate padding length */ static int oakley_padlen(len, base) int len, base; { int padlen; padlen = base - len % base; if (lcconf->pad_randomlen) padlen += ((eay_random() % (lcconf->pad_maxsize + 1) + 1) * base); return padlen; }