diff options
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/ip_ah.c | 1356 | ||||
-rw-r--r-- | sys/netinet/ip_ah.h | 23 | ||||
-rw-r--r-- | sys/netinet/ip_esp.c | 975 | ||||
-rw-r--r-- | sys/netinet/ip_esp.h | 15 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.c | 432 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.h | 117 | ||||
-rw-r--r-- | sys/netinet/ip_output.c | 130 | ||||
-rw-r--r-- | sys/netinet/ip_xform.c | 341 | ||||
-rw-r--r-- | sys/netinet/ipsec_input.c | 319 |
9 files changed, 1635 insertions, 2073 deletions
diff --git a/sys/netinet/ip_ah.c b/sys/netinet/ip_ah.c index b4a412b7a07..ed7b57df6f7 100644 --- a/sys/netinet/ip_ah.c +++ b/sys/netinet/ip_ah.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ah.c,v 1.34 2000/02/07 06:09:09 itojun Exp $ */ +/* $OpenBSD: ip_ah.c,v 1.35 2000/03/17 10:25:22 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -68,6 +68,12 @@ #include <net/pfkeyv2.h> #include <net/if_enc.h> +#include <sys/md5k.h> +#include <crypto/sha1.h> +#include <crypto/rmd160.h> +#include <crypto/crypto.h> +#include <crypto/xform.h> + #include "bpfilter.h" #ifdef ENCDEBUG @@ -80,20 +86,6 @@ #define offsetof(s, e) ((int)&((s *)0)->e) #endif -extern struct auth_hash auth_hash_hmac_md5_96; -extern struct auth_hash auth_hash_hmac_sha1_96; -extern struct auth_hash auth_hash_hmac_ripemd_160_96; -extern struct auth_hash auth_hash_key_md5; -extern struct auth_hash auth_hash_key_sha1; - -struct auth_hash *ah_hash[] = { - &auth_hash_hmac_md5_96, - &auth_hash_hmac_sha1_96, - &auth_hash_hmac_ripemd_160_96, - &auth_hash_key_md5, - &auth_hash_key_sha1, -}; - /* * ah_attach() is called from the transformation initialization code */ @@ -110,19 +102,35 @@ int ah_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii) { struct auth_hash *thash = NULL; - int i; + struct cryptoini cria; - for (i = sizeof(ah_hash) / sizeof(ah_hash[0]) - 1; i >= 0; i--) - if (ii->ii_authalg == ah_hash[i]->type) - break; - - if (i < 0) + /* Authentication operation */ + switch (ii->ii_authalg) { - DPRINTF(("ah_init(): unsupported authentication algorithm %d specified\n", ii->ii_authalg)); - return EINVAL; - } + case SADB_AALG_MD5HMAC96: + thash = &auth_hash_hmac_md5_96; + break; + + case SADB_AALG_SHA1HMAC96: + thash = &auth_hash_hmac_sha1_96; + break; - thash = ah_hash[i]; + case SADB_X_AALG_RIPEMD160HMAC96: + thash = &auth_hash_hmac_ripemd_160_96; + break; + + case SADB_X_AALG_MD5: + thash = &auth_hash_key_md5; + break; + + case SADB_X_AALG_SHA1: + thash = &auth_hash_key_sha1; + break; + + default: + DPRINTF(("ah_init(): unsupported authentication algorithm %d specified\n", ii->ii_authalg)); + return EINVAL; + } if ((ii->ii_authkeylen != thash->keysize) && (thash->keysize != 0)) { @@ -144,48 +152,23 @@ ah_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii) bcopy(ii->ii_authkey, tdbp->tdb_amxkey, tdbp->tdb_amxkeylen); - /* "Old" AH */ - if ((thash->type == SADB_X_AALG_MD5) || (thash->type == SADB_X_AALG_SHA1)) - { - MALLOC(tdbp->tdb_ictx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK); - bzero(tdbp->tdb_ictx, thash->ctxsize); - thash->Init(tdbp->tdb_ictx); - thash->Update(tdbp->tdb_ictx, tdbp->tdb_amxkey, tdbp->tdb_amxkeylen); - thash->Final(NULL, tdbp->tdb_ictx); - } - else /* HMAC */ - { - /* Precompute the I and O pads of the HMAC */ - for (i = 0; i < ii->ii_authkeylen; i++) - ii->ii_authkey[i] ^= HMAC_IPAD_VAL; - - MALLOC(tdbp->tdb_ictx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK); - bzero(tdbp->tdb_ictx, thash->ctxsize); - thash->Init(tdbp->tdb_ictx); - thash->Update(tdbp->tdb_ictx, ii->ii_authkey, ii->ii_authkeylen); - thash->Update(tdbp->tdb_ictx, hmac_ipad_buffer, - HMAC_BLOCK_LEN - ii->ii_authkeylen); - - for (i = 0; i < ii->ii_authkeylen; i++) - ii->ii_authkey[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL); - - MALLOC(tdbp->tdb_octx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK); - bzero(tdbp->tdb_octx, thash->ctxsize); - thash->Init(tdbp->tdb_octx); - thash->Update(tdbp->tdb_octx, ii->ii_authkey, ii->ii_authkeylen); - thash->Update(tdbp->tdb_octx, hmac_opad_buffer, - HMAC_BLOCK_LEN - ii->ii_authkeylen); - } - - bzero(ipseczeroes, IPSEC_ZEROES_SIZE); /* paranoid */ + /* Initialize crypto session */ + bzero(&cria, sizeof(cria)); + cria.cri_alg = tdbp->tdb_authalgxform->type; + cria.cri_klen = ii->ii_authkeylen * 8; + cria.cri_key = ii->ii_authkey; - return 0; + return crypto_newsession(&tdbp->tdb_cryptoid, &cria); } -/* Free memory */ +/* + * Paranoia. + */ int ah_zeroize(struct tdb *tdbp) { + int err; + if (tdbp->tdb_amxkey) { bzero(tdbp->tdb_amxkey, tdbp->tdb_amxkeylen); @@ -193,130 +176,32 @@ ah_zeroize(struct tdb *tdbp) tdbp->tdb_amxkey = NULL; } - if (tdbp->tdb_ictx) - { - if (tdbp->tdb_authalgxform) - bzero(tdbp->tdb_ictx, tdbp->tdb_authalgxform->ctxsize); - - FREE(tdbp->tdb_ictx, M_XDATA); - tdbp->tdb_ictx = NULL; - } - - if (tdbp->tdb_octx) - { - if (tdbp->tdb_authalgxform) - bzero(tdbp->tdb_octx, tdbp->tdb_authalgxform->ctxsize); - - FREE(tdbp->tdb_octx, M_XDATA); - tdbp->tdb_octx = NULL; - } - - return 0; + err = crypto_freesession(tdbp->tdb_cryptoid); + tdbp->tdb_cryptoid = 0; + return err; } /* - * ah_input() gets called to verify that an input packet - * passes authentication + * Massage IPv4/IPv6 headers for AH processing. */ - -struct mbuf * -ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) +int +ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out) { - struct auth_hash *ahx = (struct auth_hash *) tdb->tdb_authalgxform; - unsigned char calcauth[AH_MAX_HASHLEN], savauth[AH_MAX_HASHLEN]; - int len, count, off, roff, rplen; - struct mbuf *m0, *m1; + struct mbuf *m = *m0; unsigned char *ptr; - union authctx ctx; - struct ah ah; - + int off, count; + #ifdef INET - struct ip ipo; + struct ip *ip; #endif /* INET */ #ifdef INET6 struct ip6_ext *ip6e; struct ip6_hdr ip6; - int last; + int alloc, len, ad; #endif /* INET6 */ - if (!(tdb->tdb_flags & TDBF_NOREPLAY)) - rplen = sizeof(u_int32_t); - else - rplen = 0; - - /* Save the AH header, we use it throughout */ - m_copydata(m, skip, AH_FLENGTH + rplen, (unsigned char *) &ah); - - /* Save the Authenticator too */ - m_copydata(m, skip + AH_FLENGTH + rplen, ahx->authsize, savauth); - - /* Replay window checking, if applicable */ - if ((tdb->tdb_wnd > 0) && (!(tdb->tdb_flags & TDBF_NOREPLAY))) - { - switch (checkreplaywindow32(ntohl(ah.ah_rpl), 0, &(tdb->tdb_rpl), - tdb->tdb_wnd, &(tdb->tdb_bitmap))) - { - case 0: /* All's well */ - break; - - case 1: - DPRINTF(("ah_input(): replay counter wrapped for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_wrap++; - m_freem(m); - return NULL; - - case 2: - case 3: - DPRINTF(("ah_input(): duplicate packet received in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_replay++; - m_freem(m); - return NULL; - - default: - DPRINTF(("ah_input(): bogus value from checkreplaywindow32() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_replay++; - m_freem(m); - return NULL; - } - } - - /* Verify AH header length */ - if (ah.ah_hl * sizeof(u_int32_t) != ahx->authsize + rplen) - { - DPRINTF(("ah_input(): bad authenticator length %d for packet in SA %s/%08x\n", ah.ah_hl * sizeof(u_int32_t), ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_badauthl++; - m_freem(m); - return NULL; - } - - /* Update the counters */ - tdb->tdb_cur_bytes += (m->m_pkthdr.len - skip - - ah.ah_hl * sizeof(u_int32_t)); - ahstat.ahs_ibytes += (m->m_pkthdr.len - skip - - ah.ah_hl * sizeof(u_int32_t)); - - /* Hard expiration */ - if ((tdb->tdb_flags & TDBF_BYTES) && - (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) - { - pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); - tdb_delete(tdb, 0, TDBEXP_TIMEOUT); - m_freem(m); - return NULL; - } - - /* Notify on expiration */ - if ((tdb->tdb_flags & TDBF_SOFT_BYTES) && - (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) - { - pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); - tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */ - } - - bcopy(tdb->tdb_ictx, &ctx, ahx->ctxsize); - - switch (tdb->tdb_dst.sa.sa_family) + switch (proto) { #ifdef INET case AF_INET: @@ -325,35 +210,37 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) * and option processing -- just make sure they're in * contiguous memory. */ - m = m_pullup(m, skip); + *m0 = m = m_pullup(m, skip); if (m == NULL) { - DPRINTF(("ah_input(): m_pullup() failed, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + DPRINTF(("ah_massage_headers(): m_pullup() failed\n")); ahstat.ahs_hdrops++; - return NULL; + return ENOBUFS; } - ptr = mtod(m, unsigned char *) + sizeof(struct ip); + /* Fix the IP header */ + ip = mtod(m, struct ip *); + ip->ip_tos = 0; + ip->ip_ttl = 0; + ip->ip_sum = 0; - bcopy(mtod(m, unsigned char *), (unsigned char *) &ipo, - sizeof(struct ip)); - - ipo.ip_tos = 0; - ipo.ip_len += skip; /* adjusted in ip_intr() */ - HTONS(ipo.ip_len); - HTONS(ipo.ip_id); + /* + * On input, fix ip_len and ip_id, which have been byte-swapped + * at ip_intr() + */ + if (!out) + { + ip->ip_len += skip; + HTONS(ip->ip_len); + HTONS(ip->ip_id); + } - if ((ahx->type == SADB_X_AALG_MD5) || - (ahx->type == SADB_X_AALG_SHA1)) - ipo.ip_off = htons(ipo.ip_off & IP_DF); + if ((alg == CRYPTO_MD5_KPDK) || (alg == CRYPTO_SHA1_KPDK)) + ip->ip_off = htons(ip->ip_off & IP_DF); else - ipo.ip_off = 0; - ipo.ip_ttl = 0; - ipo.ip_sum = 0; + ip->ip_off = 0; - /* Include IP header in authenticator computation */ - ahx->Update(&ctx, (unsigned char *) &ipo, sizeof(struct ip)); + ptr = mtod(m, unsigned char *) + sizeof(struct ip); /* IPv4 option processing */ for (off = sizeof(struct ip); off < skip;) @@ -361,12 +248,10 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) switch (ptr[off]) { case IPOPT_EOL: - ahx->Update(&ctx, ptr + off, 1); off = skip; /* End the loop */ break; case IPOPT_NOP: - ahx->Update(&ctx, ptr + off, 1); off++; break; @@ -375,32 +260,57 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) case 0x86: /* Commercial security */ case 0x94: /* Router alert */ case 0x95: /* RFC1770 */ - ahx->Update(&ctx, ptr + off, ptr[off + 1]); /* Sanity check for zero-length options */ if (ptr[off + 1] == 0) { - DPRINTF(("ah_input(): illegal zero-length IPv4 option %d in SA %s/%08x\n", ptr[off], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + DPRINTF(("ah_massage_headers(): illegal zero-length IPv4 option %d\n", ptr[off])); ahstat.ahs_hdrops++; m_freem(m); - return NULL; + return EINVAL; } off += ptr[off + 1]; break; + case IPOPT_LSRR: + case IPOPT_SSRR: + /* + * On output, if we have either of the source routing + * options, we should swap the destination address of + * the IP header with the last address specified in + * the option, as that is what the destination's + * IP header will look like. + */ + if (out) + bcopy(ptr + off + ptr[off + 1] - + sizeof(struct in_addr), + &(ip->ip_dst), sizeof(struct in_addr)); + + /* Fall through */ default: - ahx->Update(&ctx, ipseczeroes, ptr[off + 1]); - off += ptr[off + 1]; + /* Sanity check for zero-length options */ + if (ptr[off + 1] == 0) + { + DPRINTF(("ah_massage_headers(): illegal zero-length IPv4 option %d\n", ptr[off])); + ahstat.ahs_hdrops++; + m_freem(m); + return EINVAL; + } + + /* Zeroize all other options */ + count = ptr[off + 1]; + bcopy(ipseczeroes, ptr, count); + off += count; break; } /* Sanity check */ if (off > skip) { - DPRINTF(("ah_input(): malformed IPv4 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + DPRINTF(("ah_massage_headers(): malformed IPv4 options header\n")); ahstat.ahs_hdrops++; m_freem(m); - return NULL; + return EINVAL; } } @@ -409,16 +319,16 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) #ifdef INET6 case AF_INET6: /* Ugly... */ - /* Copy and "cook" (later on) the IPv6 header */ - m_copydata(m, 0, sizeof(ip6), (unsigned char *) &ip6); + /* Copy and "cook" the IPv6 header */ + m_copydata(m, 0, sizeof(ip6), (caddr_t) &ip6); /* We don't do IPv6 Jumbograms */ if (ip6.ip6_plen == 0) { - DPRINTF(("ah_input(): unsupported IPv6 jumbogram in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + DPRINTF(("ah_massage_headers(): unsupported IPv6 jumbogram")); ahstat.ahs_hdrops++; m_freem(m); - return NULL; + return EMSGSIZE; } ip6.ip6_flow = 0; @@ -426,23 +336,35 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) ip6.ip6_vfc &= ~IPV6_VERSION_MASK; ip6.ip6_vfc |= IPV6_VERSION; - /* Include IPv6 header in authenticator computation */ - ahx->Update(&ctx, (unsigned char *) &ip6, sizeof(ip6)); - + /* Done with IPv6 header */ + m_copyback(m, 0, sizeof(struct ip6_hdr), (caddr_t) &ip6); + /* Let's deal with the remaining headers (if any) */ if (skip - sizeof(struct ip6_hdr) > 0) { if (m->m_len <= skip) { MALLOC(ptr, unsigned char *, skip - sizeof(struct ip6_hdr), - M_XDATA, M_WAITOK); + M_XDATA, M_DONTWAIT); + if (ptr == NULL) + { + DPRINTF(("ah_massage_headers(): failed to allocate memory for IPv6 headers\n")); + ahstat.ahs_hdrops++; + m_freem(m); + return ENOBUFS; + } /* Copy all the protocol headers after the IPv6 header */ m_copydata(m, sizeof(struct ip6_hdr), skip - sizeof(struct ip6_hdr), ptr); + alloc = 1; } else - ptr = mtod(m, unsigned char *) + sizeof(struct ip6_hdr); + { + /* No need to allocate memory */ + ptr = mtod(m, unsigned char *) + sizeof(struct ip6_hdr); + alloc = 0; + } } else break; @@ -460,224 +382,374 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) * Process the mutable/immutable options -- borrows * heavily from the KAME code. */ - for (last = len, count = len + sizeof(struct ip6_ext); + for (count = len + sizeof(struct ip6_ext); count < len + ((ip6e->ip6e_len + 1) << 3);) { if (ptr[count] == IP6OPT_PAD1) { count++; - continue; + continue; /* Skip padding */ } /* Sanity check */ - if (count + sizeof(struct ip6_ext) > len + - ((ip6e->ip6e_len + 1) << 3)) + if (count > len + ((ip6e->ip6e_len + 1) << 3)) { - DPRINTF(("ah_input(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + DPRINTF(("ah_massage_headers(): malformed IPv6 options header\n")); ahstat.ahs_hdrops++; m_freem(m); /* Free, if we allocated */ - if (m->m_len < skip) - { - FREE(ptr, M_XDATA); - ptr = NULL; - } - return NULL; + if (alloc) + FREE(ptr, M_XDATA); + + return EINVAL; } - /* - * If mutable option, calculate authenticator - * for all immutable fields so far, then include - * a zeroed-out version of this option. - */ + ad = ptr[count + 1]; + + /* If mutable option, zeroize */ if (ptr[count] & IP6OPT_MUTABLE) - { - /* Calculate immutables */ - ahx->Update(&ctx, ptr + last, - count + sizeof(struct ip6_ext) - - last); - last = count + ptr[count + 1] + - sizeof(struct ip6_ext); - - /* Calculate "zeroed-out" immutables */ - ahx->Update(&ctx, ipseczeroes, ptr[count + 1] - - sizeof(struct ip6_ext)); - } - - count += ptr[count + 1] + sizeof(struct ip6_ext); + bcopy(ipseczeroes, ptr + count, ptr[count + 1]); + + count += ad; /* Sanity check */ if (count > skip - sizeof(struct ip6_hdr)) { - DPRINTF(("ah_input(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + DPRINTF(("ah_massage_headers(): malformed IPv6 options header\n")); ahstat.ahs_hdrops++; m_freem(m); /* Free, if we allocated */ - if (m->m_len < skip) - { - FREE(ptr, M_XDATA); - ptr = NULL; - } - return NULL; + if (alloc) + FREE(ptr, M_XDATA); + + return EINVAL; } } - /* Include any trailing immutable options */ - ahx->Update(&ctx, ptr + last, - len + ((ip6e->ip6e_len + 1) << 3) - last); - len += ((ip6e->ip6e_len + 1) << 3); /* Advance */ off = ip6e->ip6e_nxt; break; case IPPROTO_ROUTING: + /* Always include routing headers in computation */ ip6e = (struct ip6_ext *) (ptr + len); - ahx->Update(&ctx, ptr + len, (ip6e->ip6e_len + 1) << 3); len += ((ip6e->ip6e_len + 1) << 3); /* Advance */ off = ip6e->ip6e_nxt; break; default: - DPRINTF(("ah_input(): unexpected IPv6 header type %d in SA %s/%08x\n", off, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - len = skip - sizeof(struct ip6_hdr); - break; + DPRINTF(("ah_massage_headers(): unexpected IPv6 header type %d\n", off)); + if (alloc) + FREE(ptr, M_XDATA); + ahstat.ahs_hdrops++; + m_freem(m); + return EINVAL; } - /* Free, if we allocated */ - if (m->m_len < skip) + /* Copyback and free, if we allocated */ + if (alloc) { + m_copyback(m, sizeof(struct ip6_hdr), + skip - sizeof(struct ip6_hdr), ptr); FREE(ptr, M_XDATA); - ptr = NULL; } break; #endif /* INET6 */ + } - default: - DPRINTF(("ah_input(): unsupported protocol family %d in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; - m_freem(m); - return NULL; + return 0; +} + +/* + * ah_input() gets called to verify that an input packet + * passes authentication. + */ +int +ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) +{ + struct auth_hash *ahx = (struct auth_hash *) tdb->tdb_authalgxform; + u_int32_t btsx; + u_int8_t hl; + int rplen; + + struct cryptodesc *crda = NULL; + struct cryptop *crp; + + if (!(tdb->tdb_flags & TDBF_NOREPLAY)) + rplen = AH_FLENGTH + sizeof(u_int32_t); + else + rplen = AH_FLENGTH; + + /* Save the AH header, we use it throughout */ + m_copydata(m, skip + offsetof(struct ah, ah_hl), sizeof(u_int8_t), + (caddr_t) &hl); + + /* Replay window checking, if applicable */ + if ((tdb->tdb_wnd > 0) && (!(tdb->tdb_flags & TDBF_NOREPLAY))) + { + m_copydata(m, skip + offsetof(struct ah, ah_rpl), sizeof(u_int32_t), + (caddr_t) &btsx); + btsx = ntohl(btsx); + + switch (checkreplaywindow32(btsx, 0, &(tdb->tdb_rpl), + tdb->tdb_wnd, &(tdb->tdb_bitmap))) + { + case 0: /* All's well */ + break; + + case 1: + DPRINTF(("ah_input(): replay counter wrapped for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + ahstat.ahs_wrap++; + m_freem(m); + return ENOBUFS; + + case 2: + case 3: + DPRINTF(("ah_input(): duplicate packet received in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + ahstat.ahs_replay++; + m_freem(m); + return ENOBUFS; + + default: + DPRINTF(("ah_input(): bogus value from checkreplaywindow32() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + ahstat.ahs_replay++; + m_freem(m); + return ENOBUFS; + } } - /* Record the beginning of the AH header */ - for (len = 0, m1 = m; m1 && (len + m1->m_len <= skip); m1 = m1->m_next) - len += m1->m_len; + /* Verify AH header length */ + if (hl * sizeof(u_int32_t) != ahx->authsize + rplen - AH_FLENGTH) + { + DPRINTF(("ah_input(): bad authenticator length %d for packet in SA %s/%08x\n", hl * sizeof(u_int32_t), ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + ahstat.ahs_badauthl++; + m_freem(m); + return EACCES; + } - if (m1 == NULL) + /* Update the counters */ + tdb->tdb_cur_bytes += (m->m_pkthdr.len - skip - hl * sizeof(u_int32_t)); + ahstat.ahs_ibytes += (m->m_pkthdr.len - skip - hl * sizeof(u_int32_t)); + + /* Hard expiration */ + if ((tdb->tdb_flags & TDBF_BYTES) && + (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) + { + pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); + tdb_delete(tdb, 0, TDBEXP_TIMEOUT); + m_freem(m); + return ENXIO; + } + + /* Notify on expiration */ + if ((tdb->tdb_flags & TDBF_SOFT_BYTES) && + (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) + { + pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); + tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */ + } + + /* Get crypto descriptors */ + crp = crypto_getreq(1); + if (crp == NULL) { - DPRINTF(("ah_input(): bad mbuf chain for packet in SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; m_freem(m); - return NULL; + DPRINTF(("ah_input(): failed to acquire crypto descriptors\n")); + ahstat.ahs_crypto++; + return ENOBUFS; } - else - roff = skip - len; - /* Skip the AH header */ - for (len = 0, m0 = m1; - m0 && (len + m0->m_len <= AH_FLENGTH + rplen + ahx->authsize + roff); - m0 = m0->m_next) - len += m0->m_len; + crda = crp->crp_desc; + + crda->crd_skip = 0; + crda->crd_len = m->m_pkthdr.len; + crda->crd_inject = skip + rplen; + + /* Authentication operation */ + crda->crd_alg = ahx->type; + crda->crd_key = tdb->tdb_amxkey; + crda->crd_klen = tdb->tdb_amxkeylen * 8; - if (m0 == NULL) + /* + * Save the authenticator, the skipped portion of the packet, and the + * AH header. + */ + MALLOC(crp->crp_opaque4, caddr_t, skip + rplen + ahx->authsize, + M_XDATA, M_DONTWAIT); + if (crp->crp_opaque4 == 0) { - DPRINTF(("ah_input(): bad mbuf chain for packet in SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; m_freem(m); - return NULL; + crypto_freereq(crp); + DPRINTF(("ah_input(): failed to allocate auth array\n")); + ahstat.ahs_crypto++; + return ENOBUFS; } - else - off = (AH_FLENGTH + rplen + ahx->authsize + roff) - len; - /* Include the AH header (minus the authenticator) in the computation */ - ahx->Update(&ctx, (unsigned char *) &ah, AH_FLENGTH + rplen); + /* Save data */ + m_copydata(m, 0, skip + rplen + ahx->authsize, crp->crp_opaque4); - /* All-zeroes for the authenticator */ - ahx->Update(&ctx, ipseczeroes, ahx->authsize); + /* Zeroize the authenticator on the packet */ + m_copyback(m, skip + rplen, ahx->authsize, ipseczeroes); - /* Amount of data to be verified */ - len = m->m_pkthdr.len - skip - AH_FLENGTH - rplen - ahx->authsize; + /* "Massage" the packet headers for crypto processing */ + if ((btsx = ah_massage_headers(&m, tdb->tdb_dst.sa.sa_family, + skip, ahx->type, 0)) != 0) + { + /* mbuf will be free'd by callee */ + FREE(crp->crp_opaque4, M_XDATA); - /* Loop through the mbuf chain computing the HMAC */ - while (len > 0) + crypto_freereq(crp); + return btsx; + } + + tdb->tdb_ref++; + + /* Crypto operation descriptor */ + crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ + crp->crp_flags = CRYPTO_F_IMBUF; + crp->crp_buf = (caddr_t) m; + crp->crp_callback = (int (*) (struct cryptop *)) ah_input_cb; + crp->crp_sid = tdb->tdb_cryptoid; + + /* These are passed as-is to the callback */ + crp->crp_opaque1 = (caddr_t) tdb; + crp->crp_opaque2 = (caddr_t) skip; + crp->crp_opaque3 = (caddr_t) protoff; + + return crypto_dispatch(crp); +} + +/* + * AH input callback, called directly by the crypto driver. + */ +int +ah_input_cb(void *op) +{ + int roff, rplen, error, skip, protoff; + unsigned char calc[AH_ALEN_MAX]; + struct mbuf *m1, *m0, *m; + struct cryptodesc *crd; + struct auth_hash *ahx; + struct cryptop *crp; + struct tdb *tdb; + + crp = (struct cryptop *) op; + crd = crp->crp_desc; + tdb = (struct tdb *) crp->crp_opaque1; + ahx = (struct auth_hash *) tdb->tdb_authalgxform; + skip = (int) crp->crp_opaque2; + protoff = (int) crp->crp_opaque3; + m = (struct mbuf *) crp->crp_buf; + + tdb->tdb_ref--; + + /* Check for crypto errors */ + if (crp->crp_etype) { - if (m0 == NULL) + if (tdb->tdb_cryptoid != 0) + tdb->tdb_cryptoid = crp->crp_sid; + + if (crp->crp_etype == EAGAIN) { - DPRINTF(("ah_input(): bad mbuf chain for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; - m_freem(m); - return NULL; + tdb->tdb_ref++; + return crypto_dispatch(crp); } - count = min(m0->m_len - off, len); - - ahx->Update(&ctx, mtod(m0, unsigned char *) + off, count); - - len -= count; - off = 0; - m0 = m0->m_next; + ahstat.ahs_noxform++; + DPRINTF(("ah_input_cb(): crypto error %d\n", crp->crp_etype)); + error = crp->crp_etype; + goto baddone; } - /* Finish computation */ - if ((ahx->type == SADB_X_AALG_MD5) || (ahx->type == SADB_X_AALG_SHA1)) + /* Shouldn't happen... */ + if (!m) { - ahx->Update(&ctx, (unsigned char *) tdb->tdb_amxkey, - tdb->tdb_amxkeylen); - ahx->Final(calcauth, &ctx); + ahstat.ahs_crypto++; + DPRINTF(("ah_input_cb(): bogus returned buffer from crypto\n")); + error = EINVAL; + goto baddone; } - else + + /* + * Check that the TDB is still valid -- not really an error, but + * we need to handle it as such. It may happen if the TDB expired + * or was deleted while there was a pending request in the crypto + * queue. + */ + if (tdb->tdb_flags & TDBF_INVALID) { - /* Finish HMAC computation */ - ahx->Final(calcauth, &ctx); - bcopy(tdb->tdb_octx, &ctx, ahx->ctxsize); - ahx->Update(&ctx, calcauth, ahx->hashsize); - ahx->Final(calcauth, &ctx); + ahstat.ahs_invalid++; + tdb_delete(tdb, 0, 0); + error = ENXIO; + DPRINTF(("ah_input_cb(): TDB expired while processing crypto\n")); + goto baddone; } - /* Verify */ - if (bcmp(savauth, calcauth, ahx->authsize)) + if (!(tdb->tdb_flags & TDBF_NOREPLAY)) + rplen = AH_FLENGTH + sizeof(u_int32_t); + else + rplen = AH_FLENGTH; + + /* Copy computed authenticator */ + m_copydata(m, skip + rplen, ahx->authsize, calc); + + /* Verify authenticator */ + if (bcmp(crp->crp_opaque4 + skip + rplen, calc, ahx->authsize)) { DPRINTF(("ah_input(): authentication failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); ahstat.ahs_badauth++; - m_freem(m); - return NULL; + error = EACCES; + goto baddone; } /* Fix the Next Protocol field */ - m_copyback(m, protoff, sizeof(u_int8_t), (u_char *) &(ah.ah_nh)); + ((u_int8_t *) crp->crp_opaque4)[protoff] = + ((u_int8_t *) crp->crp_opaque4)[skip]; - /* - * Remove the AH header from the mbuf. - */ + /* Copyback the saved (uncooked) network headers */ + m_copyback(m, 0, skip, crp->crp_opaque4); + + /* No longer needed */ + FREE(crp->crp_opaque4, M_XDATA); + crypto_freereq(crp); + + /* Record the beginning of the AH header */ + m1 = m_getptr(m, skip, &roff); + if (m1 == NULL) + { + DPRINTF(("ah_input(): bad mbuf chain for packet in SA %s/%08x\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + ahstat.ahs_hdrops++; + m_freem(m); + return EINVAL; + } + + /* Remove the AH header from the mbuf */ if (roff == 0) { /* The AH header was conveniently at the beginning of the mbuf */ - m_adj(m1, AH_FLENGTH + rplen + ahx->authsize); + m_adj(m1, rplen + ahx->authsize); if (!(m1->m_flags & M_PKTHDR)) - m->m_pkthdr.len -= AH_FLENGTH + rplen + ahx->authsize; + m->m_pkthdr.len -= rplen + ahx->authsize; } else - if (roff + AH_FLENGTH + rplen + ahx->authsize >= m1->m_len) + if (roff + rplen + ahx->authsize >= m1->m_len) { /* * Part or all of the AH header is at the end of this mbuf, so first * let's remove the remainder of the AH header from the * beginning of the remainder of the mbuf chain, if any. */ - if (roff + AH_FLENGTH + rplen + ahx->authsize > m1->m_len) + if (roff + rplen + ahx->authsize > m1->m_len) { /* Adjust the next mbuf by the remainder */ - m_adj(m1->m_next, roff + AH_FLENGTH + rplen + - ahx->authsize - m1->m_len); + m_adj(m1->m_next, roff + rplen + ahx->authsize - m1->m_len); /* The second mbuf is guaranteed not to have a pkthdr... */ - m->m_pkthdr.len -= (roff + AH_FLENGTH + rplen + - ahx->authsize - m1->m_len); + m->m_pkthdr.len -= (roff + rplen + ahx->authsize - m1->m_len); } /* Now, let's unlink the mbuf chain for a second...*/ @@ -699,38 +771,43 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) * overlapping copy of the remainder of the mbuf over the ESP * header. */ - bcopy(mtod(m1, u_char *) + roff + AH_FLENGTH + rplen + ahx->authsize, + bcopy(mtod(m1, u_char *) + roff + rplen + ahx->authsize, mtod(m1, u_char *) + roff, - m1->m_len - (roff + AH_FLENGTH + rplen + ahx->authsize)); - m1->m_len -= AH_FLENGTH + rplen + ahx->authsize; - m->m_pkthdr.len -= AH_FLENGTH + rplen + ahx->authsize; + m1->m_len - (roff + rplen + ahx->authsize)); + m1->m_len -= rplen + ahx->authsize; + m->m_pkthdr.len -= rplen + ahx->authsize; } - return m; + return ipsec_common_input_cb(m, tdb, skip, protoff); + + baddone: + if (m) + m_freem(m); + + /* We have to free this manually */ + if (crp && crp->crp_opaque4) + FREE(crp->crp_opaque4, M_XDATA); + + crypto_freereq(crp); + + return error; } +/* + * AH output routine, called by ipsp_process_packet(). + */ int ah_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, - int protoff) + int protoff) { struct auth_hash *ahx = (struct auth_hash *) tdb->tdb_authalgxform; - unsigned char calcauth[AH_MAX_HASHLEN]; - int len, off, count, rplen; - unsigned char *ptr; - union authctx ctx; - struct mbuf *mo; + struct cryptodesc *crda; + struct mbuf *mo, *mi; + struct cryptop *crp; + u_int16_t iplen; + int len, rplen; struct ah *ah; -#ifdef INET - struct ip ipo; -#endif /* INET */ - -#ifdef INET6 - struct ip6_ext *ip6e; - struct ip6_hdr ip6; - int last; -#endif /* INET6 */ - #if NBPFILTER > 0 { struct ifnet *ifn; @@ -771,35 +848,47 @@ ah_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, } if (!(tdb->tdb_flags & TDBF_NOREPLAY)) - rplen = sizeof(u_int32_t); + rplen = AH_FLENGTH + sizeof(u_int32_t); else - rplen = 0; + rplen = AH_FLENGTH; -#ifdef INET - if ((tdb->tdb_dst.sa.sa_family == AF_INET) && - (AH_FLENGTH + rplen + ahx->authsize + m->m_pkthdr.len > IP_MAXPACKET)) + switch (tdb->tdb_dst.sa.sa_family) { - DPRINTF(("ah_output(): packet in SA %s/%08x got too big\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - m_freem(m); - ahstat.ahs_toobig++; - return EMSGSIZE; - } +#ifdef INET + case AF_INET: + /* Check for IP maximum packet size violations */ + if (rplen + ahx->authsize + m->m_pkthdr.len > IP_MAXPACKET) + { + DPRINTF(("ah_output(): packet in SA %s/%08x got too big\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + m_freem(m); + ahstat.ahs_toobig++; + return EMSGSIZE; + } + break; #endif /* INET */ #ifdef INET6 - if ((tdb->tdb_dst.sa.sa_family == AF_INET6) && - (AH_FLENGTH + rplen + ahx->authsize + m->m_pkthdr.len > - IPV6_MAXPACKET)) - { - DPRINTF(("ah_output(): packet in SA %s/%08x got too big\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - m_freem(m); - ahstat.ahs_toobig++; - return EMSGSIZE; - } + case AF_INET6: + /* Check for IPv6 maximum packet size violations */ + if (rplen + ahx->authsize + m->m_pkthdr.len > IPV6_MAXPACKET) + { + DPRINTF(("ah_output(): packet in SA %s/%08x got too big\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + m_freem(m); + ahstat.ahs_toobig++; + return EMSGSIZE; + } + break; #endif /* INET6 */ + default: + DPRINTF(("ah_output(): unknown/unsupported protocol family %d, SA %s/%08x\n", tdb->tdb_dst.sa.sa_family, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + m_freem(m); + ahstat.ahs_nopf++; + return EPFNOSUPPORT; + } + /* Update the counters */ tdb->tdb_cur_bytes += m->m_pkthdr.len - skip; ahstat.ahs_obytes += m->m_pkthdr.len - skip; @@ -825,24 +914,21 @@ ah_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, /* * Loop through mbuf chain; if we find an M_EXT mbuf with * more than one reference, replace the rest of the chain. - * This may not be strictly necessary for AH packets, if we were - * careful with the rest of our processing (and made a lot of - * assumptions about the layout of the packets/mbufs). */ - (*mp) = m; - while ((*mp) != NULL && - (!((*mp)->m_flags & M_EXT) || - ((*mp)->m_ext.ext_ref == NULL && - mclrefcnt[mtocl((*mp)->m_ext.ext_buf)] <= 1))) + mi = m; + while (mi != NULL && + (!(mi->m_flags & M_EXT) || + (mi->m_ext.ext_ref == NULL && + mclrefcnt[mtocl(mi->m_ext.ext_buf)] <= 1))) { - mo = (*mp); - (*mp) = (*mp)->m_next; + mo = mi; + mi = mi->m_next; } - if ((*mp) != NULL) + if (mi != NULL) { /* Replace the rest of the mbuf chain. */ - struct mbuf *n = m_copym2((*mp), 0, M_COPYALL, M_DONTWAIT); + struct mbuf *n = m_copym2(mi, 0, M_COPYALL, M_DONTWAIT); if (n == NULL) { @@ -856,385 +942,211 @@ ah_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, else m = n; - m_freem((*mp)); - (*mp) = NULL; + m_freem(mi); } - bcopy(tdb->tdb_ictx, (caddr_t) &ctx, ahx->ctxsize); - - switch (tdb->tdb_dst.sa.sa_family) + /* Inject AH header */ + mi = m_inject(m, skip, rplen + ahx->authsize, M_DONTWAIT); + if (mi == NULL) { -#ifdef INET - case AF_INET: - /* - * This is the most painless way of dealing with IPv4 header - * and option processing -- just make sure they're in - * contiguous memory. - */ - m = m_pullup(m, skip); - if (m == NULL) - { - DPRINTF(("ah_output(): m_pullup() failed, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; - return ENOBUFS; - } - - ptr = mtod(m, unsigned char *) + sizeof(struct ip); - - bcopy(mtod(m, unsigned char *), (unsigned char *) &ipo, - sizeof(struct ip)); + DPRINTF(("ah_output(): failed to inject AH header for SA %s/%08x\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + m_freem(m); + ahstat.ahs_wrap++; + return ENOBUFS; + } - ipo.ip_tos = 0; - if ((ahx->type == SADB_X_AALG_MD5) || - (ahx->type == SADB_X_AALG_SHA1)) - ipo.ip_off = htons(ntohs(ipo.ip_off) & IP_DF); - else - ipo.ip_off = 0; - ipo.ip_ttl = 0; - ipo.ip_sum = 0; - ipo.ip_p = IPPROTO_AH; - ipo.ip_len = htons(ntohs(ipo.ip_len) + AH_FLENGTH + rplen + - ahx->authsize); - - /* - * If we have a loose or strict routing option, we are - * supposed to use the last address in it as the - * destination address in the authenticated IPv4 header. - * - * Note that this is an issue only with the output routine; - * we will correctly process (in the AH input routine) incoming - * packets with these options without special consideration. - * - * We assume that the IP header contains the next hop's address, - * and that the last entry in the option is the final - * destination's address. - */ - if (skip > sizeof(struct ip)) - { - for (off = sizeof(struct ip); off < skip;) - { - /* First sanity check for zero-length options */ - if ((ptr[off] != IPOPT_EOL) && (ptr[off] != IPOPT_NOP) && - (ptr[off + 1] == 0)) - { - DPRINTF(("ah_output(): illegal zero-length IPv4 option %d in SA %s/%08x\n", ptr[off], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; - m_freem(m); - return EMSGSIZE; - } + /* + * The AH header is guaranteed by m_inject() to be in contiguous memory, + * at the beginning of the returned mbuf. + */ + ah = mtod(mi, struct ah *); + + /* Initialize the AH header */ + m_copydata(m, protoff, sizeof(u_int8_t), (caddr_t) &ah->ah_nh); + ah->ah_hl = (rplen + ahx->authsize - AH_FLENGTH) / sizeof(u_int32_t); + ah->ah_rv = 0; + ah->ah_spi = tdb->tdb_spi; - switch (ptr[off]) - { - case IPOPT_LSRR: - case IPOPT_SSRR: - /* Sanity check for length */ - if (ptr[off + 1] < 2 + sizeof(struct in_addr)) - { - DPRINTF(("ah_output(): malformed LSRR or SSRR IPv4 option header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; - m_freem(m); - return EMSGSIZE; - } - - bcopy(ptr + off + ptr[off + 1] - - sizeof(struct in_addr), - &(ipo.ip_dst), sizeof(struct in_addr)); - off = skip; - break; - - case IPOPT_EOL: - off = skip; - break; - - case IPOPT_NOP: - off++; - break; - - default: /* Some other option, just skip it */ - off += ptr[off + 1]; - break; - } + /* Zeroize authenticator */ + m_copyback(m, skip + rplen, ahx->authsize, ipseczeroes); - /* Sanity check */ - if (off > skip) - { - DPRINTF(("ah_output(): malformed IPv4 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; - m_freem(m); - return EMSGSIZE; - } - } - } + if (!(tdb->tdb_flags & TDBF_NOREPLAY)) + ah->ah_rpl = htonl(tdb->tdb_rpl++); - /* Include IP header in authenticator computation */ - ahx->Update(&ctx, (unsigned char *) &ipo, sizeof(struct ip)); + /* Get crypto descriptors */ + crp = crypto_getreq(1); + if (crp == NULL) + { + m_freem(m); + DPRINTF(("ah_output(): failed to acquire crypto descriptors\n")); + ahstat.ahs_crypto++; + return ENOBUFS; + } - /* IPv4 option processing */ - for (off = sizeof(struct ip); off < skip;) - { - switch (ptr[off]) - { - case IPOPT_EOL: - ahx->Update(&ctx, ptr + off, 1); - off = skip; /* End the loop */ - break; + crda = crp->crp_desc; - case IPOPT_NOP: - ahx->Update(&ctx, ptr + off, 1); - off++; - break; + crda->crd_skip = 0; + crda->crd_inject = skip + rplen; + crda->crd_len = m->m_pkthdr.len; - case IPOPT_SECURITY: /* 0x82 */ - case 0x85: /* Extended security */ - case 0x86: /* Commercial security */ - case 0x94: /* Router alert */ - case 0x95: /* RFC1770 */ - ahx->Update(&ctx, ptr + off, ptr[off + 1]); - off += ptr[off + 1]; - break; + /* Authentication operation */ + crda->crd_alg = ahx->type; + crda->crd_key = tdb->tdb_amxkey; + crda->crd_klen = tdb->tdb_amxkeylen * 8; - default: - ahx->Update(&ctx, ipseczeroes, ptr[off + 1]); - off += ptr[off + 1]; - break; - } + /* Save the skipped portion of the packet */ + MALLOC(crp->crp_opaque4, caddr_t, skip, M_XDATA, M_DONTWAIT); + if (crp->crp_opaque4 == 0) + { + m_freem(m); + crypto_freereq(crp); + DPRINTF(("ah_output(): failed to allocate auth array\n")); + ahstat.ahs_crypto++; + return ENOBUFS; + } + else + m_copydata(m, 0, skip, crp->crp_opaque4); - /* Sanity check */ - if (off > skip) - { - DPRINTF(("ah_output(): malformed IPv4 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; - m_freem(m); - return EMSGSIZE; - } - } + /* + * Fix IP header length on the header used for authentication. We don't + * need to fix the original header length as it will be fixed by our + * caller. + */ + switch (tdb->tdb_dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + bcopy(crp->crp_opaque4 + offsetof(struct ip, ip_len), + (caddr_t) &iplen, sizeof(u_int16_t)); + iplen = htons(ntohs(iplen) + rplen + ahx->authsize); + m_copyback(m, offsetof(struct ip, ip_len), sizeof(u_int16_t), + (caddr_t) &iplen); break; #endif /* INET */ #ifdef INET6 case AF_INET6: - /* Copy and "cook" the IPv6 header */ - m_copydata(m, 0, sizeof(ip6), (unsigned char *) &ip6); - - /* We don't do IPv6 Jumbograms */ - if (ip6.ip6_plen == 0) - { - DPRINTF(("ah_output(): unsupported IPv6 jumbogram in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; - m_freem(m); - return EMSGSIZE; - } - - ip6.ip6_flow = 0; - ip6.ip6_hlim = 0; - ip6.ip6_vfc &= ~IPV6_VERSION_MASK; - ip6.ip6_vfc |= IPV6_VERSION; - - /* - * Note that here we assume that on output, the IPv6 header - * and any Type0 Routing Header present have been made to look - * like the will at the destination. Note that this is a - * different assumption than we made for IPv4 (because of - * different option processing in IPv4 and IPv6, and different - * code paths from IPv4/IPv6 to here). - */ - - /* Include IPv6 header in authenticator computation */ - ahx->Update(&ctx, (unsigned char *) &ip6, sizeof(ip6)); - - /* Let's deal with the remaining headers (if any) */ - if (skip - sizeof(struct ip6_hdr) > 0) - { - if (m->m_len <= skip) - { - MALLOC(ptr, unsigned char *, - skip - sizeof(struct ip6_hdr), M_XDATA, M_WAITOK); - - /* Copy all the protocol headers after the IPv6 header */ - m_copydata(m, sizeof(struct ip6_hdr), - skip - sizeof(struct ip6_hdr), ptr); - } - else - ptr = mtod(m, unsigned char *) + sizeof(struct ip6_hdr); - } - else - break; /* Done */ + bcopy(crp->crp_opaque4 + offsetof(struct ip6_hdr, ip6_plen), + (caddr_t) &iplen, sizeof(u_int16_t)); + iplen = htons(ntohs(iplen) + rplen + ahx->authsize); + m_copyback(m, offsetof(struct ip6_hdr, ip6_plen), + sizeof(u_int16_t), (caddr_t) &iplen); + break; +#endif /* INET6 */ + } - off = ip6.ip6_nxt & 0xff; /* Next header type */ - for (len = 0; len < skip - sizeof(struct ip6_hdr);) - switch (off) - { - case IPPROTO_HOPOPTS: - case IPPROTO_DSTOPTS: - ip6e = (struct ip6_ext *) (ptr + len); + tdb->tdb_ref++; - /* - * Process the mutable/immutable options -- borrows - * heavily from the KAME code. - */ - for (last = len, count = len + sizeof(struct ip6_ext); - count < len + ((ip6e->ip6e_len + 1) << 3);) - { - if (ptr[count] == IP6OPT_PAD1) - { - count++; - continue; - } + /* Update the Next Protocol field in the IP header and the saved data */ + len = IPPROTO_AH; + m_copyback(m, protoff, sizeof(u_int8_t), (caddr_t) &len); + ((u_int8_t *) crp->crp_opaque4)[protoff] = IPPROTO_AH; - /* Sanity check */ - if (count + sizeof(struct ip6_ext) > len + - ((ip6e->ip6e_len + 1) << 3)) - { - DPRINTF(("ah_output(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; - m_freem(m); + /* "Massage" the packet headers for crypto processing */ + if ((len = ah_massage_headers(&m, tdb->tdb_dst.sa.sa_family, + skip, ahx->type, 1)) != 0) + { + /* mbuf will be free'd by callee */ + FREE(crp->crp_opaque4, M_XDATA); + crypto_freereq(crp); + return len; + } - /* Free, if we allocated */ - if (m->m_len < skip) - FREE(ptr, M_XDATA); - return EMSGSIZE; - } + /* Crypto operation descriptor */ + crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ + crp->crp_flags = CRYPTO_F_IMBUF; + crp->crp_buf = (caddr_t) m; + crp->crp_callback = (int (*) (struct cryptop *)) ah_output_cb; + crp->crp_sid = tdb->tdb_cryptoid; - /* - * If mutable option, calculate authenticator - * for all immutable fields so far, then include - * a zeroed-out version of this option. - */ - if (ptr[count] & IP6OPT_MUTABLE) - { - /* Calculate immutables */ - ahx->Update(&ctx, ptr + last, count + 2 - last); - last = count + ptr[count + 1]; + /* These are passed as-is to the callback */ + crp->crp_opaque1 = (caddr_t) tdb; + crp->crp_opaque2 = (caddr_t) skip; + crp->crp_opaque3 = (caddr_t) protoff; - /* Calculate "zeroed-out" immutables */ - ahx->Update(&ctx, ipseczeroes, - ptr[count + 1] - 2); - } - - count += ptr[count + 1]; - - /* Sanity check */ - if (count > skip - sizeof(struct ip6_hdr)) - { - DPRINTF(("ah_output(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; - m_freem(m); + return crypto_dispatch(crp); +} - /* Free, if we allocated */ - if (m->m_len < skip) - FREE(ptr, M_XDATA); - return EMSGSIZE; - } - } +/* + * AH output callback, called directly from the crypto handler. + */ +int +ah_output_cb(void *op) +{ + int skip, protoff, error; + struct cryptop *crp; + struct tdb *tdb; + struct mbuf *m; - /* Include any trailing immutable options */ - ahx->Update(&ctx, ptr + last, - len + ((ip6e->ip6e_len + 1) << 3) - last); + crp = (struct cryptop *) op; + tdb = (struct tdb *) crp->crp_opaque1; + skip = (int) crp->crp_opaque2; + protoff = (int) crp->crp_opaque3; + m = (struct mbuf *) crp->crp_buf; - len += ((ip6e->ip6e_len + 1) << 3); /* Advance */ - off = ip6e->ip6e_nxt; - break; + tdb->tdb_ref--; - case IPPROTO_ROUTING: - ip6e = (struct ip6_ext *) (ptr + len); - ahx->Update(&ctx, ptr + len, (ip6e->ip6e_len + 1) << 3); - len += ((ip6e->ip6e_len + 1) << 3); /* Advance */ - off = ip6e->ip6e_nxt; - break; - } - - /* Free, if we allocated */ - if (m->m_len < skip) - { - FREE(ptr, M_XDATA); - ptr = NULL; - } + /* Check for crypto errors */ + if (crp->crp_etype) + { + if (tdb->tdb_cryptoid != 0) + tdb->tdb_cryptoid = crp->crp_sid; - break; -#endif /* INET6 */ + if (crp->crp_etype == EAGAIN) + { + tdb->tdb_ref++; + return crypto_dispatch(crp); + } - default: - DPRINTF(("ah_output(): unsupported protocol family %d in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_nopf++; - m_freem(m); - return EPFNOSUPPORT; + ahstat.ahs_noxform++; + DPRINTF(("ah_output_cb(): crypto error %d\n", crp->crp_etype)); + error = crp->crp_etype; + goto baddone; } - /* Inject AH header */ - (*mp) = m_inject(m, skip, AH_FLENGTH + rplen + ahx->authsize, M_WAITOK); - if ((*mp) == NULL) + /* Shouldn't happen... */ + if (!m) { - DPRINTF(("ah_output(): failed to inject AH header for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - m_freem(m); - ahstat.ahs_wrap++; - return ENOBUFS; + ahstat.ahs_crypto++; + DPRINTF(("ah_output_cb(): bogus returned buffer from crypto\n")); + error = EINVAL; + goto baddone; } /* - * The AH header is guaranteed by m_inject() to be in contiguous memory, - * at the beginning of the returned mbuf. + * Check that the TDB is still valid -- not really an error, but + * we need to handle it as such. It may happen if the TDB expired + * or was deleted while there was a pending request in the crypto + * queue. */ - ah = mtod((*mp), struct ah *); - - /* Initialize the AH header */ - m_copydata(m, protoff, sizeof(u_int8_t), (caddr_t) &ah->ah_nh); - ah->ah_hl = (rplen + ahx->authsize) / sizeof(u_int32_t); - ah->ah_rv = 0; - ah->ah_spi = tdb->tdb_spi; - - if (!(tdb->tdb_flags & TDBF_NOREPLAY)) - ah->ah_rpl = htonl(tdb->tdb_rpl++); - - /* Update the Next Protocol field in the IP header */ - len = IPPROTO_AH; - m_copyback(m, protoff, sizeof(u_int8_t), (caddr_t) &len); - - /* Include the header AH in the authenticator computation */ - ahx->Update(&ctx, (unsigned char *) ah, AH_FLENGTH + rplen); - ahx->Update(&ctx, ipseczeroes, ahx->authsize); - - /* Calculate the authenticator over the rest of the packet */ - len = m->m_pkthdr.len - (skip + AH_FLENGTH + rplen + ahx->authsize); - off = AH_FLENGTH + rplen + ahx->authsize; - - while (len > 0) + if (tdb->tdb_flags & TDBF_INVALID) { - if ((*mp) == 0) - { - DPRINTF(("ah_output(): bad mbuf chain for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - ahstat.ahs_hdrops++; - m_freem(m); - (*mp) = NULL; - return EMSGSIZE; - } + ahstat.ahs_invalid++; + tdb_delete(tdb, 0, 0); + error = ENXIO; + DPRINTF(("ah_output_cb(): TDB expired while processing crypto\n")); + goto baddone; + } - count = min((*mp)->m_len - off, len); + /* Copy original headers (with the new protocol number) back in place */ + m_copyback(m, 0, skip, crp->crp_opaque4); - ahx->Update(&ctx, mtod((*mp), unsigned char *) + off, count); + /* No longer needed */ + FREE(crp->crp_opaque4, M_XDATA); + crypto_freereq(crp); - len -= count; - off = 0; - (*mp) = (*mp)->m_next; - } + return ipsp_process_done(m, tdb); - if ((ahx->type == SADB_X_AALG_MD5) || (ahx->type == SADB_X_AALG_SHA1)) - ahx->Update(&ctx, (unsigned char *) tdb->tdb_amxkey, - tdb->tdb_amxkeylen); - else - { - /* HMAC */ - ahx->Final(calcauth, &ctx); - bcopy(tdb->tdb_octx, &ctx, ahx->ctxsize); - ahx->Update(&ctx, calcauth, ahx->hashsize); - } + baddone: + if (m) + m_freem(m); - ahx->Final(calcauth, &ctx); + /* We have to free this manually */ + if (crp && crp->crp_opaque4) + FREE(crp->crp_opaque4, M_XDATA); - /* Copy the authenticator */ - bcopy(calcauth, ((caddr_t) ah) + AH_FLENGTH + rplen, ahx->authsize); + crypto_freereq(crp); - *mp = m; - - return 0; + return error; } diff --git a/sys/netinet/ip_ah.h b/sys/netinet/ip_ah.h index 1938abad340..45901eea86f 100644 --- a/sys/netinet/ip_ah.h +++ b/sys/netinet/ip_ah.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ah.h,v 1.24 2000/01/27 08:09:08 angelos Exp $ */ +/* $OpenBSD: ip_ah.h,v 1.25 2000/03/17 10:25:22 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -40,23 +40,24 @@ struct ahstat { - u_int32_t ahs_hdrops; /* packet shorter than header shows */ - u_int32_t ahs_nopf; /* Protocol family not supported */ + u_int32_t ahs_hdrops; /* Packet shorter than header shows */ + u_int32_t ahs_nopf; /* Protocol family not supported */ u_int32_t ahs_notdb; u_int32_t ahs_badkcr; u_int32_t ahs_badauth; u_int32_t ahs_noxform; u_int32_t ahs_qfull; - u_int32_t ahs_wrap; - u_int32_t ahs_replay; - u_int32_t ahs_badauthl; /* bad authenticator length */ + u_int32_t ahs_wrap; + u_int32_t ahs_replay; + u_int32_t ahs_badauthl; /* Bad authenticator length */ u_int32_t ahs_input; /* Input AH packets */ u_int32_t ahs_output; /* Output AH packets */ - u_int32_t ahs_invalid; /* Trying to use an invalid TDB */ - u_int64_t ahs_ibytes; /* input bytes */ - u_int64_t ahs_obytes; /* output bytes */ - u_int32_t ahs_toobig; /* packet got larger than IP_MAXPACKET */ - u_int32_t ahs_pdrops; /* packet blocked due to policy */ + u_int32_t ahs_invalid; /* Trying to use an invalid TDB */ + u_int64_t ahs_ibytes; /* Input bytes */ + u_int64_t ahs_obytes; /* Output bytes */ + u_int32_t ahs_toobig; /* Packet got larger than IP_MAXPACKET */ + u_int32_t ahs_pdrops; /* Packet blocked due to policy */ + u_int32_t ahs_crypto; /* Crypto processing failure */ }; struct ah diff --git a/sys/netinet/ip_esp.c b/sys/netinet/ip_esp.c index b6bf578d455..57ee4adb935 100644 --- a/sys/netinet/ip_esp.c +++ b/sys/netinet/ip_esp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_esp.c,v 1.32 2000/02/07 06:09:09 itojun Exp $ */ +/* $OpenBSD: ip_esp.c,v 1.33 2000/03/17 10:25:22 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -73,6 +73,12 @@ #include <net/pfkeyv2.h> #include <net/if_enc.h> +#include <sys/md5k.h> +#include <crypto/sha1.h> +#include <crypto/rmd160.h> +#include <crypto/crypto.h> +#include <crypto/xform.h> + #include "bpfilter.h" #ifdef ENCDEBUG @@ -85,34 +91,9 @@ #define offsetof(s, e) ((int)&((s *)0)->e) #endif -extern struct auth_hash auth_hash_hmac_md5_96; -extern struct auth_hash auth_hash_hmac_sha1_96; -extern struct auth_hash auth_hash_hmac_ripemd_160_96; - -struct auth_hash *esp_hash[] = { - &auth_hash_hmac_md5_96, - &auth_hash_hmac_sha1_96, - &auth_hash_hmac_ripemd_160_96 -}; - -extern struct enc_xform enc_xform_des; -extern struct enc_xform enc_xform_3des; -extern struct enc_xform enc_xform_blf; -extern struct enc_xform enc_xform_cast5; -extern struct enc_xform enc_xform_skipjack; - -struct enc_xform *esp_xform[] = { - &enc_xform_des, - &enc_xform_3des, - &enc_xform_blf, - &enc_xform_cast5, - &enc_xform_skipjack, -}; - /* * esp_attach() is called from the transformation initialization code. */ - int esp_attach() { @@ -122,27 +103,39 @@ esp_attach() /* * esp_init() is called when an SPI is being set up. */ - int esp_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii) { struct enc_xform *txform = NULL; struct auth_hash *thash = NULL; - int i; + struct cryptoini cria, crie; - /* Check whether the encryption algorithm is supported */ - for (i = sizeof(esp_xform) / sizeof(esp_xform[0]) - 1; - i >= 0; i--) - if (ii->ii_encalg == esp_xform[i]->type) - break; - - if (i < 0) + switch (ii->ii_encalg) { - DPRINTF(("esp_init(): unsupported encryption algorithm %d specified\n", ii->ii_encalg)); - return EINVAL; - } + case SADB_EALG_DESCBC: + txform = &enc_xform_des; + break; + + case SADB_EALG_3DESCBC: + txform = &enc_xform_3des; + break; + + case SADB_X_EALG_BLF: + txform = &enc_xform_blf; + break; + + case SADB_X_EALG_CAST: + txform = &enc_xform_cast5; + break; + + case SADB_X_EALG_SKIPJACK: + txform = &enc_xform_skipjack; + break; - txform = esp_xform[i]; + default: + DPRINTF(("esp_init(): unsupported encryption algorithm %d specified\n", ii->ii_encalg)); + return EINVAL; + } if (ii->ii_enckeylen < txform->minkey) { @@ -158,18 +151,24 @@ esp_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii) if (ii->ii_authalg) { - for (i = sizeof(esp_hash) / sizeof(esp_hash[0]) - 1; - i >= 0; i--) - if (ii->ii_authalg == esp_hash[i]->type) - break; - - if (i < 0) + switch (ii->ii_authalg) { - DPRINTF(("esp_init(): unsupported authentication algorithm %d specified\n", ii->ii_authalg)); - return EINVAL; - } + case SADB_AALG_MD5HMAC96: + thash = &auth_hash_hmac_md5_96; + break; + + case SADB_AALG_SHA1HMAC96: + thash = &auth_hash_hmac_sha1_96; + break; - thash = esp_hash[i]; + case SADB_X_AALG_RIPEMD160HMAC96: + thash = &auth_hash_hmac_ripemd_160_96; + break; + + default: + DPRINTF(("esp_init(): unsupported authentication algorithm %d specified\n", ii->ii_authalg)); + return EINVAL; + } if (ii->ii_authkeylen != thash->keysize) { @@ -195,53 +194,62 @@ esp_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii) if (tdbp->tdb_flags & TDBF_HALFIV) tdbp->tdb_ivlen /= 2; - /* Initialize the IV */ - get_random_bytes(tdbp->tdb_iv, tdbp->tdb_ivlen); - /* Save the raw keys */ - tdbp->tdb_amxkeylen = ii->ii_authkeylen; - MALLOC(tdbp->tdb_amxkey, u_int8_t *, tdbp->tdb_amxkeylen, M_XDATA, - M_WAITOK); - bcopy(ii->ii_authkey, tdbp->tdb_amxkey, tdbp->tdb_amxkeylen); + if (tdbp->tdb_authalgxform) + { + tdbp->tdb_amxkeylen = ii->ii_authkeylen; + MALLOC(tdbp->tdb_amxkey, u_int8_t *, tdbp->tdb_amxkeylen, M_XDATA, + M_WAITOK); + bcopy(ii->ii_authkey, tdbp->tdb_amxkey, tdbp->tdb_amxkeylen); + } - tdbp->tdb_emxkeylen = ii->ii_enckeylen; - MALLOC(tdbp->tdb_emxkey, u_int8_t *, tdbp->tdb_emxkeylen, M_XDATA, - M_WAITOK); - bcopy(ii->ii_enckey, tdbp->tdb_emxkey, tdbp->tdb_emxkeylen); + if (tdbp->tdb_encalgxform) + { + tdbp->tdb_emxkeylen = ii->ii_enckeylen; + MALLOC(tdbp->tdb_emxkey, u_int8_t *, tdbp->tdb_emxkeylen, M_XDATA, + M_WAITOK); + bcopy(ii->ii_enckey, tdbp->tdb_emxkey, tdbp->tdb_emxkeylen); + } + + /* Initialize crypto session */ + if (tdbp->tdb_encalgxform) + { + bzero(&crie, sizeof(crie)); + + crie.cri_alg = tdbp->tdb_encalgxform->type; - if (txform->setkey) - txform->setkey(&tdbp->tdb_key, ii->ii_enckey, ii->ii_enckeylen); + if (tdbp->tdb_authalgxform) + crie.cri_next = &cria; + else + crie.cri_next = NULL; + + crie.cri_klen = ii->ii_enckeylen * 8; + crie.cri_key = ii->ii_enckey; + /* XXX Rounds ? */ + } - if (thash) + if (tdbp->tdb_authalgxform) { - /* Precompute the I and O pads of the HMAC */ - for (i = 0; i < ii->ii_authkeylen; i++) - ii->ii_authkey[i] ^= HMAC_IPAD_VAL; - - MALLOC(tdbp->tdb_ictx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK); - bzero(tdbp->tdb_ictx, thash->ctxsize); - thash->Init(tdbp->tdb_ictx); - thash->Update(tdbp->tdb_ictx, ii->ii_authkey, ii->ii_authkeylen); - thash->Update(tdbp->tdb_ictx, hmac_ipad_buffer, - HMAC_BLOCK_LEN - ii->ii_authkeylen); - - for (i = 0; i < ii->ii_authkeylen; i++) - ii->ii_authkey[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL); - - MALLOC(tdbp->tdb_octx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK); - bzero(tdbp->tdb_octx, thash->ctxsize); - thash->Init(tdbp->tdb_octx); - thash->Update(tdbp->tdb_octx, ii->ii_authkey, ii->ii_authkeylen); - thash->Update(tdbp->tdb_octx, hmac_opad_buffer, - HMAC_BLOCK_LEN - ii->ii_authkeylen); + bzero(&cria, sizeof(cria)); + + cria.cri_alg = tdbp->tdb_authalgxform->type; + cria.cri_next = NULL; + cria.cri_klen = ii->ii_authkeylen * 8; + cria.cri_key = ii->ii_authkey; } - return 0; + return crypto_newsession(&tdbp->tdb_cryptoid, + (tdbp->tdb_encalgxform ? &crie : &cria)); } +/* + * Paranoia. + */ int esp_zeroize(struct tdb *tdbp) { + int err; + if (tdbp->tdb_amxkey) { bzero(tdbp->tdb_amxkey, tdbp->tdb_amxkeylen); @@ -256,69 +264,54 @@ esp_zeroize(struct tdb *tdbp) tdbp->tdb_emxkey = NULL; } - if (tdbp->tdb_key && tdbp->tdb_encalgxform && - tdbp->tdb_encalgxform->zerokey) - tdbp->tdb_encalgxform->zerokey(&tdbp->tdb_key); - - if (tdbp->tdb_ictx) - { - if (tdbp->tdb_authalgxform) - bzero(tdbp->tdb_ictx, tdbp->tdb_authalgxform->ctxsize); - FREE(tdbp->tdb_ictx, M_XDATA); - tdbp->tdb_ictx = NULL; - } - - if (tdbp->tdb_octx) - { - if (tdbp->tdb_authalgxform) - bzero(tdbp->tdb_octx, tdbp->tdb_authalgxform->ctxsize); - FREE(tdbp->tdb_octx, M_XDATA); - tdbp->tdb_octx = NULL; - } - - return 0; + err = crypto_freesession(tdbp->tdb_cryptoid); + tdbp->tdb_cryptoid = 0; + return err; } #define MAXBUFSIZ (AH_ALEN_MAX > ESP_MAX_IVS ? AH_ALEN_MAX : ESP_MAX_IVS) -struct mbuf * +/* + * ESP input processing, called (eventually) through the protocol switch. + */ +int esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) { struct auth_hash *esph = (struct auth_hash *) tdb->tdb_authalgxform; struct enc_xform *espx = (struct enc_xform *) tdb->tdb_encalgxform; - int oplen, plen, alen, ilen, i, blks, rest, count, off, roff, hlen; - u_char iv[MAXBUFSIZ], niv[MAXBUFSIZ], blk[ESP_MAX_BLKS], *lblk; - u_char *idat, *odat, *ivp, *ivn; - struct mbuf *mi, *mo, *m1; - union authctx ctx; + int plen, alen, hlen; u_int32_t btsx; + struct cryptodesc *crde = NULL, *crda = NULL; + struct cryptop *crp; + /* Determine the ESP header length */ if (tdb->tdb_flags & TDBF_NOREPLAY) hlen = sizeof(u_int32_t) + tdb->tdb_ivlen; /* "old" ESP */ else hlen = 2 * sizeof(u_int32_t) + tdb->tdb_ivlen; /* "new" ESP */ - blks = espx->blocksize; - if (esph) alen = AH_HMAC_HASHLEN; else alen = 0; - /* Skip the IP header, IP options, SPI, IV, and any Replay and Auth Data */ - plen = m->m_pkthdr.len - (skip + hlen + alen); - if ((plen & (blks - 1)) || (plen <= 0)) + if (espx) { - DPRINTF(("esp_input(): payload not a multiple of %d octets, SA %s/%08x\n", blks, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_badilen++; - m_freem(m); - return NULL; + /* + * Verify payload length is multiple of encryption algorithm + * block size. + */ + plen = m->m_pkthdr.len - (skip + hlen + alen); + if ((plen & (espx->blocksize - 1)) || (plen <= 0)) + { + DPRINTF(("esp_input(): payload not a multiple of %d octets, SA %s/%08x\n", espx->blocksize, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + espstat.esps_badilen++; + m_freem(m); + return EINVAL; + } } - /* Auth covers SPI + SN + IV */ - oplen = plen + hlen; - /* Replay window checking, if appropriate */ if ((tdb->tdb_wnd > 0) && (!(tdb->tdb_flags & TDBF_NOREPLAY))) { @@ -336,20 +329,20 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) DPRINTF(("esp_input(): replay counter wrapped for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_wrap++; m_freem(m); - return NULL; + return EACCES; case 2: case 3: DPRINTF(("esp_input(): duplicate packet received in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_replay++; m_freem(m); - return NULL; + return EACCES; default: DPRINTF(("esp_input(): bogus value from checkreplaywindow32() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_replay++; m_freem(m); - return NULL; + return EACCES; } } @@ -364,10 +357,10 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); tdb_delete(tdb, 0, TDBEXP_TIMEOUT); m_freem(m); - return NULL; + return ENXIO; } - /* Notify on expiration */ + /* Notify on soft expiration */ if ((tdb->tdb_flags & TDBF_SOFT_BYTES) && (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) { @@ -375,114 +368,191 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */ } - /* - * Skip forward to the beginning of the ESP header. If we run out - * of mbufs in the process, the check inside the following while() - * loop will catch it. - */ - for (mo = m, i = 0; mo && i + mo->m_len <= skip; mo = mo->m_next) - i += mo->m_len; - - off = skip - i; - - /* Preserve these for later processing */ - roff = off; - m1 = mo; + /* Get crypto descriptors */ + crp = crypto_getreq(esph && espx ? 2 : 1); + if (crp == NULL) + { + m_freem(m); + DPRINTF(("esp_input(): failed to acquire crypto descriptors\n")); + espstat.esps_crypto++; + return ENOBUFS; + } - /* Verify the authenticator, if applicable */ if (esph) { - bcopy(tdb->tdb_ictx, &ctx, esph->ctxsize); + crda = crp->crp_desc; + crde = crda->crd_next; - /* Copy the authentication data */ - m_copydata(m, m->m_pkthdr.len - alen, alen, iv); + /* Authentication descriptor */ + crda->crd_skip = skip; + crda->crd_len = m->m_pkthdr.len - (skip + alen); + crda->crd_inject = m->m_pkthdr.len - alen; - /* Compute authenticator over the mbuf chain */ - while (oplen > 0) - { - if (mo == NULL) - { - DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_hdrops++; - m_freem(m); - return NULL; - } + crda->crd_alg = esph->type; + crda->crd_key = tdb->tdb_amxkey; + crda->crd_klen = tdb->tdb_amxkeylen * 8; + } + else + crde = crp->crp_desc; - count = min(mo->m_len - off, oplen); - esph->Update(&ctx, mtod(mo, unsigned char *) + off, count); - oplen -= count; - off = 0; - mo = mo->m_next; - } + tdb->tdb_ref++; - esph->Final(niv, &ctx); - bcopy(tdb->tdb_octx, &ctx, esph->ctxsize); - esph->Update(&ctx, niv, esph->hashsize); - esph->Final(niv, &ctx); + /* Crypto operation descriptor */ + crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ + crp->crp_flags = CRYPTO_F_IMBUF; + crp->crp_buf = (caddr_t) m; + crp->crp_callback = (int (*) (struct cryptop *)) esp_input_cb; + crp->crp_sid = tdb->tdb_cryptoid; - /* Verify */ - if (bcmp(niv, iv, AH_HMAC_HASHLEN)) - { - DPRINTF(("esp_input(): authentication failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_badauth++; - m_freem(m); - return NULL; - } + /* These are passed as-is to the callback */ + crp->crp_opaque1 = (caddr_t) tdb; + crp->crp_opaque2 = (caddr_t) skip; + crp->crp_opaque3 = (caddr_t) protoff; + + /* Decryption descriptor */ + if (espx) + { + crde->crd_skip = skip + hlen; + crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen); + crde->crd_inject = skip + hlen - tdb->tdb_ivlen; + if (tdb->tdb_flags & TDBF_HALFIV) + crde->crd_flags |= CRD_F_HALFIV; + + crde->crd_alg = espx->type; + crde->crd_key = tdb->tdb_emxkey; + crde->crd_klen = tdb->tdb_emxkeylen * 8; + /* XXX Rounds ? */ } - oplen = plen; + MALLOC(crp->crp_opaque4, caddr_t, alen, M_XDATA, M_DONTWAIT); + if (crp->crp_opaque4 == 0) + { + m_freem(m); + crypto_freereq(crp); + DPRINTF(("esp_input(): failed to allocate auth array\n")); + espstat.esps_crypto++; + return ENOBUFS; + } - /* Find beginning of encrypted data (actually, the IV) */ - mi = m1; - ilen = mi->m_len - roff - sizeof(u_int32_t); - if (!(tdb->tdb_flags & TDBF_NOREPLAY)) - ilen -= sizeof(u_int32_t); - while (ilen <= 0) + /* Copy the authenticator */ + m_copydata(m, m->m_pkthdr.len - alen, alen, crp->crp_opaque4); + + return crypto_dispatch(crp); +} + +/* + * ESP input callback, called directly by the crypto driver. + */ +int +esp_input_cb(void *op) +{ + u_int8_t lastthree[3], aalg[AH_HMAC_HASHLEN]; + int hlen, roff, skip, protoff, error; + struct mbuf *m1, *mo, *m; + struct cryptodesc *crd; + struct auth_hash *esph; + struct enc_xform *espx; + struct cryptop *crp; + struct tdb *tdb; + + crp = (struct cryptop *) op; + crd = crp->crp_desc; + tdb = (struct tdb *) crp->crp_opaque1; + esph = (struct auth_hash *) tdb->tdb_authalgxform; + espx = (struct enc_xform *) tdb->tdb_encalgxform; + skip = (int) crp->crp_opaque2; + protoff = (int) crp->crp_opaque3; + m = (struct mbuf *) crp->crp_buf; + + tdb->tdb_ref--; + + /* Check for crypto errors */ + if (crp->crp_etype) { - mi = mi->m_next; - if (mi == NULL) + /* Reset the session ID */ + if (tdb->tdb_cryptoid != 0) + tdb->tdb_cryptoid = crp->crp_sid; + + if (crp->crp_etype == EAGAIN) { - DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_hdrops++; - m_freem(m); - return NULL; + tdb->tdb_ref++; + return crypto_dispatch(crp); } - ilen += mi->m_len; + espstat.esps_noxform++; + DPRINTF(("esp_input_cb(): crypto error %d\n", crp->crp_etype)); + error = crp->crp_etype; + goto baddone; } - idat = mtod(mi, unsigned char *) + (mi->m_len - ilen); - m_copydata(mi, mi->m_len - ilen, tdb->tdb_ivlen, iv); + /* Shouldn't happen... */ + if (!m) + { + espstat.esps_crypto++; + DPRINTF(("esp_input_cb(): bogus returned buffer from crypto\n")); + error = EINVAL; + goto baddone; + } - /* If we're doing half-IV, generate full IV */ - if (tdb->tdb_flags & TDBF_HALFIV) + /* + * Check that the TDB is still valid -- not really an error, but + * we need to handle it as such. It may happen if the TDB expired + * or was deleted while there was a pending request in the crypto + * queue. + */ + if (tdb->tdb_flags & TDBF_INVALID) { - for (i = 0; i < tdb->tdb_ivlen; i++) - iv[tdb->tdb_ivlen + i] = ~iv[i]; + espstat.esps_invalid++; + tdb_delete(tdb, 0, 0); + error = ENXIO; + DPRINTF(("esp_input_cb(): TDB expired while processing crypto\n")); + goto baddone; } - /* Now skip over the IV */ - ilen -= tdb->tdb_ivlen; - while (ilen <= 0) + /* If authentication was performed, check now */ + if (esph) { - mi = mi->m_next; - if (mi == NULL) + /* Copy the authenticator from the packet */ + m_copydata(m, m->m_pkthdr.len - esph->authsize, + esph->authsize, aalg); + + /* Verify authenticator */ + if (bcmp(crp->crp_opaque4, aalg, esph->authsize)) { - DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_hdrops++; - m_freem(m); - return NULL; + DPRINTF(("esp_input_cb(): authentication failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + espstat.esps_badauth++; + error = EACCES; + goto baddone; } - ilen += mi->m_len; + /* Remove trailing authenticator */ + m_adj(m, -(esph->authsize)); + + /* We have to manually free this */ + FREE(crp->crp_opaque4, M_XDATA); } - /* - * Remove the ESP header and IV from the mbuf. - */ + /* Release the crypto descriptors */ + crypto_freereq(crp); + + /* Determine the ESP header length */ + if (tdb->tdb_flags & TDBF_NOREPLAY) + hlen = sizeof(u_int32_t) + tdb->tdb_ivlen; /* "old" ESP */ + else + hlen = 2 * sizeof(u_int32_t) + tdb->tdb_ivlen; /* "new" ESP */ + + /* Find beginning of ESP header */ + m1 = m_getptr(m, skip, &roff); + if (m1 == NULL) + { + DPRINTF(("esp_input_cb(): bad mbuf chain, SA %s/%08x\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + espstat.esps_hdrops++; + m_freem(m); + return EINVAL; + } + + /* Remove the ESP header and IV from the mbuf. */ if (roff == 0) { /* The ESP header was conveniently at the beginning of the mbuf */ @@ -494,8 +564,8 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) if (roff + hlen >= m1->m_len) { /* - * Part or all of the ESP header is at the end of this mbuf, so first - * let's remove the remainder of the ESP header from the + * Part or all of the ESP header is at the end of this mbuf, so + * first let's remove the remainder of the ESP header from the * beginning of the remainder of the mbuf chain, if any. */ if (roff + hlen > m1->m_len) @@ -532,166 +602,67 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) m->m_pkthdr.len -= hlen; } - /* Point to the encrypted data */ - idat = mtod(mi, unsigned char *) + (mi->m_len - ilen); - - /* - * At this point: - * plen is # of encapsulated payload octets - * ilen is # of octets left in this mbuf - * idat is first encapsulated payload octed in this mbuf - * same for olen and odat - * ivp points to the IV, ivn buffers the next IV. - * mi points to the first mbuf - * - * From now on until the end of the mbuf chain: - * . move the next eight octets of the chain into ivn - * . decrypt idat and xor with ivp - * . swap ivp and ivn. - * . repeat - */ + /* Save the last three bytes of decrypted data */ + m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree); - ivp = iv; - ivn = niv; - rest = ilen % blks; - while (plen > 0) /* while not done */ + /* Verify pad length */ + if (lastthree[1] + 2 > m->m_pkthdr.len - skip - hlen) { - if (ilen < blks) - { - if (rest) - { - bcopy(idat, blk, rest); - odat = idat; - } - - do { - mi = (mo = mi)->m_next; - if (mi == NULL) - { - DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_hdrops++; - m_freem(m); - return NULL; - } - } while (mi->m_len == 0); - - if (mi->m_len < blks - rest) - { - if ((mi = m_pullup(mi, blks - rest)) == NULL) - { - DPRINTF(("esp_input(): m_pullup() failed, SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - m_freem(m); - espstat.esps_hdrops++; - return NULL; - } - /* - * m_pullup was not called at the beginning of the chain - * but might return a new mbuf, link it into the chain. - */ - mo->m_next = mi; - } - - ilen = mi->m_len; - idat = mtod(mi, u_char *); - - if (rest) - { - bcopy(idat, blk + rest, blks - rest); - bcopy(blk, ivn, blks); - - espx->decrypt(tdb, blk); - - for (i = 0; i < blks; i++) - blk[i] ^= ivp[i]; - - ivp = ivn; - ivn = (ivp == iv) ? niv : iv; - - bcopy(blk, odat, rest); - bcopy(blk + rest, idat, blks - rest); - - lblk = blk; /* last block touched */ - - idat += blks - rest; - ilen -= blks - rest; - plen -= blks; - } - - rest = ilen % blks; - } - - while (ilen >= blks && plen > 0) - { - bcopy(idat, ivn, blks); - - espx->decrypt(tdb, idat); - - for (i = 0; i < blks; i++) - idat[i] ^= ivp[i]; - - ivp = ivn; - ivn = (ivp == iv) ? niv : iv; - - lblk = idat; /* last block touched */ - idat += blks; - - ilen -= blks; - plen -= blks; - } - } - - /* Save last block (end of padding), if it was in-place decrypted */ - if (lblk != blk) - bcopy(lblk, blk, blks); - - /* - * Now, the entire chain has been decrypted. As a side effect, - * blk[blks - 1] contains the next protocol, and blk[blks - 2] contains - * the amount of padding the original chain had. Chop off the - * appropriate parts of the chain, and return. - */ - - if (blk[blks - 2] + 2 + alen > m->m_pkthdr.len - skip - hlen) - { - DPRINTF(("esp_input(): invalid padding length %d for packet in SA %s/%08x\n", blk[blks - 2], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + DPRINTF(("esp_input_cb(): invalid padding length %d for packet in SA %s/%08x\n", lastthree[2], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_badilen++; m_freem(m); - return NULL; + return EINVAL; } - /* Verify correct decryption by checking the last padding bytes. */ + /* Verify correct decryption by checking the last padding bytes */ if (!(tdb->tdb_flags & TDBF_RANDOMPADDING)) { - if ((blk[blks - 2] != blk[blks - 3]) && (blk[blks - 2] != 0)) + if ((lastthree[1] != lastthree[0]) && (lastthree[1] != 0)) { DPRINTF(("esp_input(): decryption failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_badenc++; m_freem(m); - return NULL; + return EINVAL; } } - /* Trim the mbuf chain to remove the trailing authenticator */ - m_adj(m, - blk[blks - 2] - 2 - alen); + /* Trim the mbuf chain to remove the trailing authenticator and padding */ + m_adj(m, -(lastthree[1] + 2)); /* Restore the Next Protocol field */ - m_copyback(m, protoff, 1, &blk[blks - 1]); + m_copyback(m, protoff, sizeof(u_int8_t), lastthree + 2); + + /* Back to generic IPsec input processing */ + return ipsec_common_input_cb(m, tdb, skip, protoff); + + baddone: + if (m) + m_freem(m); - return m; + /* We have to manually free this */ + if (crp && crp->crp_opaque4) + FREE(crp->crp_opaque4, M_XDATA); + + crypto_freereq(crp); + + return error; } +/* + * ESP output routine, called by ipsp_process_packet(). + */ int esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, - int protoff) + int protoff) { struct enc_xform *espx = (struct enc_xform *) tdb->tdb_encalgxform; struct auth_hash *esph = (struct auth_hash *) tdb->tdb_authalgxform; - u_char iv[ESP_MAX_IVS], blk[ESP_MAX_BLKS], auth[AH_ALEN_MAX]; - int i, ilen, hlen, rlen, plen, padding, rest, blks, alen; + int ilen, hlen, rlen, plen, padding, blks, alen; struct mbuf *mi, *mo = (struct mbuf *) NULL; - u_char *pad, *idat, *odat, *ivp; - union authctx ctx; + unsigned char *pad; + + struct cryptodesc *crde = NULL, *crda = NULL; + struct cryptop *crp; #if NBPFILTER > 0 { @@ -727,8 +698,12 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, else hlen = 2 * sizeof(u_int32_t) + tdb->tdb_ivlen; - blks = espx->blocksize; rlen = m->m_pkthdr.len - skip; /* Raw payload length */ + if (espx) + blks = espx->blocksize; + else + blks = 4; /* If no encryption is used, we have to be 4-byte aligned */ + padding = ((blks - ((rlen + 2) % blks)) % blks) + 2; plen = rlen + padding; /* Padded payload length */ @@ -743,39 +718,50 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, if ((!(tdb->tdb_flags & TDBF_NOREPLAY)) && (tdb->tdb_rpl == 0) && (tdb->tdb_wnd > 0)) { - DPRINTF(("esp_output(): SA %s/%0x8 should have expired\n", + DPRINTF(("esp_output(): SA %s/%08x should have expired\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); m_freem(m); espstat.esps_wrap++; - return ENOBUFS; + return EACCES; } + switch (tdb->tdb_dst.sa.sa_family) + { #ifdef INET - /* Check for IPv6 maximum packet size violations */ - if (tdb->tdb_dst.sa.sa_family == AF_INET) - if (skip + hlen + rlen + padding + alen > IP_MAXPACKET) - { - DPRINTF(("esp_output(): packet in SA %s/%0x8 got too big\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - m_freem(m); - espstat.esps_toobig++; - return EMSGSIZE; - } + case AF_INET: + /* Check for IP maximum packet size violations */ + if (skip + hlen + rlen + padding + alen > IP_MAXPACKET) + { + DPRINTF(("esp_output(): packet in SA %s/%08x got too big\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + m_freem(m); + espstat.esps_toobig++; + return EMSGSIZE; + } + break; #endif /* INET */ #ifdef INET6 - /* Check for IPv6 maximum packet size violations */ - if (tdb->tdb_dst.sa.sa_family == AF_INET6) - if (skip + hlen + rlen + padding + alen > IPV6_MAXPACKET) - { - DPRINTF(("esp_output(): packet in SA %s/%0x8 got too big\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - m_freem(m); - espstat.esps_toobig++; - return EMSGSIZE; - } + case AF_INET6: + /* Check for IPv6 maximum packet size violations */ + if (skip + hlen + rlen + padding + alen > IPV6_MAXPACKET) + { + DPRINTF(("esp_output(): packet in SA %s/%08x got too big\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + m_freem(m); + espstat.esps_toobig++; + return EMSGSIZE; + } + break; #endif /* INET6 */ + default: + DPRINTF(("esp_output(): unknown/unsupported protocol family %d, SA %s/%08x\n", tdb->tdb_dst.sa.sa_family, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + m_freem(m); + espstat.esps_nopf++; + return EPFNOSUPPORT; + } + /* Update the counters */ tdb->tdb_cur_bytes += m->m_pkthdr.len - skip; espstat.esps_obytes += m->m_pkthdr.len - skip; @@ -800,7 +786,7 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, /* * Loop through mbuf chain; if we find an M_EXT mbuf with - * more than one reference, replace the rest of the chain. + * more than one reference, replace the rest of the chain. */ mi = m; while (mi != NULL && @@ -833,37 +819,29 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, } /* Inject ESP header */ - mo = m_inject(m, skip, hlen, M_WAITOK); + mo = m_inject(m, skip, hlen, M_DONTWAIT); if (mo == NULL) { - DPRINTF(("esp_output(): failed to inject ESP header for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); + DPRINTF(("esp_output(): failed to inject ESP header for SA %s/%08x\n", + ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); m_freem(m); espstat.esps_wrap++; return ENOBUFS; } /* Initialize ESP header */ - m_copyback(mo, 0, sizeof(u_int32_t), (caddr_t) &tdb->tdb_spi); + bcopy((caddr_t) &tdb->tdb_spi, mtod(mo, caddr_t), sizeof(u_int32_t)); if (!(tdb->tdb_flags & TDBF_NOREPLAY)) { u_int32_t replay = htonl(tdb->tdb_rpl++); - m_copyback(mo, sizeof(u_int32_t), sizeof(u_int32_t), - (caddr_t) &replay); + bcopy((caddr_t) &replay, mtod(mo, caddr_t) + sizeof(u_int32_t), + sizeof(u_int32_t)); } - /* Initialize IV (cook the half-IV if applicable) */ - bcopy(tdb->tdb_iv, iv, tdb->tdb_ivlen); - if (tdb->tdb_flags & TDBF_HALFIV) - { - for (i = 0; i < tdb->tdb_ivlen; i++) - iv[i + tdb->tdb_ivlen] = ~iv[i]; - } - - /* Copy IV in ESP header */ - m_copyback(mo, hlen - tdb->tdb_ivlen, tdb->tdb_ivlen, - (caddr_t) &tdb->tdb_iv); - - /* Add padding */ + /* + * Add padding -- better to do it ourselves than use the crypto engine, + * although if/when we support compression, we'd have to do that. + */ pad = (u_char *) m_pad(m, padding + alen, tdb->tdb_flags & TDBF_RANDOMPADDING); if (pad == NULL) @@ -882,145 +860,142 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, /* Fix padding length and Next Protocol in padding itself */ pad[padding - 2] = padding - 2; - m_copydata(m, protoff, 1, &pad[padding - 1]); + m_copydata(m, protoff, sizeof(u_int8_t), pad + padding - 1); /* Fix Next Protocol in IPv4/IPv6 header */ ilen = IPPROTO_ESP; m_copyback(m, protoff, sizeof(u_int8_t), (u_char *) &ilen); - mi = mo; - - /* If it's just the ESP header, just skip to the next mbuf */ - if (mi->m_len == hlen) + /* Get crypto descriptors */ + crp = crypto_getreq(esph && espx ? 2 : 0); + if (crp == NULL) { - mi = mi->m_next; - ilen = mi->m_len; - idat = mtod(mi, u_char *); - } - else - { /* There's data at the end of this mbuf, skip over ESP header */ - ilen = mi->m_len - hlen; - idat = mtod(mi, u_char *) + hlen; + m_freem(m); + DPRINTF(("esp_output(): failed to acquire crypto descriptors\n")); + espstat.esps_crypto++; + return ENOBUFS; } - /* Authenticate the ESP header if applicable */ - if (esph) + if (espx) { - bcopy(tdb->tdb_ictx, &ctx, esph->ctxsize); - esph->Update(&ctx, mtod(mo, unsigned char *), hlen); + crde = crp->crp_desc; + crda = crde->crd_next; + + /* Encryption descriptor */ + crde->crd_skip = skip + hlen; + crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen); + crde->crd_flags = CRD_F_ENCRYPT; + if (tdb->tdb_flags & TDBF_HALFIV) + crde->crd_flags |= CRD_F_HALFIV; + crde->crd_inject = skip + hlen - tdb->tdb_ivlen; + + /* Encryption operation */ + crde->crd_alg = espx->type; + crde->crd_key = tdb->tdb_emxkey; + crde->crd_klen = tdb->tdb_emxkeylen * 8; + /* XXX Rounds ? */ } + else + crda = crp->crp_desc; - /* Encrypt the payload */ - ivp = iv; - rest = ilen % blks; - while (plen > 0) /* while not done */ - { - if (ilen < blks) - { - if (rest) - { - if (ivp == blk) - { - bcopy(blk, iv, blks); - ivp = iv; - } - - bcopy(idat, blk, rest); - odat = idat; - } + tdb->tdb_ref++; - do { - mi = (mo = mi)->m_next; - if (mi == NULL) - { - DPRINTF(("esp_output(): bad mbuf chain, SA %s/%08x\n", - ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - espstat.esps_hdrops++; - m_freem(m); - return EINVAL; - } - } while (mi->m_len == 0); - - if (mi->m_len < blks - rest) - { - if ((mi = m_pullup(mi, blks - rest)) == NULL) - { - DPRINTF(("esp_output(): m_pullup() failed, SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); - m_freem(m); - espstat.esps_hdrops++; - return ENOBUFS; - } - /* - * m_pullup was not called at the beginning of the chain - * but might return a new mbuf, link it into the chain. - */ - mo->m_next = mi; - } - - ilen = mi->m_len; - idat = mtod(mi, u_char *); + /* Crypto operation descriptor */ + crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ + crp->crp_flags = CRYPTO_F_IMBUF; + crp->crp_buf = (caddr_t) m; + crp->crp_callback = (int (*) (struct cryptop *)) esp_output_cb; + crp->crp_opaque1 = (caddr_t) tdb; + crp->crp_sid = tdb->tdb_cryptoid; - if (rest) - { - bcopy(idat, blk + rest, blks - rest); - - for (i = 0; i < blks; i++) - blk[i] ^= ivp[i]; + if (esph) + { + /* Authentication descriptor */ + crda->crd_skip = skip; + crda->crd_len = m->m_pkthdr.len - (skip + alen); + crda->crd_inject = m->m_pkthdr.len - alen; + + /* Authentication operation */ + crda->crd_alg = esph->type; + crda->crd_key = tdb->tdb_amxkey; + crda->crd_klen = tdb->tdb_amxkeylen * 8; + } - espx->encrypt(tdb, blk); + return crypto_dispatch(crp); +} - if (esph) - esph->Update(&ctx, blk, blks); +/* + * ESP output callback, called directly by the crypto driver. + */ +int +esp_output_cb(void *op) +{ + struct cryptop *crp = (struct cryptop *) op; + struct tdb *tdb; + struct mbuf *m; + int error; - ivp = blk; + tdb = (struct tdb *) crp->crp_opaque1; + m = (struct mbuf *) crp->crp_buf; - bcopy(blk, odat, rest); - bcopy(blk + rest, idat, blks - rest); - - idat += blks - rest; - ilen -= blks - rest; - plen -= blks; - } + tdb->tdb_ref--; - rest = ilen % blks; - } + /* Check for crypto errors */ + if (crp->crp_etype) + { + /* Reset session ID */ + if (tdb->tdb_cryptoid != 0) + tdb->tdb_cryptoid = crp->crp_sid; - while (ilen >= blks && plen > 0) + if (crp->crp_etype == EAGAIN) { - for (i = 0; i < blks; i++) - idat[i] ^= ivp[i]; - - espx->encrypt(tdb, idat); - - if (esph) - esph->Update(&ctx, idat, blks); - - ivp = idat; - idat += blks; - - ilen -= blks; - plen -= blks; + tdb->tdb_ref++; + return crypto_dispatch(crp); } + + espstat.esps_noxform++; + DPRINTF(("esp_output_cb(): crypto error %d\n", crp->crp_etype)); + error = crp->crp_etype; + goto baddone; } - /* Put in authentication data */ - if (esph) + /* Shouldn't happen... */ + if (!m) { - esph->Final(auth, &ctx); - bcopy(tdb->tdb_octx, &ctx, esph->ctxsize); - esph->Update(&ctx, auth, esph->hashsize); - esph->Final(auth, &ctx); + espstat.esps_crypto++; + DPRINTF(("esp_output_cb(): bogus returned buffer from crypto\n")); + error = EINVAL; + goto baddone; + } - /* Copy the final authenticator -- cheat and use bcopy() again */ - bcopy(auth, pad + padding, alen); + /* + * Check that the TDB is still valid -- not really an error, but + * we need to handle it as such. It may happen if the TDB expired + * or was deleted while there was a pending request in the crypto + * queue. + */ + if (tdb->tdb_flags & TDBF_INVALID) + { + espstat.esps_invalid++; + tdb_delete(tdb, 0, 0); + error = ENXIO; + DPRINTF(("esp_output_cb(): TDB expired while processing crypto\n")); + goto baddone; } - - /* Save the last encrypted block, to be used as the next IV */ - bcopy(ivp, tdb->tdb_iv, tdb->tdb_ivlen); - *mp = m; + /* Release crypto descriptors */ + crypto_freereq(crp); - return 0; + /* Call the IPsec input callback */ + return ipsp_process_done(m, tdb); + + baddone: + if (m) + m_freem(m); + + crypto_freereq(crp); + + return error; } /* @@ -1057,6 +1032,7 @@ checkreplaywindow32(u_int32_t seq, u_int32_t initial, u_int32_t *lastseq, espstat.esps_wrap++; return 2; } + if ((*bitmap) & (((u_int32_t) 1) << diff)) { espstat.esps_replay++; @@ -1068,13 +1044,10 @@ checkreplaywindow32(u_int32_t seq, u_int32_t initial, u_int32_t *lastseq, } /* - * - * * m_pad(m, n) pads <m> with <n> bytes at the end. The packet header * length is updated, and a pointer to the first byte of the padding * (which is guaranteed to be all in one mbuf) is returned. The third * argument specifies whether we need randompadding or not. - * */ caddr_t diff --git a/sys/netinet/ip_esp.h b/sys/netinet/ip_esp.h index 2438f1a8c8d..f7cae5bbe6c 100644 --- a/sys/netinet/ip_esp.h +++ b/sys/netinet/ip_esp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_esp.h,v 1.31 2000/01/27 08:09:10 angelos Exp $ */ +/* $OpenBSD: ip_esp.h,v 1.32 2000/03/17 10:25:22 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -42,7 +42,7 @@ struct espstat { - u_int32_t esps_hdrops; /* packet shorter than header shows */ + u_int32_t esps_hdrops; /* Packet shorter than header shows */ u_int32_t esps_nopf; /* Protocol family not supported */ u_int32_t esps_notdb; u_int32_t esps_badkcr; @@ -55,11 +55,12 @@ struct espstat u_int32_t esps_replay; /* Possible packet replay detected */ u_int32_t esps_input; /* Input ESP packets */ u_int32_t esps_output; /* Output ESP packets */ - u_int32_t esps_invalid; /* Trying to use an invalid TDB */ - u_int64_t esps_ibytes; /* input bytes */ - u_int64_t esps_obytes; /* output bytes */ - u_int32_t esps_toobig; /* packet got larger than IP_MAXPACKET */ - u_int32_t esps_pdrops; /* packet blocked due to policy */ + u_int32_t esps_invalid; /* Trying to use an invalid TDB */ + u_int64_t esps_ibytes; /* Input bytes */ + u_int64_t esps_obytes; /* Output bytes */ + u_int32_t esps_toobig; /* Packet got larger than IP_MAXPACKET */ + u_int32_t esps_pdrops; /* Packet blocked due to policy */ + u_int32_t esps_crypto; /* Crypto processing failure */ }; /* diff --git a/sys/netinet/ip_ipsp.c b/sys/netinet/ip_ipsp.c index 97087d12935..db5ea0581f7 100644 --- a/sys/netinet/ip_ipsp.c +++ b/sys/netinet/ip_ipsp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ipsp.c,v 1.79 2000/02/09 04:19:19 itojun Exp $ */ +/* $OpenBSD: ip_ipsp.c,v 1.80 2000/03/17 10:25:22 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -60,6 +60,7 @@ #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/in_pcb.h> +#include <netinet/ip_var.h> #endif /* INET */ #ifdef INET6 @@ -74,6 +75,9 @@ #include <netinet/ip_ah.h> #include <netinet/ip_esp.h> +#include <crypto/crypto.h> +#include <crypto/xform.h> + #include <dev/rndvar.h> #ifdef DDB @@ -112,26 +116,6 @@ struct expclusterlist_head expclusterlist = TAILQ_HEAD_INITIALIZER(expclusterlist); struct explist_head explist = TAILQ_HEAD_INITIALIZER(explist); -u_int8_t hmac_ipad_buffer[64] = { - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 }; - -u_int8_t hmac_opad_buffer[64] = { - 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, - 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, - 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, - 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, - 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, - 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, - 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, - 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C }; - /* * This is the proper place to define the various encapsulation transforms. */ @@ -139,7 +123,7 @@ u_int8_t hmac_opad_buffer[64] = { struct xformsw xformsw[] = { { XF_IP4, 0, "IPv4 Simple Encapsulation", ipe4_attach, ipe4_init, ipe4_zeroize, - (struct mbuf * (*)(struct mbuf *, struct tdb *, int, int))ipe4_input, + (int (*)(struct mbuf *, struct tdb *, int, int))ipe4_input, ipip_output, }, { XF_AH, XFT_AUTH, "IPsec AH", ah_attach, ah_init, ah_zeroize, @@ -1121,6 +1105,7 @@ puttdb(struct tdb *tdbp) } tdbp->tdb_hnext = tdbh[hashval]; tdbh[hashval] = tdbp; + tdbp->tdb_ref++; tdb_count++; splx(s); } @@ -1181,7 +1166,7 @@ delete_flow(struct flow *flow, struct tdb *tdb, int ingress) void tdb_delete(struct tdb *tdbp, int delchain, int expflags) { - struct tdb *tdbpp; + struct tdb *tdbpp, *tdbpn; struct inpcb *inp; u_int32_t hashval = tdbp->tdb_sproto + tdbp->tdb_spi; int s; @@ -1211,12 +1196,15 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags) { tdbpp->tdb_hnext = tdbp->tdb_hnext; tdbpp = tdbp; + break; } + tdbp->tdb_hnext = NULL; + skip_hash: /* * If there was something before us in the chain pointing to us, - * make it point nowhere + * make it point nowhere. */ if ((tdbp->tdb_inext) && (tdbp->tdb_inext->tdb_onext == tdbp)) @@ -1224,16 +1212,20 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags) /* * If there was something after us in the chain pointing to us, - * make it point nowhere + * make it point nowhere. */ if ((tdbp->tdb_onext) && (tdbp->tdb_onext->tdb_inext == tdbp)) tdbp->tdb_onext->tdb_inext = NULL; - tdbpp = tdbp->tdb_onext; - + tdbpn = tdbp->tdb_onext; + tdbp->tdb_inext = tdbp->tdb_onext = NULL; + if (tdbp->tdb_xform) - (*(tdbp->tdb_xform->xf_zeroize))(tdbp); + { + (*(tdbp->tdb_xform->xf_zeroize))(tdbp); + tdbp->tdb_xform = NULL; + } while (tdbp->tdb_access) delete_flow(tdbp->tdb_access, tdbp, FLOW_INGRESS); @@ -1326,6 +1318,7 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags) TAILQ_REMOVE(&tdbpp->tdb_bind_in, tdbpp, tdb_bind_in_next); tdbpp->tdb_bind_out = NULL; } + /* Cleanup inp references */ for (inp = TAILQ_FIRST(&tdbp->tdb_inp); inp; inp = TAILQ_FIRST(&tdbp->tdb_inp)) @@ -1346,20 +1339,32 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags) } if (tdbp->tdb_srcid) - FREE(tdbp->tdb_srcid, M_XDATA); + { + FREE(tdbp->tdb_srcid, M_XDATA); + tdbp->tdb_srcid = NULL; + } if (tdbp->tdb_dstid) - FREE(tdbp->tdb_dstid, M_XDATA); + { + FREE(tdbp->tdb_dstid, M_XDATA); + tdbp->tdb_dstid = NULL; + } /* If we're deleting the bypass tdb, reset the variable. */ if (tdbp == tdb_bypass) tdb_bypass = NULL; - FREE(tdbp, M_TDB); - tdb_count--; + /* Don't always delete TDBs as they may be referenced by something else */ + if (--tdbp->tdb_ref <= 0) + { + FREE(tdbp, M_TDB); + tdb_count--; + } + else + tdbp->tdb_flags |= TDBF_INVALID; - if (delchain && tdbpp) - tdb_delete(tdbpp, delchain, expflags); + if (delchain && tdbpn) + tdb_delete(tdbpn, delchain, expflags); splx(s); } @@ -1520,6 +1525,11 @@ ipsp_kern(int off, char **bufp, int len) l += sprintf(buffer + l, ">\n"); } + l += sprintf(buffer + l, "\tCrypto ID: %qu\n", tdb->tdb_cryptoid); + + l += sprintf(buffer + l, "\tCurrently referenced %d time%s\n", + tdb->tdb_ref, tdb->tdb_ref == 1 ? "" : "s"); + if (tdb->tdb_xform) l += sprintf(buffer + l, "\txform = <%s>\n", tdb->tdb_xform->xf_name); @@ -1714,221 +1724,285 @@ ipsp_address(union sockaddr_union sa) * place. */ int -ipsp_process_packet(struct mbuf *m, struct mbuf **mp, struct tdb *tdb, int *af, - int tunalready) +ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready) { - int i, error, off; - struct tdb *t; + int i, off, error; + struct mbuf *mp; #ifdef INET struct ip *ip; #endif /* INET */ - #ifdef INET6 struct ip6_hdr *ip6; #endif /* INET6 */ - for (t = tdb; t != NULL; t = t->tdb_onext) - if ((t->tdb_sproto == IPPROTO_ESP && !esp_enable) || - (t->tdb_sproto == IPPROTO_AH && !ah_enable)) - { - DPRINTF(("ipsp_process_packet(): IPSec outbound packet dropped due to policy\n")); - m_freem(m); - return EHOSTUNREACH; - } + /* Check that the transform is allowed by the administrator */ + if ((tdb->tdb_sproto == IPPROTO_ESP && !esp_enable) || + (tdb->tdb_sproto == IPPROTO_AH && !ah_enable)) + { + DPRINTF(("ipsp_process_packet(): IPSec outbound packet dropped due to policy\n")); + m_freem(m); + return EHOSTUNREACH; + } - while (tdb && tdb->tdb_xform) + /* Sanity check */ + if (!tdb->tdb_xform) { - /* Check if the SPI is invalid */ - if (tdb->tdb_flags & TDBF_INVALID) - { - DPRINTF(("ipsp_process_packet(): attempt to use invalid SA %s/%08x/%u\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto)); - m_freem(m); - return ENXIO; - } + DPRINTF(("ipsp_process_packet(): uninitialized TDB\n")); + m_freem(m); + return EHOSTUNREACH; + } -#ifndef INET6 - /* Sanity check */ - if (tdb->tdb_dst.sa.sa_family != AF_INET) - { - DPRINTF(("ipsp_process_packet(): attempt to use SA %s/%08x/%u for protocol family %d\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto, tdb->tdb_dst.sa.sa_family)); - m_freem(m); - return ENXIO; - } + /* Check if the SPI is invalid */ + if (tdb->tdb_flags & TDBF_INVALID) + { + DPRINTF(("ipsp_process_packet(): attempt to use invalid SA %s/%08x/%u\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto)); + m_freem(m); + return ENXIO; + } + + /* Check that the network protocol is supported */ + switch (tdb->tdb_dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + break; #endif /* INET6 */ -#ifndef INET - /* Sanity check */ - if (tdb->tdb_dst.sa.sa_family != AF_INET6) - { + default: DPRINTF(("ipsp_process_packet(): attempt to use SA %s/%08x/%u for protocol family %d\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto, tdb->tdb_dst.sa.sa_family)); m_freem(m); return ENXIO; - } -#endif /* INET */ + } - /* Register first use, setup expiration timer */ - if (tdb->tdb_first_use == 0) - { - tdb->tdb_first_use = time.tv_sec; - tdb_expiration(tdb, TDBEXP_TIMEOUT); - } + /* Register first use if applicable, setup relevant expiration timer */ + if (tdb->tdb_first_use == 0) + { + tdb->tdb_first_use = time.tv_sec; + tdb_expiration(tdb, TDBEXP_TIMEOUT); + } - /* Check for tunneling if we don't have the first header in place */ - if (tunalready == 0) + /* + * Check for tunneling if we don't have the first header in place. + * When doing Ethernet-over-IP, we are handed an already-encapsulated + * frame, so we don't need to re-encapsulate. + */ + if (tunalready == 0) + { + /* + * If the target protocol family is different, we know we'll be + * doing tunneling. + */ + if (af == tdb->tdb_dst.sa.sa_family) { - if ((*af) == tdb->tdb_dst.sa.sa_family) - { #ifdef INET - if ((*af) == AF_INET) - i = sizeof(struct ip); + if (af == AF_INET) + i = sizeof(struct ip); #endif /* INET */ #ifdef INET6 - if ((*af) == AF_INET6) - i = sizeof(struct ip6_hdr); + if (af == AF_INET6) + i = sizeof(struct ip6_hdr); #endif /* INET6 */ - if (m->m_len < i) - { - if ((m = m_pullup(m, i)) == 0) - return ENOBUFS; - } + /* Bring the network header in the first mbuf */ + if (m->m_len < i) + { + if ((m = m_pullup(m, i)) == 0) + return ENOBUFS; + } #ifdef INET - ip = mtod(m, struct ip *); + ip = mtod(m, struct ip *); #endif /* INET */ #ifdef INET6 - ip6 = mtod(m, struct ip6_hdr *); + ip6 = mtod(m, struct ip6_hdr *); #endif /* INET6 */ - } + } - if ((tdb->tdb_dst.sa.sa_family != (*af)) || - ((tdb->tdb_flags & TDBF_TUNNELING) && - (tdb->tdb_xform->xf_type != XF_IP4)) || + /* Do the appropriate encapsulation, if necessary */ + if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */ + (tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling requested */ + (tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */ #ifdef INET - ((tdb->tdb_dst.sa.sa_family == AF_INET) && - (tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) && - (tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) || + ((tdb->tdb_dst.sa.sa_family == AF_INET) && + (tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) && + (tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) || #endif /* INET */ #ifdef INET6 - ((tdb->tdb_dst.sa.sa_family == AF_INET6) && - (!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) && - (!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr, - &ip6->ip6_dst))) || + ((tdb->tdb_dst.sa.sa_family == AF_INET6) && + (!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) && + (!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr, + &ip6->ip6_dst))) || #endif /* INET6 */ - 0) - { + 0) + { #ifdef INET - /* Fix IPv4 header checksum and length */ - if ((*af) == AF_INET) - { - ip->ip_len = htons(m->m_pkthdr.len); - ip->ip_sum = in_cksum(m, ip->ip_hl << 2); - } + /* Fix IPv4 header checksum and length */ + if (af == AF_INET) + { + if ((m = m_pullup(m, sizeof(struct ip))) == 0) + return ENOBUFS; + + ip = mtod(m, struct ip *); + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_sum = in_cksum(m, ip->ip_hl << 2); + } #endif /* INET */ #ifdef INET6 - /* Fix IPv6 header payload length */ - if ((*af) == AF_INET6) - ip6->ip6_plen = htons(m->m_pkthdr.len); + /* Fix IPv6 header payload length */ + if (af == AF_INET6) + { + if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == 0) + return ENOBUFS; + + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(m->m_pkthdr.len); + } #endif /* INET6 */ - /* Encapsulate -- the last two arguments are unused */ - error = ipip_output(m, tdb, mp, 0, 0); - if (((*mp) == NULL) && (!error)) - error = EFAULT; - if (error) + /* Encapsulate -- the last two arguments are unused */ + error = ipip_output(m, tdb, &mp, 0, 0); + if ((mp == NULL) && (!error)) + error = EFAULT; + if (error) + { + if (mp) { - if (*mp) - { - m_freem(*mp); - *mp = NULL; - } - return error; + m_freem(mp); + mp = NULL; } - *af = tdb->tdb_dst.sa.sa_family; - m = *mp; - *mp = NULL; + return error; } - } - else - { - tunalready = 0; - if (tdb->tdb_xform->xf_type == XF_IP4) - continue; + m = mp; + mp = NULL; } - + + /* We may be done with this TDB */ + if (tdb->tdb_xform->xf_type == XF_IP4) + return ipsp_process_done(m, tdb); + } + else + { + /* + * If this is just an IP-IP TDB and we're told there's already an + * encapsulation header, move on. + */ + if (tdb->tdb_xform->xf_type == XF_IP4) + return ipsp_process_done(m, tdb); + } + + /* Extract some information off the headers */ + switch (tdb->tdb_dst.sa.sa_family) + { #ifdef INET - if (tdb->tdb_dst.sa.sa_family == AF_INET) - { + case AF_INET: ip = mtod(m, struct ip *); i = ip->ip_hl << 2; off = offsetof(struct ip, ip_p); - - if (tdb->tdb_xform->xf_type == XF_IP4) - { - ip->ip_len = htons(m->m_pkthdr.len); - ip->ip_sum = in_cksum(m, i); - } - } + break; #endif /* INET */ #ifdef INET6 - if (tdb->tdb_dst.sa.sa_family == AF_INET6) - { + case AF_INET6: ip6 = mtod(m, struct ip6_hdr *); i = sizeof(struct ip6_hdr); off = offsetof(struct ip6_hdr, ip6_nxt); - ip6->ip6_plen = htons(m->m_pkthdr.len); - } + break; #endif /* INET6 */ - - error = (*(tdb->tdb_xform->xf_output))(m, tdb, mp, i, off); - if ((*mp) == NULL) - error = EFAULT; - if (error) - { - if (*mp) - m_freem(*mp); - return error; - } + } - m = *mp; - *mp = NULL; - tdb = tdb->tdb_onext; + /* Invoke the IPsec transform */ + return (*(tdb->tdb_xform->xf_output))(m, tdb, NULL, i, off); +} +/* + * Called by the IPsec output transform callbacks, to transmit the packet + * or do further processing, as necessary. + */ +int +ipsp_process_done(struct mbuf *m, struct tdb *tdb) +{ #ifdef INET - /* Fix the header length, for AH processing */ - if ((*af) == AF_INET) - { - ip = mtod(m, struct ip *); - ip->ip_len = htons(m->m_pkthdr.len); - } + struct ip *ip; #endif /* INET */ #ifdef INET6 - /* Fix the header length, for AH processing */ - if ((*af) == AF_INET6) - { - ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_plen = htons(m->m_pkthdr.len); - } + struct ip6_hdr *ip6; #endif /* INET6 */ - } -#ifdef INET - /* Fix checksum */ - if ((*af) == AF_INET) + switch (tdb->tdb_dst.sa.sa_family) { - ip = mtod(m, struct ip *); - ip->ip_sum = in_cksum(m, ip->ip_hl << 2); +#ifdef INET + case AF_INET: + /* Fix the header length, for AH processing */ + if (tdb->tdb_dst.sa.sa_family == AF_INET) + { + ip = mtod(m, struct ip *); + ip->ip_len = htons(m->m_pkthdr.len); + } + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + /* Fix the header length, for AH processing */ + if (tdb->tdb_dst.sa.sa_family == AF_INET6) + { + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(m->m_pkthdr.len); + } + break; +#endif /* INET6 */ + + default: + m_freem(m); + DPRINTF(("ipsp_process_done(): unknown protocol family (%d)\n", + tdb->tdb_dst.sa.sa_family)); + return ENXIO; } + + /* If there's another TDB to apply, do so. */ + if (tdb->tdb_onext) + return ipsp_process_packet(m, tdb->tdb_onext, + tdb->tdb_onext->tdb_dst.sa.sa_family, 0); + + /* + * If we're done with IPsec processing, transmit the packet using the + * appropriate network protocol (IP or IPv6). + */ + switch (tdb->tdb_dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + NTOHS(ip->ip_len); + NTOHS(ip->ip_off); + ip->ip_sum = in_cksum(m, ip->ip_hl << 2); /* Fix checksum */ + + return ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, + NULL, NULL); #endif /* INET */ - *mp = m; - return 0; +#ifdef INET6 + case AF_INET6: + ip6 = mtod(m, struct ip6_hdr *); + NTOHS(ip6->ip6_plen); + + /* XXX ip6_output() has to honor those two flags... */ + return ip6_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, + NULL, NULL); +#endif /* INET6 */ + } + + /* Not reached */ + return EINVAL; } diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h index 7c3feb7d986..5695fc19692 100644 --- a/sys/netinet/ip_ipsp.h +++ b/sys/netinet/ip_ipsp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ipsp.h,v 1.61 2000/02/28 23:13:07 deraadt Exp $ */ +/* $OpenBSD: ip_ipsp.h,v 1.62 2000/03/17 10:25:22 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -47,12 +47,6 @@ #include <sys/types.h> #include <sys/queue.h> #include <netinet/in.h> -#include <sys/md5k.h> -#include <crypto/sha1.h> -#include <crypto/rmd160.h> -#include <crypto/blf.h> -#include <crypto/cast.h> -#include <crypto/skipjack.h> union sockaddr_union { @@ -69,33 +63,10 @@ union sockaddr_union #define SHA1HMAC96_KEYSIZE 20 #define RIPEMD160HMAC96_KEYSIZE 20 -/* IV lengths */ -#define ESP_DES_IVS 8 -#define ESP_3DES_IVS 8 -#define ESP_BLF_IVS 8 -#define ESP_CAST_IVS 8 -#define ESP_SKIPJACK_IVS 8 -#define ESP_MAX_IVS 8 /* Keep updated */ - -/* Block sizes -- it is assumed that they're powers of 2 */ -#define ESP_DES_BLKS 8 -#define ESP_3DES_BLKS 8 -#define ESP_BLF_BLKS 8 -#define ESP_CAST_BLKS 8 -#define ESP_SKIPJACK_BLKS 8 -#define ESP_MAX_BLKS 8 /* Keep updated */ - -#define HMAC_BLOCK_LEN 64 - #define AH_HMAC_HASHLEN 12 /* 96 bits of authenticator */ #define AH_HMAC_RPLENGTH 4 /* 32 bits of replay counter */ #define AH_HMAC_INITIAL_RPL 1 /* Replay counter initial value */ -/* HMAC definitions */ -#define HMAC_IPAD_VAL 0x36 -#define HMAC_OPAD_VAL 0x5C -#define HMAC_BLOCK_LEN 64 - /* Authenticator lengths */ #define AH_MD5_ALEN 16 #define AH_SHA1_ALEN 20 @@ -298,7 +269,9 @@ struct tdb /* tunnel descriptor block */ u_int64_t tdb_soft_first_use; /* Soft warning */ u_int64_t tdb_exp_first_use; /* Expire if tdb_first_use + * tdb_exp_first_use <= curtime */ + u_int64_t tdb_cryptoid; /* Crypto session ID */ + u_int32_t tdb_ref; /* References */ u_int32_t tdb_spi; /* SPI */ u_int16_t tdb_amxkeylen; /* Raw authentication key length */ u_int16_t tdb_emxkeylen; /* Raw encryption key length */ @@ -311,24 +284,11 @@ struct tdb /* tunnel descriptor block */ union sockaddr_union tdb_src; /* Source address for this SA */ union sockaddr_union tdb_proxy; - u_int8_t *tdb_key; /* Key material (schedules) */ - u_int8_t *tdb_ictx; /* Authentication contexts */ - u_int8_t *tdb_octx; u_int8_t *tdb_srcid; /* Source ID for this SA */ u_int8_t *tdb_dstid; /* Destination ID for this SA */ u_int8_t *tdb_amxkey; /* Raw authentication key */ u_int8_t *tdb_emxkey; /* Raw encryption key */ - union - { - u_int8_t Iv[ESP_3DES_IVS]; /* That's enough space */ - u_int32_t Ivl; /* Make sure this is 4 bytes */ - u_int64_t Ivq; /* Make sure this is 8 bytes! */ - }IV; -#define tdb_iv IV.Iv -#define tdb_ivl IV.Ivl -#define tdb_ivq IV.Ivq - u_int32_t tdb_rpl; /* Replay counter */ u_int32_t tdb_bitmap; /* Used for replay sliding window */ u_int32_t tdb_initial; /* Initial replay value */ @@ -349,47 +309,12 @@ struct tdb /* tunnel descriptor block */ TAILQ_HEAD(tdb_inp_head, inpcb) tdb_inp; }; -union authctx_old { - MD5_CTX md5ctx; - SHA1_CTX sha1ctx; -}; - -union authctx { - MD5_CTX md5ctx; - SHA1_CTX sha1ctx; - RMD160_CTX rmd160ctx; -}; - struct tdb_ident { u_int32_t spi; union sockaddr_union dst; u_int8_t proto; }; -struct auth_hash { - int type; - char *name; - u_int16_t keysize; - u_int16_t hashsize; - u_int16_t authsize; - u_int16_t ctxsize; - void (*Init)(void *); - void (*Update)(void *, u_int8_t *, u_int16_t); - void (*Final)(u_int8_t *, void *); -}; - -struct enc_xform { - int type; - char *name; - u_int16_t blocksize, ivsize; - u_int16_t minkey, maxkey; - u_int32_t ivmask; /* Or all possible modes, zero iv = 1 */ - void (*encrypt)(struct tdb *, u_int8_t *); - void (*decrypt)(struct tdb *, u_int8_t *); - void (*setkey)(u_int8_t **, u_int8_t *, int len); - void (*zerokey)(u_int8_t **); -}; - struct ipsecinit { u_int8_t *ii_enckey; @@ -408,7 +333,7 @@ struct xformsw int (*xf_attach)(void); /* called at config time */ int (*xf_init)(struct tdb *, struct xformsw *, struct ipsecinit *); int (*xf_zeroize)(struct tdb *); /* termination */ - struct mbuf *(*xf_input)(struct mbuf *, struct tdb *, int, int); /* input */ + int (*xf_input)(struct mbuf *, struct tdb *, int, int); /* input */ int (*xf_output)(struct mbuf *, struct tdb *, struct mbuf **, int, int); /* output */ }; @@ -463,8 +388,6 @@ extern int ipsec_keep_invalid; extern int ipsec_in_use; extern int ipsec_require_pfs; -extern u_int8_t hmac_ipad_buffer[64]; -extern u_int8_t hmac_opad_buffer[64]; extern int ipsec_soft_allocations; extern int ipsec_exp_allocations; extern int ipsec_soft_bytes; @@ -488,6 +411,7 @@ extern struct auth_hash auth_hash_hmac_ripemd_160_96; extern TAILQ_HEAD(expclusterlist_head, tdb) expclusterlist; extern TAILQ_HEAD(explist_head, tdb) explist; + extern struct xformsw xformsw[], *xformswNXFORMSW; /* Check if a given tdb has encryption, authentication and/or tunneling */ @@ -571,34 +495,41 @@ extern void etherip_input __P((struct mbuf *, ...)); extern int ah_attach(void); extern int ah_init(struct tdb *, struct xformsw *, struct ipsecinit *); extern int ah_zeroize(struct tdb *); -extern int ah_output(struct mbuf *, struct tdb *, struct mbuf **, - int, int); -extern struct mbuf *ah_input(struct mbuf *, struct tdb *, int, int); +extern int ah_output(struct mbuf *, struct tdb *, struct mbuf **, int, int); +extern int ah_output_cb(void *); +extern int ah_input(struct mbuf *, struct tdb *, int, int); +extern int ah_input_cb(void *); extern int ah_sysctl(int *, u_int, void *, size_t *, void *, size_t); +extern int ah_massage_headers(struct mbuf **, int, int, int, int); #ifdef INET extern void ah4_input __P((struct mbuf *, ...)); +extern int ah4_input_cb __P((struct mbuf *, ...)); #endif /* INET */ #ifdef INET6 -int ah6_input __P((struct mbuf **, int *, int)); +extern int ah6_input __P((struct mbuf **, int *, int)); +extern int ah6_input_cb __P((struct mbuf *, int)); #endif /* INET6 */ /* XF_ESP */ extern int esp_attach(void); extern int esp_init(struct tdb *, struct xformsw *, struct ipsecinit *); extern int esp_zeroize(struct tdb *); -extern int esp_output(struct mbuf *, struct tdb *, struct mbuf **, - int, int); -extern struct mbuf *esp_input(struct mbuf *, struct tdb *, int, int); +extern int esp_output(struct mbuf *, struct tdb *, struct mbuf **, int, int); +extern int esp_output_cb(void *); +extern int esp_input(struct mbuf *, struct tdb *, int, int); +extern int esp_input_cb(void *); extern int esp_sysctl(int *, u_int, void *, size_t *, void *, size_t); #ifdef INET extern void esp4_input __P((struct mbuf *, ...)); +extern int esp4_input_cb __P((struct mbuf *, ...)); #endif /* INET */ #ifdef INET6 -int esp6_input __P((struct mbuf **, int *, int)); +extern int esp6_input __P((struct mbuf **, int *, int)); +extern int esp6_input_cb __P((struct mbuf *, int)); #endif /* INET6 */ /* XF_TCPSIGNATURE */ @@ -606,7 +537,8 @@ extern int tcp_signature_tdb_attach __P((void)); extern int tcp_signature_tdb_init __P((struct tdb *, struct xformsw *, struct ipsecinit *)); extern int tcp_signature_tdb_zeroize __P((struct tdb *)); -extern struct mbuf *tcp_signature_tdb_input __P((struct mbuf *, struct tdb *, int, int)); +extern int tcp_signature_tdb_input __P((struct mbuf *, struct tdb *, int, + int)); extern int tcp_signature_tdb_output __P((struct mbuf *, struct tdb *, struct mbuf **, int, int)); @@ -620,7 +552,8 @@ extern int checkreplaywindow32(u_int32_t, u_int32_t, u_int32_t *, u_int32_t, extern unsigned char ipseczeroes[]; /* Packet processing */ -int ipsp_process_packet(struct mbuf *, struct mbuf **, struct tdb *, - int *, int); +extern int ipsp_process_packet(struct mbuf *, struct tdb *, int, int); +extern int ipsp_process_done(struct mbuf *, struct tdb *); +extern int ipsec_common_input_cb(struct mbuf *, struct tdb *, int, int); #endif /* _KERNEL */ #endif /* _NETINET_IPSP_H_ */ diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index e4fb895561a..605f1e50885 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_output.c,v 1.64 2000/01/11 03:10:04 angelos Exp $ */ +/* $OpenBSD: ip_output.c,v 1.65 2000/03/17 10:25:22 angelos Exp $ */ /* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */ /* @@ -130,20 +130,13 @@ ip_output(m0, va_alist) va_list ap; #ifdef IPSEC union sockaddr_union sunion; - struct mbuf *mp; - struct udphdr *udp; - struct tcphdr *tcp; struct inpcb *inp; struct route_enc re0, *re = &re0; struct sockaddr_encap *ddst, *gw; u_int8_t sa_require, sa_have = 0; - int s, protoflag = AF_INET; + int s; struct tdb *tdb, tdb2; - -#ifdef INET6 - struct ip6_hdr *ip6; -#endif /* INET6 */ #endif /* IPSEC */ va_start(ap, m0); @@ -385,12 +378,19 @@ sendit: goto have_tdb; } + /* + * If there are no flows in place, there's no point + * continuing with the SPD lookup. + */ if (!ipsec_in_use) { splx(s); goto no_encap; } - /* Do an SPD lookup */ + /* + * Do an SPD lookup -- this code should probably be moved + * to a separate function. + */ ddst = (struct sockaddr_encap *) &re->re_dst; ddst->sen_family = PF_KEY; ddst->sen_len = SENT_IP4_LEN; @@ -399,36 +399,27 @@ sendit: ddst->sen_ip_dst = ip->ip_dst; ddst->sen_proto = ip->ip_p; + /* If TCP/UDP, extract the port numbers to use in the lookup */ switch (ip->ip_p) { case IPPROTO_UDP: - if (m->m_len < hlen + 2 * sizeof(u_int16_t)) { - if ((m = m_pullup(m, hlen + 2 * - sizeof(u_int16_t))) == 0) - return ENOBUFS; - ip = mtod(m, struct ip *); - } - udp = (struct udphdr *) (mtod(m, u_char *) + hlen); - ddst->sen_sport = udp->uh_sport; - ddst->sen_dport = udp->uh_dport; - break; - case IPPROTO_TCP: - if (m->m_len < hlen + 2 * sizeof(u_int16_t)) { - if ((m = m_pullup(m, hlen + 2 * - sizeof(u_int16_t))) == 0) - return ENOBUFS; - ip = mtod(m, struct ip *); - } - tcp = (struct tcphdr *) (mtod(m, u_char *) + hlen); - ddst->sen_sport = tcp->th_sport; - ddst->sen_dport = tcp->th_dport; - break; + /* + * Luckily, the offset of the src/dst ports in both the UDP + * and TCP headers is the same (first two 16-bit values + * in the respective headers), so we can just copy them. + */ + m_copydata(m, hlen, sizeof(u_int16_t), + (caddr_t) &ddst->sen_sport); + m_copydata(m, hlen + sizeof(u_int16_t), sizeof(u_int16_t), + (caddr_t) &ddst->sen_dport); + break; default: ddst->sen_sport = 0; ddst->sen_dport = 0; } + /* Actual SPD lookup */ rtalloc((struct route *) re); if (re->re_rt == NULL) { splx(s); @@ -496,6 +487,7 @@ sendit: } #endif /* INET6 */ + /* Lookup in the TDB table */ tdb = (struct tdb *) gettdb(gw->sen_ipsp_spi, &sunion, gw->sen_ipsp_sproto); @@ -524,7 +516,7 @@ sendit: sa_require |= NOTIFY_SATYPE_TUNNEL; } - /* Check for PFS */ + /* Check whether Perfect Forward Secrect is required */ if (ipsec_require_pfs) tdb->tdb_flags |= TDBF_PFS; else @@ -637,21 +629,6 @@ sendit: have_tdb: - ip->ip_len = htons((u_short) ip->ip_len); - ip->ip_off = htons((u_short) ip->ip_off); - ip->ip_sum = 0; - - /* - * Now we check if this tdb has all the transforms which - * are requried by the socket or our default policy. - */ - SPI_CHAIN_ATTRIB(sa_have, tdb_onext, tdb); - - if (sa_require & ~sa_have) { - splx(s); - goto no_encap; - } - if (tdb == NULL) { splx(s); if (gw->sen_type == SENT_IPSP) @@ -662,63 +639,40 @@ sendit: DPRINTF(("ip_output(): non-existant TDB for SA %s/%08x/%u\n", inet6_ntoa4(gw->sen_ipsp6_dst), ntohl(gw->sen_ipsp6_spi), gw->sen_ipsp6_sproto)); #endif /* INET6 */ - if (re->re_rt) - RTFREE(re->re_rt); error = EHOSTUNREACH; m_freem(m); goto done; } - error = ipsp_process_packet(m, &mp, tdb, &protoflag, 0); - if ((mp == NULL) && (!error)) - error = ENOBUFS; - if (error) { - if (re->re_rt) - RTFREE(re->re_rt); - if (mp) - m_freem(mp); - goto done; - } - - m = mp; - mp = NULL; - - splx(s); - - /* - * At this point, m is pointing to an mbuf chain with the - * processed packet. Call ourselves recursively, but - * bypass the encap code. - */ + /* We don't need this anymore */ if (re->re_rt) RTFREE(re->re_rt); - if (protoflag == AF_INET) { - ip = mtod(m, struct ip *); - NTOHS(ip->ip_len); - NTOHS(ip->ip_off); + /* Massage the IP header for use by the IPsec code */ + ip->ip_len = htons((u_short) ip->ip_len); + ip->ip_off = htons((u_short) ip->ip_off); + ip->ip_sum = 0; - return ip_output(m, NULL, NULL, - IP_ENCAPSULATED | IP_RAWOUTPUT, - NULL, NULL); + /* + * Now we check if this tdb has all the transforms which + * are required by the socket or our default policy. + */ + SPI_CHAIN_ATTRIB(sa_have, tdb_onext, tdb); + if (sa_require & ~sa_have) { + splx(s); + goto no_encap; } -#ifdef INET6 - if (protoflag == AF_INET6) { - ip6 = mtod(m, struct ip6_hdr *); - NTOHS(ip6->ip6_plen); - - /* Naturally, ip6_output() has to honor those two flags */ - return ip6_output(m, NULL, NULL, - IP_ENCAPSULATED | IP_RAWOUTPUT, - NULL, NULL); - } -#endif /* INET6 */ + /* Callee frees mbuf */ + error = ipsp_process_packet(m, tdb, AF_INET, 0); + splx(s); + return error; /* Nothing more to be done */ no_encap: /* This is for possible future use, don't move or delete */ if (re->re_rt) RTFREE(re->re_rt); + /* No IPSec processing though it was required, drop packet */ if (sa_require) { error = EHOSTUNREACH; diff --git a/sys/netinet/ip_xform.c b/sys/netinet/ip_xform.c deleted file mode 100644 index 144dc098356..00000000000 --- a/sys/netinet/ip_xform.c +++ /dev/null @@ -1,341 +0,0 @@ -/* $OpenBSD: ip_xform.c,v 1.6 2000/03/10 03:51:59 deraadt Exp $ */ - -/* - * The authors of this code are John Ioannidis (ji@tla.org), - * Angelos D. Keromytis (kermit@csd.uch.gr) and - * Niels Provos (provos@physnet.uni-hamburg.de). - * - * This code was written by John Ioannidis for BSD/OS in Athens, Greece, - * in November 1995. - * - * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, - * by Angelos D. Keromytis. - * - * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis - * and Niels Provos. - * - * Additional features in 1999 by Angelos D. Keromytis. - * - * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, - * Angelos D. Keromytis and Niels Provos. - * - * Permission to use, copy, and modify this software without fee - * is hereby granted, provided that this entire notice is included in - * all copies of any software which is or includes a copy or - * modification of this software. - * You may use this code under the GNU public license if you so wish. Please - * contribute changes back to the authors under this freer than GPL license - * so that we may further the use of strong encryption without limitations to - * all. - * - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE - * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR - * PURPOSE. - */ - -/* - * Encapsulation Security Payload Processing - * Per RFC1827 (Atkinson, 1995) - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/malloc.h> -#include <sys/mbuf.h> -#include <sys/domain.h> -#include <sys/protosw.h> -#include <sys/socket.h> -#include <sys/sysctl.h> -#include <sys/errno.h> -#include <sys/time.h> -#include <sys/kernel.h> -#include <machine/cpu.h> - -#include <net/if.h> -#include <net/route.h> -#include <net/netisr.h> -#include <net/bpf.h> - -#include <netinet/in.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <netinet/in_pcb.h> -#include <netinet/in_var.h> -#include <netinet/ip_var.h> - -#include <sys/socketvar.h> -#include <net/raw_cb.h> - -#include <netinet/ip_icmp.h> -#include <netinet/ip_ipsp.h> -#include <netinet/ip_esp.h> -#include <net/pfkeyv2.h> -#include <net/if_enc.h> - -extern void des_ecb3_encrypt(caddr_t, caddr_t, caddr_t, caddr_t, caddr_t, int); -extern void des_ecb_encrypt(caddr_t, caddr_t, caddr_t, int); -extern void des_set_key(caddr_t, caddr_t); - -static void des1_encrypt(struct tdb *, u_int8_t *); -static void des3_encrypt(struct tdb *, u_int8_t *); -static void blf_encrypt(struct tdb *, u_int8_t *); -static void cast5_encrypt(struct tdb *, u_int8_t *); -static void skipjack_encrypt(struct tdb *, u_int8_t *); -static void des1_decrypt(struct tdb *, u_int8_t *); -static void des3_decrypt(struct tdb *, u_int8_t *); -static void blf_decrypt(struct tdb *, u_int8_t *); -static void cast5_decrypt(struct tdb *, u_int8_t *); -static void skipjack_decrypt(struct tdb *, u_int8_t *); - -static void -des1_encrypt(struct tdb *tdb, u_int8_t *blk) -{ - des_ecb_encrypt(blk, blk, tdb->tdb_key, 1); -} - -static void -des1_decrypt(struct tdb *tdb, u_int8_t *blk) -{ - des_ecb_encrypt(blk, blk, tdb->tdb_key, 0); -} - -static void -des1_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - MALLOC(*sched, u_int8_t *, 128, M_XDATA, M_WAITOK); - bzero(*sched, 128); - des_set_key(key, *sched); -} - -static void -des1_zerokey(u_int8_t **sched) -{ - bzero(*sched, 128); - FREE(*sched, M_XDATA); - *sched = NULL; -} - -struct enc_xform enc_xform_des = { - SADB_EALG_DESCBC, "Data Encryption Standard (DES)", - ESP_DES_BLKS, ESP_DES_IVS, - 8, 8, 8, - des1_encrypt, - des1_decrypt, - des1_setkey, - des1_zerokey, -}; - -static void -des3_encrypt(struct tdb *tdb, u_int8_t *blk) -{ - des_ecb3_encrypt(blk, blk, tdb->tdb_key, tdb->tdb_key + 128, - tdb->tdb_key + 256, 1); -} - -static void -des3_decrypt(struct tdb *tdb, u_int8_t *blk) -{ - des_ecb3_encrypt(blk, blk, tdb->tdb_key + 256, tdb->tdb_key + 128, - tdb->tdb_key, 0); -} - -static void -des3_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - MALLOC(*sched, u_int8_t *, 384, M_XDATA, M_WAITOK); - bzero(*sched, 384); - des_set_key(key, *sched); - des_set_key(key + 8, *sched + 128); - des_set_key(key + 16, *sched + 256); -} - -static void -des3_zerokey(u_int8_t **sched) -{ - bzero(*sched, 384); - FREE(*sched, M_XDATA); - *sched = NULL; -} - -struct enc_xform enc_xform_3des = { - SADB_EALG_3DESCBC, "Triple DES (3DES)", - ESP_3DES_BLKS, ESP_3DES_IVS, - 24, 24, 8, - des3_encrypt, - des3_decrypt, - des3_setkey, - des3_zerokey -}; - -static void -blf_encrypt(struct tdb *tdb, u_int8_t *blk) -{ - blf_ecb_encrypt((blf_ctx *) tdb->tdb_key, blk, 8); -} - -static void -blf_decrypt(struct tdb *tdb, u_int8_t *blk) -{ - blf_ecb_decrypt((blf_ctx *) tdb->tdb_key, blk, 8); -} - -static void -blf_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - MALLOC(*sched, u_int8_t *, sizeof(blf_ctx), M_XDATA, M_WAITOK); - bzero(*sched, sizeof(blf_ctx)); - blf_key((blf_ctx *)*sched, key, len); -} - -static void -blf_zerokey(u_int8_t **sched) -{ - bzero(*sched, sizeof(blf_ctx)); - FREE(*sched, M_XDATA); - *sched = NULL; -} - -struct enc_xform enc_xform_blf = { - SADB_X_EALG_BLF, "Blowfish", - ESP_BLF_BLKS, ESP_BLF_IVS, - 5, BLF_MAXKEYLEN, 8, - blf_encrypt, - blf_decrypt, - blf_setkey, - blf_zerokey -}; - -static void -cast5_encrypt(struct tdb *tdb, u_int8_t *blk) -{ - cast_encrypt((cast_key *) tdb->tdb_key, blk, blk); -} - -static void -cast5_decrypt(struct tdb *tdb, u_int8_t *blk) -{ - cast_decrypt((cast_key *) tdb->tdb_key, blk, blk); -} - -static void -cast5_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - MALLOC(*sched, u_int8_t *, sizeof(blf_ctx), M_XDATA, M_WAITOK); - bzero(*sched, sizeof(blf_ctx)); - cast_setkey((cast_key *)*sched, key, len); -} - -static void -cast5_zerokey(u_int8_t **sched) -{ - bzero(*sched, sizeof(cast_key)); - FREE(*sched, M_XDATA); - *sched = NULL; -} - -struct enc_xform enc_xform_cast5 = { - SADB_X_EALG_CAST, "CAST", - ESP_CAST_BLKS, ESP_CAST_IVS, - 5, 16, 8, - cast5_encrypt, - cast5_decrypt, - cast5_setkey, - cast5_zerokey -}; - -static void -skipjack_encrypt(struct tdb *tdb, u_int8_t *blk) -{ - skipjack_forwards(blk, blk, (u_int8_t **) tdb->tdb_key); -} - -static void -skipjack_decrypt(struct tdb *tdb, u_int8_t *blk) -{ - skipjack_backwards(blk, blk, (u_int8_t **) tdb->tdb_key); -} - -static void -skipjack_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - MALLOC(*sched, u_int8_t *, 10 * sizeof(u_int8_t *), M_XDATA, M_WAITOK); - bzero(*sched, 10 * sizeof(u_int8_t *)); - subkey_table_gen(key, (u_int8_t **) *sched); -} - -static void -skipjack_zerokey(u_int8_t **sched) -{ - int k; - - for (k = 0; k < 10; k++) - if (((u_int8_t **)(*sched))[k]) - { - bzero(((u_int8_t **)(*sched))[k], 0x100); - FREE(((u_int8_t **)(*sched))[k], M_XDATA); - } - bzero(*sched, 10 * sizeof(u_int8_t *)); - FREE(*sched, M_XDATA); - *sched = NULL; -} - -struct enc_xform enc_xform_skipjack = { - SADB_X_EALG_SKIPJACK, "Skipjack", - ESP_SKIPJACK_BLKS, ESP_SKIPJACK_IVS, - 10, 10, 8, - skipjack_encrypt, - skipjack_decrypt, - skipjack_setkey, - skipjack_zerokey -}; - -/* - * And now for auth - */ - -struct auth_hash auth_hash_hmac_md5_96 = { - SADB_AALG_MD5HMAC96, "HMAC-MD5-96", - MD5HMAC96_KEYSIZE, AH_MD5_ALEN, AH_HMAC_HASHLEN, - sizeof(MD5_CTX), - (void (*) (void *)) MD5Init, - (void (*) (void *, u_int8_t *, u_int16_t)) MD5Update, - (void (*) (u_int8_t *, void *)) MD5Final -}; - -struct auth_hash auth_hash_hmac_sha1_96 = { - SADB_AALG_SHA1HMAC96, "HMAC-SHA1-96", - SHA1HMAC96_KEYSIZE, AH_SHA1_ALEN, AH_HMAC_HASHLEN, - sizeof(SHA1_CTX), - (void (*) (void *)) SHA1Init, - (void (*) (void *, u_int8_t *, u_int16_t)) SHA1Update, - (void (*) (u_int8_t *, void *)) SHA1Final -}; - -struct auth_hash auth_hash_hmac_ripemd_160_96 = { - SADB_X_AALG_RIPEMD160HMAC96, "HMAC-RIPEMD-160-96", - RIPEMD160HMAC96_KEYSIZE, AH_RMD160_ALEN, AH_HMAC_HASHLEN, - sizeof(RMD160_CTX), - (void (*)(void *)) RMD160Init, - (void (*)(void *, u_int8_t *, u_int16_t)) RMD160Update, - (void (*)(u_int8_t *, void *)) RMD160Final -}; - -struct auth_hash auth_hash_key_md5 = { - SADB_X_AALG_MD5, "Keyed MD5", - 0, AH_MD5_ALEN, AH_MD5_ALEN, - sizeof(MD5_CTX), - (void (*)(void *))MD5Init, - (void (*)(void *, u_int8_t *, u_int16_t))MD5Update, - (void (*)(u_int8_t *, void *))MD5Final -}; - -struct auth_hash auth_hash_key_sha1 = { - SADB_X_AALG_SHA1, "Keyed SHA1", - 0, AH_SHA1_ALEN, AH_SHA1_ALEN, - sizeof(SHA1_CTX), - (void (*)(void *))SHA1Init, - (void (*)(void *, u_int8_t *, u_int16_t))SHA1Update, - (void (*)(u_int8_t *, void *))SHA1Final -}; diff --git a/sys/netinet/ipsec_input.c b/sys/netinet/ipsec_input.c index a46d2921356..7aca77e11cb 100644 --- a/sys/netinet/ipsec_input.c +++ b/sys/netinet/ipsec_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ipsec_input.c,v 1.19 2000/02/07 06:09:09 itojun Exp $ */ +/* $OpenBSD: ipsec_input.c,v 1.20 2000/03/17 10:25:23 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -81,7 +81,7 @@ #include "bpfilter.h" -int ipsec_common_input(struct mbuf **, int, int, int, int); +int ipsec_common_input(struct mbuf *, int, int, int, int); #ifdef ENCDEBUG #define DPRINTF(x) if (encdebug) printf x @@ -99,50 +99,34 @@ int ah_enable = 0; /* * ipsec_common_input() gets called when we receive an IPsec-protected packet - * in IPv4 or IPv6. + * in IPv4 or IPv6. All it does is find the right TDB and call the appropriate + * transform. The callback takes care of further processing (like ingress + * filtering). */ - int -ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) +ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto) { #define IPSEC_ISTAT(y,z) (sproto == IPPROTO_ESP ? (y)++ : (z)++) -#define IPSEC_NAME (sproto == IPPROTO_ESP ? (af == AF_INET ? "esp4_input()" :\ - "esp6_input()") :\ - (af == AF_INET ? "ah4_input()" :\ - "ah6_input()")) - union sockaddr_union src_address, dst_address, src2, dst2; + + union sockaddr_union src_address, dst_address; caddr_t sport = 0, dport = 0; - struct flow *flow; struct tdb *tdbp; - struct mbuf *m; u_int32_t spi; - u_int8_t prot; int s; -#ifdef INET - struct ip *ip, ipn; -#endif /* INET */ - -#ifdef INET6 - struct ip6_hdr *ip6, ip6n; -#endif /* INET6 */ - IPSEC_ISTAT(espstat.esps_input, ahstat.ahs_input); - if (m0 == 0) + if (m == 0) { DPRINTF(("%s: NULL packet received\n")); IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops); return EINVAL; } - else - m = *m0; if ((sproto == IPPROTO_ESP && !esp_enable) || (sproto == IPPROTO_AH && !ah_enable)) { m_freem(m); - *m0 = NULL; IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); return EOPNOTSUPP; } @@ -187,9 +171,9 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) #endif /* INET6 */ default: - DPRINTF(("%s: unsupported protocol family %d\n", IPSEC_NAME, af)); + DPRINTF(("ipsec_common_input(): unsupported protocol family %d\n", + af)); m_freem(m); - *m0 = NULL; IPSEC_ISTAT(espstat.esps_nopf, ahstat.ahs_nopf); return EPFNOSUPPORT; } @@ -199,10 +183,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) if (tdbp == NULL) { splx(s); - DPRINTF(("%s: could not find SA for packet to %s, spi %08x\n", - IPSEC_NAME, ipsp_address(dst_address), ntohl(spi))); + DPRINTF(("ipsec_common_input(): could not find SA for packet to %s, spi %08x\n", ipsp_address(dst_address), ntohl(spi))); m_freem(m); - *m0 = NULL; IPSEC_ISTAT(espstat.esps_notdb, ahstat.ahs_notdb); return ENOENT; } @@ -210,10 +192,9 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) if (tdbp->tdb_flags & TDBF_INVALID) { splx(s); - DPRINTF(("%s: attempted to use invalid SA %s/%08x\n", - IPSEC_NAME, ipsp_address(dst_address), ntohl(spi))); + DPRINTF(("ipsec_common_input(): attempted to use invalid SA %s/%08x\n", + ipsp_address(dst_address), ntohl(spi))); m_freem(m); - *m0 = NULL; IPSEC_ISTAT(espstat.esps_invalid, ahstat.ahs_invalid); return EINVAL; } @@ -221,10 +202,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) if (tdbp->tdb_xform == NULL) { splx(s); - DPRINTF(("%s: attempted to use uninitialized SA %s/%08x\n", - IPSEC_NAME, ipsp_address(dst_address), ntohl(spi))); + DPRINTF(("ipsec_common_input(): attempted to use uninitialized SA %s/%08x\n", ipsp_address(dst_address), ntohl(spi))); m_freem(m); - *m0 = NULL; IPSEC_ISTAT(espstat.esps_noxform, ahstat.ahs_noxform); return ENXIO; } @@ -244,39 +223,74 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) /* If we do ingress filtering and the list is empty, quick drop */ if (ipsec_acl && (tdbp->tdb_access == NULL)) { - DPRINTF(("%s: packet from %s dropped due to empty policy list, SA %s/%08x\n", IPSEC_NAME, ipsp_address(src_address), ipsp_address(tdbp->tdb_dst), ntohl(spi))); + DPRINTF(("ipsec_common_input(): packet from %s dropped due to empty policy list, SA %s/%08x\n", ipsp_address(src_address), ipsp_address(tdbp->tdb_dst), ntohl(spi))); splx(s); m_freem(m); - *m0 = NULL; IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); return EACCES; } - m = (*(tdbp->tdb_xform->xf_input))(m, tdbp, skip, protoff); + /* + * Call appropriate transform and return -- callback takes care of + * everything else. + */ + if ((*(tdbp->tdb_xform->xf_input))(m, tdbp, skip, protoff) == NULL) + { + splx(s); + return EINVAL; + } + else + { + splx(s); + return 0; + } +} + +/* + * IPsec input callback, called by the transform callback. Takes care of + * filtering and other sanity checks on the processed packet. + */ +int +ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) +{ + union sockaddr_union src_address, dst_address, src2, dst2; + caddr_t sport = 0, dport = 0; + int prot, af, sproto; + struct flow *flow; + +#ifdef INET + struct ip *ip, ipn; +#endif /* INET */ + +#ifdef INET6 + struct ip6_hdr *ip6, ip6n; +#endif /* INET6 */ + + af = tdbp->tdb_dst.sa.sa_family; + sproto = tdbp->tdb_sproto; + + /* Sanity check */ if (m == NULL) { /* The called routine will print a message if necessary */ - splx(s); IPSEC_ISTAT(espstat.esps_badkcr, ahstat.ahs_badkcr); return EINVAL; } #ifdef INET /* Fix IPv4 header */ - if (af == AF_INET) + if (tdbp->tdb_dst.sa.sa_family == AF_INET) { if ((m = m_pullup(m, skip)) == 0) { - DPRINTF(("%s: processing failed for SA %s/%08x\n", - IPSEC_NAME, ipsp_address(tdbp->tdb_dst), ntohl(spi))); - splx(s); + DPRINTF(("ipsec_common_input_cb(): processing failed for SA %s/%08x\n", ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops); - *m0 = NULL; - return ENOMEM; + return ENOBUFS; } ip = mtod(m, struct ip *); ip->ip_len = htons(m->m_pkthdr.len); + HTONS(ip->ip_id); ip->ip_sum = 0; ip->ip_sum = in_cksum(m, ip->ip_hl << 2); prot = ip->ip_p; @@ -297,10 +311,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) ((tdbp->tdb_proxy.sa.sa_family != AF_INET) && (tdbp->tdb_proxy.sa.sa_family != 0))) { - DPRINTF(("%s: inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", IPSEC_NAME, inet_ntoa4(ipn.ip_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(spi))); - splx(s); + DPRINTF(("ipsec_common_input_cb(): inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", inet_ntoa4(ipn.ip_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); m_freem(m); - *m0 = NULL; IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); return EACCES; } @@ -325,10 +337,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) ((tdbp->tdb_proxy.sa.sa_family != AF_INET6) && (tdbp->tdb_proxy.sa.sa_family != 0))) { - DPRINTF(("%s: inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", IPSEC_NAME, inet6_ntoa4(ip6n.ip6_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(spi))); - splx(s); + DPRINTF(("ipsec_common_input_cb(): inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", inet6_ntoa4(ip6n.ip6_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); m_freem(m); - *m0 = NULL; IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); return EACCES; } @@ -345,11 +355,9 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) ((tdbp->tdb_src.sa.sa_family != AF_INET) && (tdbp->tdb_src.sa.sa_family != 0))) { - DPRINTF(("%s: source address %s doesn't correspond to expected source %s, SA %s/%08x\n", IPSEC_NAME, inet_ntoa4(ip->ip_src), ipsp_address(tdbp->tdb_src), ipsp_address(tdbp->tdb_dst), ntohl(spi))); - splx(s); + DPRINTF(("ipsec_common_input_cb(): source address %s doesn't correspond to expected source %s, SA %s/%08x\n", inet_ntoa4(ip->ip_src), ipsp_address(tdbp->tdb_src), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); m_freem(m); IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); - *m0 = NULL; return EACCES; } } @@ -361,12 +369,9 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) { if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == 0) { - DPRINTF(("%s: processing failed for SA %s/%08x\n", - IPSEC_NAME, ipsp_address(tdbp->tdb_dst), ntohl(spi))); - splx(s); + DPRINTF(("ipsec_common_input_cb(): processing failed for SA %s/%08x\n", ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops); - *m0 = NULL; - return ENOMEM; + return EACCES; } ip6 = mtod(m, struct ip6_hdr *); @@ -392,11 +397,9 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) ((tdbp->tdb_proxy.sa.sa_family != AF_INET) && (tdbp->tdb_proxy.sa.sa_family != 0))) { - DPRINTF(("%s: inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", IPSEC_NAME, inet_ntoa4(ipn.ip_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(spi))); - splx(s); + DPRINTF(("ipsec_common_input_cb(): inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", inet_ntoa4(ipn.ip_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); m_freem(m); IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); - *m0 = NULL; return EACCES; } } @@ -419,10 +422,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) ((tdbp->tdb_proxy.sa.sa_family != AF_INET6) && (tdbp->tdb_proxy.sa.sa_family != 0))) { - DPRINTF(("%s: inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", IPSEC_NAME, inet6_ntoa4(ip6n.ip6_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(spi))); - splx(s); + DPRINTF(("ipsec_common_input_cb(): inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", inet6_ntoa4(ip6n.ip6_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); m_freem(m); - *m0 = NULL; IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); return EACCES; } @@ -439,10 +440,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) ((tdbp->tdb_src.sa.sa_family != AF_INET6) && (tdbp->tdb_src.sa.sa_family != 0))) { - DPRINTF(("%s: packet %s to %s does not match any ACL entries, SA %s/%08x\n", IPSEC_NAME, ipsp_address(src_address), ipsp_address(dst_address), ipsp_address(tdbp->tdb_src), ipsp_address(tdbp->tdb_dst), ntohl(spi))); - splx(s); + DPRINTF(("ipsec_common_input_cb(): packet %s to %s does not match any ACL entries, SA %s/%08x\n", ipsp_address(src_address), ipsp_address(dst_address), ipsp_address(tdbp->tdb_src), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); m_freem(m); - *m0 = NULL; IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); return EACCES; } @@ -515,10 +514,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) if (flow == NULL) { /* Failed to match any entry in the ACL */ - DPRINTF(("%s: packet from %s to %s dropped due to policy, SA %s/%08x\n", IPSEC_NAME, ipsp_address(src_address), ipsp_address(dst_address), ipsp_address(tdbp->tdb_dst), ntohl(spi))); - splx(s); + DPRINTF(("ipsec_common_input_cb(): packet from %s to %s dropped due to policy, SA %s/%08x\n", ipsp_address(src_address), ipsp_address(dst_address), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); m_freem(m); - *m0 = NULL; IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); return EACCES; } @@ -529,7 +526,7 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) if (tdbp->tdb_bind_out) { if (!(m->m_flags & M_PKTHDR)) - DPRINTF(("%s: mbuf is not a packet header!\n", IPSEC_NAME)); + DPRINTF(("ipsec_common_input_cb(): mbuf is not a packet header!\n")); MALLOC(m->m_pkthdr.tdbi, struct tdb_ident *, sizeof(struct tdb_ident), M_TEMP, M_NOWAIT); @@ -586,11 +583,51 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto) bpf_mtap(m->m_pkthdr.rcvif->if_bpf, &m1); } #endif - splx(s); - *m0 = m; - return 0; -#undef IPSEC_NAME + /* Call the appropriate IPsec transform callback */ + switch (af) + { +#ifdef INET + case AF_INET: + switch (sproto) + { + case IPPROTO_ESP: + return esp4_input_cb(m); + + case IPPROTO_AH: + return ah4_input_cb(m); + + default: + DPRINTF(("ipsec_common_input_cb(): unknown/unsupported security protocol %d\n", sproto)); + m_freem(m); + return EPFNOSUPPORT; + } + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + switch (sproto) + { + case IPPROTO_ESP: + return esp6_input_cb(m, protoff); + + case IPPROTO_AH: + return ah6_input_cb(m, protoff); + + default: + DPRINTF(("ipsec_common_input_cb(): unknown/unsupported security protocol %d\n", sproto)); + m_freem(m); + return EPFNOSUPPORT; + } + break; +#endif /* INET6 */ + + default: + DPRINTF(("ipsec_common_input_cb(): unknown/unsupported protocol family %d\n", af)); + m_freem(m); + return EPFNOSUPPORT; + } #undef IPSEC_ISTAT } @@ -635,80 +672,86 @@ ah_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlen, void *newp, void ah4_input(struct mbuf *m, ...) { - struct ifqueue *ifq = &ipintrq; - struct mbuf *mp = m; - int skip, s; + int skip; va_list ap; va_start(ap, m); skip = va_arg(ap, int); va_end(ap); - if (ipsec_common_input(&mp, skip, offsetof(struct ip, ip_p), AF_INET, - IPPROTO_AH) != 0) - return; + ipsec_common_input(m, skip, offsetof(struct ip, ip_p), AF_INET, + IPPROTO_AH); + return; +} + +/* IPv4 AH callback */ +int +ah4_input_cb(struct mbuf *m, ...) +{ + struct ifqueue *ifq = &ipintrq; /* * Interface pointer is already in first mbuf; chop off the * `outer' header and reschedule. */ - s = splimp(); /* isn't it already? */ if (IF_QFULL(ifq)) { IF_DROP(ifq); - if (mp->m_pkthdr.tdbi) - free(mp->m_pkthdr.tdbi, M_TEMP); - m_freem(mp); + if (m->m_pkthdr.tdbi) + free(m->m_pkthdr.tdbi, M_TEMP); + m_freem(m); ahstat.ahs_qfull++; - splx(s); - DPRINTF(("ah4_input(): dropped packet because of full IP queue\n")); - return; + + DPRINTF(("ah4_input_cb(): dropped packet because of full IP queue\n")); + return ENOBUFS; } - IF_ENQUEUE(ifq, mp); + IF_ENQUEUE(ifq, m); schednetisr(NETISR_IP); - splx(s); + return 0; } /* IPv4 ESP wrapper */ void esp4_input(struct mbuf *m, ...) { - struct ifqueue *ifq = &ipintrq; - struct mbuf *mp = m; - int skip, s; + int skip; va_list ap; va_start(ap, m); skip = va_arg(ap, int); va_end(ap); - if (ipsec_common_input(&mp, skip, offsetof(struct ip, ip_p), AF_INET, - IPPROTO_ESP) != 0) - return; + ipsec_common_input(m, skip, offsetof(struct ip, ip_p), AF_INET, + IPPROTO_ESP); +} + +/* IPv4 ESP callback */ +int +esp4_input_cb(struct mbuf *m, ...) +{ + struct ifqueue *ifq = &ipintrq; /* * Interface pointer is already in first mbuf; chop off the * `outer' header and reschedule. */ - - s = splimp(); /* isn't it already? */ if (IF_QFULL(ifq)) { IF_DROP(ifq); - if (mp->m_pkthdr.tdbi) - free(mp->m_pkthdr.tdbi, M_TEMP); - m_freem(mp); + if (m->m_pkthdr.tdbi) + free(m->m_pkthdr.tdbi, M_TEMP); + m_freem(m); espstat.esps_qfull++; - splx(s); - DPRINTF(("esp4_input(): dropped packet because of full IP queue\n")); - return; + + DPRINTF(("esp4_input_cb(): dropped packet because of full IP queue\n")); + return ENOBUFS; } - IF_ENQUEUE(ifq, mp); + IF_ENQUEUE(ifq, m); schednetisr(NETISR_IP); - splx(s); + return 0; } #endif /* INET */ @@ -717,7 +760,6 @@ esp4_input(struct mbuf *m, ...) int ah6_input(struct mbuf **mp, int *offp, int proto) { - struct mbuf *m = *mp; u_int8_t nxt = 0; int protoff; @@ -725,14 +767,13 @@ ah6_input(struct mbuf **mp, int *offp, int proto) protoff = offsetof(struct ip6_hdr, ip6_nxt); else { - /* Chase the header chain... */ - + /* Chase down the header chain... */ protoff = sizeof(struct ip6_hdr); do { protoff += nxt; - m_copydata(m, protoff + offsetof(struct ip6_ext, ip6e_len), + m_copydata(*mp, protoff + offsetof(struct ip6_ext, ip6e_len), sizeof(u_int8_t), (caddr_t) &nxt); nxt = (nxt + 1) * 8; } while (protoff + nxt < *offp); @@ -742,7 +783,7 @@ ah6_input(struct mbuf **mp, int *offp, int proto) { DPRINTF(("ah6_input(): bad packet header chain\n")); ahstat.ahs_hdrops++; - m_freem(m); + m_freem(*mp); *mp = NULL; return IPPROTO_DONE; } @@ -750,22 +791,29 @@ ah6_input(struct mbuf **mp, int *offp, int proto) protoff += offsetof(struct ip6_ext, ip6e_nxt); } - if (ipsec_common_input(&m, *offp, protoff, AF_INET6, proto) != 0) - { - *mp = NULL; - return IPPROTO_DONE; - } + ipsec_common_input(*mp, *offp, protoff, AF_INET6, proto); + return IPPROTO_DONE; +} + +/* IPv6 AH callback */ +int +ah6_input_cb(struct mbuf *mp, int protoff) +{ + u_int8_t nxt = 0; /* Retrieve new protocol */ - m_copydata(m, protoff, sizeof(u_int8_t), (caddr_t) &nxt); - return nxt; + m_copydata(mp, protoff, sizeof(u_int8_t), (caddr_t) &nxt); + + /* XXX Requeue -- for now, drop packet */ + m_freem(mp); + + return 0; } /* IPv6 ESP wrapper */ int esp6_input(struct mbuf **mp, int *offp, int proto) { - struct mbuf *m = *mp; u_int8_t nxt = 0; int protoff; @@ -773,14 +821,13 @@ esp6_input(struct mbuf **mp, int *offp, int proto) protoff = offsetof(struct ip6_hdr, ip6_nxt); else { - /* Chase the header chain... */ - + /* Chase down the header chain... */ protoff = sizeof(struct ip6_hdr); do { protoff += nxt; - m_copydata(m, protoff + offsetof(struct ip6_ext, ip6e_len), + m_copydata(*mp, protoff + offsetof(struct ip6_ext, ip6e_len), sizeof(u_int8_t), (caddr_t) &nxt); nxt = (nxt + 1) * 8; } while (protoff + nxt < *offp); @@ -790,7 +837,7 @@ esp6_input(struct mbuf **mp, int *offp, int proto) { DPRINTF(("esp6_input(): bad packet header chain\n")); espstat.esps_hdrops++; - m_freem(m); + m_freem(*mp); *mp = NULL; return IPPROTO_DONE; } @@ -799,14 +846,22 @@ esp6_input(struct mbuf **mp, int *offp, int proto) } protoff = offsetof(struct ip6_hdr, ip6_nxt); - if (ipsec_common_input(&m, *offp, protoff, AF_INET6, proto) != 0) - { - *mp = NULL; - return IPPROTO_DONE; - } + ipsec_common_input(*mp, *offp, protoff, AF_INET6, proto); + return IPPROTO_DONE; +} + +/* IPv6 ESP callback */ +int +esp6_input_cb(struct mbuf *mp, int protoff) +{ + u_int8_t nxt = 0; /* Retrieve new protocol */ - m_copydata(m, protoff, sizeof(u_int8_t), (caddr_t) &nxt); - return nxt; + m_copydata(mp, protoff, sizeof(u_int8_t), (caddr_t) &nxt); + + /* XXX Requeue -- for now, drop packet */ + m_freem(mp); + + return 0; } #endif /* INET6 */ |