From 7a398ca36b953f95e5768c72ada0f40965ff2426 Mon Sep 17 00:00:00 2001 From: angelos Date: Fri, 17 Mar 2000 10:25:20 +0000 Subject: [PATCH] Cryptographic services framework, and software "device driver". The idea is to support various cryptographic hardware accelerators (which may be (detachable) cards, secondary/tertiary/etc processors, software crypto, etc). Supports session migration between crypto devices. What it doesn't (yet) support: - multiple instances of the same algorithm used in the same session - use of multiple crypto drivers in the same session - asymmetric crypto No support for a userland device yet. IPsec code path modified to allow for asynchronous cryptography (callbacks used in both input and output processing). Some unrelated code simplification done in the process (especially for AH). Development of this code kindly supported by Network Security Technologies (NSTI). The code was writen mostly in Greece, and is being committed from Montreal. --- sys/conf/GENERIC | 4 +- sys/conf/files | 5 +- sys/crypto/crypto.c | 1140 +++++++++++++++++++++++++++++++ sys/crypto/crypto.h | 200 ++++++ sys/crypto/xform.c | 354 ++++++++++ sys/crypto/xform.h | 73 ++ sys/kern/init_main.c | 10 +- sys/net/if_enc.c | 29 +- sys/net/pfkeyv2.c | 101 ++- sys/netinet/ip_ah.c | 1356 +++++++++++++++++-------------------- sys/netinet/ip_ah.h | 23 +- sys/netinet/ip_esp.c | 975 +++++++++++++------------- sys/netinet/ip_esp.h | 15 +- sys/netinet/ip_ipsp.c | 432 +++++++----- sys/netinet/ip_ipsp.h | 117 +--- sys/netinet/ip_output.c | 130 ++-- sys/netinet/ip_xform.c | 341 ---------- sys/netinet/ipsec_input.c | 319 +++++---- 18 files changed, 3516 insertions(+), 2108 deletions(-) create mode 100644 sys/crypto/crypto.c create mode 100644 sys/crypto/crypto.h create mode 100644 sys/crypto/xform.c create mode 100644 sys/crypto/xform.h delete mode 100644 sys/netinet/ip_xform.c diff --git a/sys/conf/GENERIC b/sys/conf/GENERIC index ba99502ea08..0b788ab288a 100644 --- a/sys/conf/GENERIC +++ b/sys/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.62 2000/03/02 10:50:29 art Exp $ +# $OpenBSD: GENERIC,v 1.63 2000/03/17 10:25:20 angelos Exp $ # # Machine-independent option; used by all architectures for their # GENERIC kernel @@ -21,6 +21,8 @@ option DIAGNOSTIC # internal consistency checks option KTRACE # system call tracing, a la ktrace(1) option KMEMSTATS # collect malloc(9) statistics +option CRYPTO # Cryptographic framework + option SYSVMSG # System V-like message queues option SYSVSEM # System V-like semaphores option SYSVSHM # System V-like memory sharing diff --git a/sys/conf/files b/sys/conf/files index 40307baf8ce..7b82dff2071 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.152 2000/02/28 23:13:06 deraadt Exp $ +# $OpenBSD: files,v 1.153 2000/03/17 10:25:20 angelos Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -503,7 +503,6 @@ file netinet/ip_ipsp.c (inet | inet6) & (ipsec | tcp_signature) file netinet/ip_ipip.c inet | inet6 file netinet/ip_ether.c inet & ipsec file netinet/ipsec_input.c (inet | inet6) & ipsec -file netinet/ip_xform.c inet & ipsec file netinet/ip_esp.c inet & ipsec file netinet/ip_ah.c inet & ipsec file crypto/rmd160.c (inet & ipsec) | crypto @@ -514,6 +513,8 @@ file crypto/skipjack.c (inet & ipsec) | crypto file crypto/ecb_enc.c (inet & ipsec) | crypto file crypto/set_key.c (inet & ipsec) | crypto file crypto/ecb3_enc.c (inet & ipsec) | crypto +file crypto/crypto.c (inet & ipsec) | crypto +file crypto/xform.c (inet & ipsec) | crypto file netatalk/aarp.c netatalk file netatalk/at_control.c netatalk file netatalk/at_proto.c netatalk diff --git a/sys/crypto/crypto.c b/sys/crypto/crypto.c new file mode 100644 index 00000000000..14fa7f885ca --- /dev/null +++ b/sys/crypto/crypto.c @@ -0,0 +1,1140 @@ +/* + * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) + * + * This code was written by Angelos D. Keromytis in Athens, Greece, in + * February 2000. Network Security Technologies Inc. (NSTI) kindly + * supported the development of this code. + * + * Copyright (c) 2000 Angelos D. Keromytis + * + * Permission to use, copy, and modify this software without fee + * is hereby granted, provided that this entire notice is included in + * all source code copies of any software which is or includes a copy or + * modification of this software. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cryptocap *crypto_drivers = NULL; +int crypto_drivers_num = 0; + +struct swcr_data **swcr_sessions = NULL; +u_int32_t swcr_sesnum = 0; +int32_t swcr_id = -1; + +struct cryptop *cryptop_queue = NULL; +struct cryptodesc *cryptodesc_queue = NULL; +int crypto_queue_num = 0; +int crypto_queue_max = CRYPTO_MAX_CACHED; + +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 }; + +/* + * Create a new session. + */ +int +crypto_newsession(u_int64_t *sid, struct cryptoini *cri) +{ + struct cryptoini *cr; + u_int32_t hid, lid; + int err; + + if (crypto_drivers == NULL) + return EINVAL; + + /* + * The algorithm we use here is pretty stupid; just use the + * first driver that supports all the algorithms we need. + * + * XXX We need more smarts here (in real life too, but that's + * XXX another story altogether). + */ + + for (hid = 0; hid < crypto_drivers_num; hid++) + { + /* + * If it's not initialized or has remaining sessions referencing + * it, skip. + */ + if ((crypto_drivers[hid].cc_newsession == NULL) || + (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP)) + continue; + + /* See if all the algorithms are supported */ + for (cr = cri; cr; cr = cr->cri_next) + if (crypto_drivers[hid].cc_alg[cr->cri_alg] == 0) + break; + + /* Ok, all algorithms are supported */ + if (cr == NULL) + break; + } + + /* + * Can't do everything in one session. + * + * XXX Fix this. We need to inject a "virtual" session layer right + * XXX about here. + */ + + if (hid == crypto_drivers_num) + return EINVAL; + + /* Call the driver initialization routine */ + lid = hid; /* Pass the driver ID */ + err = crypto_drivers[hid].cc_newsession(&lid, cri); + if (err == 0) + { + (*sid) = hid; + (*sid) <<= 31; + (*sid) |= (lid & 0xffffffff); + crypto_drivers[hid].cc_sessions++; + } + + return err; +} + +/* + * Delete an existing session (or a reserved session on an unregistered + * driver). + */ +int +crypto_freesession(u_int64_t sid) +{ + u_int32_t hid, lid; + int err = 0; + + if (crypto_drivers == NULL) + return EINVAL; + + /* Determine two IDs */ + hid = (sid >> 31) & 0xffffffff; + lid = sid & 0xffffffff; + + if (hid >= crypto_drivers_num) + return ENOENT; + + if (crypto_drivers[hid].cc_sessions) + crypto_drivers[hid].cc_sessions--; + + /* Call the driver cleanup routine, if available */ + if (crypto_drivers[hid].cc_freesession) + err = crypto_drivers[hid].cc_freesession(lid); + + /* + * If this was the last session of a driver marked as invalid, make + * the entry available for reuse. + */ + if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) && + (crypto_drivers[hid].cc_sessions == 0)) + bzero(&crypto_drivers[hid], sizeof(struct cryptocap)); + + return err; +} + +/* + * Find an empty slot. + */ +int32_t +crypto_get_driverid(void) +{ + struct cryptocap *newdrv; + int i; + + if (crypto_drivers_num == 0) + { + crypto_drivers_num = CRYPTO_DRIVERS_INITIAL; + MALLOC(crypto_drivers, struct cryptocap *, + crypto_drivers_num * sizeof(struct cryptocap), M_XDATA, + M_DONTWAIT); + if (crypto_drivers == NULL) + { + crypto_drivers_num = 0; + return -1; + } + + bzero(crypto_drivers, crypto_drivers_num * sizeof(struct cryptocap)); + } + + for (i = 0; i < crypto_drivers_num; i++) + if ((crypto_drivers[i].cc_process == NULL) && + !(crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) && + (crypto_drivers[i].cc_sessions == 0)) + return i; + + /* Out of entries, allocate some more */ + if (i == crypto_drivers_num) + { + /* Be careful about wrap-around */ + if (2 * crypto_drivers_num <= crypto_drivers_num) + return -1; + + MALLOC(newdrv, struct cryptocap *, + 2 * crypto_drivers_num * sizeof(struct cryptocap), + M_XDATA, M_DONTWAIT); + if (newdrv == NULL) + return -1; + + bcopy(crypto_drivers, newdrv, + crypto_drivers_num * sizeof(struct cryptocap)); + bzero(&newdrv[crypto_drivers_num], + crypto_drivers_num * sizeof(struct cryptocap)); + crypto_drivers_num *= 2; + return i; + } + + /* Shouldn't really get here... */ + return -1; +} + +/* + * Register a crypto driver. It should be called once for each algorithm + * supported by the driver. + */ +int +crypto_register(u_int32_t driverid, int alg, void *newses, void *freeses, + void *process) +{ + if ((driverid >= crypto_drivers_num) || (alg <= 0) || + (alg > CRYPTO_ALGORITHM_MAX) || (crypto_drivers == NULL)) + return EINVAL; + + /* + * XXX Do some performance testing to determine placing. + * XXX We probably need an auxiliary data structure that describes + * XXX relative performances. + */ + + crypto_drivers[driverid].cc_alg[alg] = 1; + + if (crypto_drivers[driverid].cc_process == NULL) + { + crypto_drivers[driverid].cc_newsession = + (int (*) (u_int32_t *, struct cryptoini *)) newses; + crypto_drivers[driverid].cc_process = + (int (*) (struct cryptop *)) process; + crypto_drivers[driverid].cc_freesession = + (int (*) (u_int32_t)) freeses; + } + + return 0; +} + +/* + * Unregister a crypto driver. If there are pending sessions using it, + * leave enough information around so that subsequent calls using those + * sessions will correctly detect the driver being unregistered and reroute + * the request. + */ +int +crypto_unregister(u_int32_t driverid, int alg) +{ + u_int32_t ses; + int i; + + /* Sanity checks */ + if ((driverid >= crypto_drivers_num) || (alg <= 0) || + (alg > CRYPTO_ALGORITHM_MAX) || (crypto_drivers == NULL) || + (crypto_drivers[driverid].cc_alg[alg] == 0)) + return EINVAL; + + crypto_drivers[driverid].cc_alg[alg] = 0; + + /* Was this the last algorithm ? */ + for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++) + if (crypto_drivers[driverid].cc_alg[i] != 0) + break; + + if (i == CRYPTO_ALGORITHM_MAX + 1) + { + ses = crypto_drivers[driverid].cc_sessions; + bzero(&crypto_drivers[driverid], sizeof(struct cryptocap)); + + if (ses != 0) + { + /* If there are pending sessions, just mark as invalid */ + crypto_drivers[driverid].cc_flags |= CRYPTOCAP_F_CLEANUP; + crypto_drivers[driverid].cc_sessions = ses; + } + } + + return 0; +} + +/* + * Dispatch a crypto request to the appropriate crypto devices. + */ +int +crypto_dispatch(struct cryptop *crp) +{ + struct cryptodesc *crd; + u_int64_t nid; + u_int32_t hid; + + /* Sanity checks */ + if ((crp == NULL) || (crp->crp_callback == NULL)) + return EINVAL; + + if ((crp->crp_desc == NULL) || (crypto_drivers == NULL)) + { + crp->crp_etype = EINVAL; + return crp->crp_callback(crp); + } + + hid = (crp->crp_sid >> 31) & 0xffffffff; + + if (hid >= crypto_drivers_num) + { + /* Migrate session */ + for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next) + crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI); + if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI)) == 0) + crp->crp_sid = nid; + + crp->crp_etype = EAGAIN; + return crp->crp_callback(crp); + } + + if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) + crypto_freesession(crp->crp_sid); + + if (crypto_drivers[hid].cc_process == NULL) + { + /* Migrate session */ + for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next) + crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI); + if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI)) == 0) + crp->crp_sid = nid; + + crp->crp_etype = EAGAIN; + return crp->crp_callback(crp); + } + + return crypto_drivers[hid].cc_process(crp); +} + +/* + * Release a set of crypto descriptors. + */ +void +crypto_freereq(struct cryptop *crp) +{ + struct cryptodesc *crd; + + if (crp == NULL) + return; + + while ((crd = crp->crp_desc) != NULL) + { + crp->crp_desc = crd->crd_next; + + if (crypto_queue_num + 1 > crypto_queue_max) + FREE(crd, M_XDATA); + else + { + crd->crd_next = cryptodesc_queue; + cryptodesc_queue = crd; + crypto_queue_num++; + } + } + + if (crypto_queue_num + 1 > crypto_queue_max) + FREE(crp, M_XDATA); + else + { + crp->crp_next = cryptop_queue; + cryptop_queue = crp; + crypto_queue_num++; + } +} + +/* + * Acquire a set of crypto descriptors. + */ +struct cryptop * +crypto_getreq(int num) +{ + struct cryptodesc *crd; + struct cryptop *crp; + + if (cryptop_queue == NULL) + { + MALLOC(crp, struct cryptop *, sizeof(struct cryptop), M_XDATA, + M_DONTWAIT); + if (crp == NULL) + return NULL; + } + else + { + crp = cryptop_queue; + cryptop_queue = crp->crp_next; + crypto_queue_num--; + } + + bzero(crp, sizeof(struct cryptop)); + + while (num--) + { + if (cryptodesc_queue == NULL) + { + MALLOC(crd, struct cryptodesc *, sizeof(struct cryptodesc), + M_XDATA, M_DONTWAIT); + if (crd == NULL) + { + crypto_freereq(crp); + return NULL; + } + } + else + { + crd = cryptodesc_queue; + cryptodesc_queue = crd->crd_next; + crypto_queue_num--; + } + + bzero(crd, sizeof(struct cryptodesc)); + crd->crd_next = crp->crp_desc; + crp->crp_desc = crd; + } + + return crp; +} + +/* + * Apply a symmetric encryption/decryption algorithm. + */ +int +swcr_encdec(struct cryptodesc *crd, struct swcr_data *sw, caddr_t buf, + int outtype) +{ + unsigned char iv[EALG_MAX_BLOCK_LEN], blk[EALG_MAX_BLOCK_LEN], *idat; + unsigned char *ivp, piv[EALG_MAX_BLOCK_LEN]; + struct enc_xform *exf; + int i, k, j, blks; + struct mbuf *m; + + exf = sw->sw_exf; + blks = exf->blocksize; + + /* Check for non-padded data */ + if (crd->crd_len % blks) + return EINVAL; + + if (outtype == CRYPTO_BUF_CONTIG) + { + if (crd->crd_flags & CRD_F_ENCRYPT) + { + /* Inject IV */ + if (crd->crd_flags & CRD_F_HALFIV) + { + /* "Cook" half-IV */ + for (k = 0; k < blks / 2; k++) + sw->sw_iv[(blks / 2) + k] = ~sw->sw_iv[k]; + + bcopy(sw->sw_iv, buf + crd->crd_inject, blks / 2); + } + else + bcopy(sw->sw_iv, buf + crd->crd_inject, blks); + + for (i = crd->crd_skip; + i < crd->crd_skip + crd->crd_len; + i += blks) + { + /* XOR with the IV/previous block, as appropriate. */ + if (i == crd->crd_skip) + for (k = 0; k < blks; k++) + buf[i + k] ^= sw->sw_iv[k]; + else + for (k = 0; k < blks; k++) + buf[i + k] ^= buf[i + k - blks]; + + exf->encrypt(sw->sw_kschedule, buf + i); + } + + /* Keep the last block */ + bcopy(buf + crd->crd_len - blks, sw->sw_iv, blks); + } + else /* Decrypt */ + { + /* Copy the IV off the buffer */ + bcopy(buf + crd->crd_inject, sw->sw_iv, blks); + + /* "Cook" half-IV */ + if (crd->crd_flags & CRD_F_HALFIV) + for (k = 0; k < blks / 2; k++) + sw->sw_iv[(blks / 2) + k] = ~sw->sw_iv[k]; + + /* + * Start at the end, so we don't need to keep the encrypted + * block as the IV for the next block. + */ + for (i = crd->crd_skip + crd->crd_len - blks; + i >= crd->crd_skip; + i -= blks) + { + exf->decrypt(sw->sw_kschedule, buf + i); + + /* XOR with the IV/previous block, as appropriate */ + if (i == crd->crd_skip) + for (k = 0; k < blks; k++) + buf[i + k] ^= sw->sw_iv[k]; + else + for (k = 0; k < blks; k++) + buf[i + k] ^= buf[i + k - blks]; + } + } + + return 0; /* Done with contiguous buffer encryption/decryption */ + } + else /* mbuf */ + { + m = (struct mbuf *) buf; + + /* Initialize the IV */ + if (crd->crd_flags & CRD_F_ENCRYPT) + { + bcopy(sw->sw_iv, iv, blks); + + /* "Cook" half-IV */ + if (crd->crd_flags & CRD_F_HALFIV) + for (k = 0; k < blks / 2; k++) + iv[(blks / 2) + k] = ~iv[k]; + + /* Inject IV */ + m_copyback(m, crd->crd_inject, blks, iv); + } + else + { + m_copydata(m, crd->crd_inject, blks, iv); /* Get IV off mbuf */ + + /* "Cook" half-IV */ + if (crd->crd_flags & CRD_F_HALFIV) + for (k = 0; k < blks / 2; k++) + iv[(blks / 2) + k] = ~iv[k]; + } + + ivp = iv; + + /* Find beginning of data */ + m = m_getptr(m, crd->crd_skip, &k); + if (m == NULL) + return EINVAL; + + i = crd->crd_len; + + while (i > 0) + { + /* + * If there's insufficient data at the end of an mbuf, we have + * to do some copying. + */ + if ((m->m_len < k + blks) && (m->m_len != k)) + { + m_copydata(m, k, blks, blk); + + /* Actual encryption/decryption */ + if (crd->crd_flags & CRD_F_ENCRYPT) + { + /* XOR with previous block */ + for (j = 0; j < blks; j++) + blk[j] ^= ivp[j]; + + exf->encrypt(sw->sw_kschedule, blk); + + /* Keep encrypted block for XOR'ing with next block */ + bcopy(blk, iv, blks); + ivp = iv; + } + else /* decrypt */ + { + /* Keep encrypted block for XOR'ing with next block */ + if (ivp == iv) + bcopy(blk, piv, blks); + else + bcopy(blk, iv, blks); + + exf->decrypt(sw->sw_kschedule, blk); + + /* XOR with previous block */ + for (j = 0; j < blks; j++) + blk[j] ^= ivp[j]; + + if (ivp == iv) + bcopy(piv, iv, blks); + else + ivp = iv; + } + + /* Copy back decrypted block */ + m_copyback(m, k, blks, blk); + + /* Advance pointer */ + m = m_getptr(m, k + blks, &k); + if (m == NULL) + return EINVAL; + + i -= blks; + + /* Could be done... */ + if (i == 0) + break; + } + + /* Skip possibly empty mbufs */ + if (k == m->m_len) + { + for (m = m->m_next; m && m->m_len == 0; m = m->m_next) + ; + + k = 0; + } + + /* Sanity check */ + if (m == NULL) + return EINVAL; + + /* + * Warning: idat may point to garbage here, but we only use it + * in the while() loop, only if there are indeed enough data. + */ + idat = mtod(m, unsigned char *) + k; + + while ((m->m_len >= k + blks) && (i > 0)) + { + if (crd->crd_flags & CRD_F_ENCRYPT) + { + /* XOR with previous block/IV */ + for (j = 0; j < blks; j++) + idat[j] ^= ivp[j]; + + exf->encrypt(sw->sw_kschedule, idat); + ivp = idat; + } + else /* decrypt */ + { + /* + * Keep encrypted block to be used in next block's + * processing. + */ + if (ivp == iv) + bcopy(idat, piv, blks); + else + bcopy(idat, iv, blks); + + exf->decrypt(sw->sw_kschedule, idat); + + /* XOR with previous block/IV */ + for (j = 0; j < blks; j++) + idat[j] ^= ivp[j]; + + if (ivp == iv) + bcopy(piv, iv, blks); + else + ivp = iv; + } + + idat += blks; + k += blks; + i -= blks; + } + } + + /* Keep the last block */ + if (crd->crd_flags & CRD_F_ENCRYPT) + bcopy(ivp, sw->sw_iv, blks); + + return 0; /* Done with mbuf encryption/decryption */ + } + + /* Unreachable */ + return EINVAL; +} + +/* + * Compute keyed-hash authenticator. + */ +int +swcr_authcompute(struct cryptodesc *crd, struct swcr_data *sw, + caddr_t buf, int outtype) +{ + unsigned char aalg[AALG_MAX_RESULT_LEN]; + struct auth_hash *axf; + union authctx ctx; + int err; + + if (sw->sw_ictx == 0) + return EINVAL; + + axf = sw->sw_axf; + + bcopy(sw->sw_ictx, &ctx, axf->ctxsize); + + if (outtype == CRYPTO_BUF_CONTIG) + { + axf->Update(&ctx, buf + crd->crd_skip, crd->crd_len); + axf->Final(aalg, &ctx); + } + else + { + err = m_apply((struct mbuf *) buf, crd->crd_skip, + crd->crd_len, + (int (*)(caddr_t, caddr_t, unsigned int)) axf->Update, + (caddr_t) &ctx); + if (err) + return err; + + axf->Final(aalg, &ctx); + } + + /* HMAC processing */ + switch (sw->sw_alg) + { + case CRYPTO_MD5_HMAC96: + case CRYPTO_SHA1_HMAC96: + case CRYPTO_RIPEMD160_HMAC96: + if (sw->sw_octx == NULL) + return EINVAL; + + bcopy(sw->sw_octx, &ctx, axf->ctxsize); + axf->Update(&ctx, aalg, axf->hashsize); + axf->Final(aalg, &ctx); + break; + } + + /* Inject the authentication data */ + if (outtype == CRYPTO_BUF_CONTIG) + bcopy(aalg, buf + crd->crd_inject, axf->authsize); + else + m_copyback((struct mbuf *) buf, crd->crd_inject, axf->authsize, aalg); + + return 0; +} + +/* + * Generate a new software session. + */ +int +swcr_newsession(u_int32_t *sid, struct cryptoini *cri) +{ + struct swcr_data **swd; + struct auth_hash *axf; + struct enc_xform *txf; + u_int32_t i; + int k; + + if ((sid == NULL) || (cri == NULL)) + return EINVAL; + + if (swcr_sessions) + for (i = 1; i < swcr_sesnum; i++) + if (swcr_sessions[i] == NULL) + break; + + if ((swcr_sessions == NULL) || (i == swcr_sesnum)) + { + if (swcr_sessions == NULL) + { + i = 1; /* We leave swcr_sessions[0] empty */ + swcr_sesnum = CRYPTO_SW_SESSIONS; + } + else + swcr_sesnum *= 2; + + MALLOC(swd, struct swcr_data **, + swcr_sesnum * sizeof(struct swcr_data *), M_XDATA, M_DONTWAIT); + if (swd == NULL) + { + /* Reset session number */ + if (swcr_sesnum == CRYPTO_SW_SESSIONS) + swcr_sesnum = 0; + else + swcr_sesnum /= 2; + + return ENOBUFS; + } + + bzero(swd, swcr_sesnum * sizeof(struct swcr_data *)); + + /* Copy existing sessions */ + if (swcr_sessions) + { + bcopy(swcr_sessions, swd, + (swcr_sesnum / 2) * sizeof(struct swcr_data *)); + FREE(swcr_sessions, M_XDATA); + } + + swcr_sessions = swd; + } + + swd = &swcr_sessions[i]; + *sid = i; + + while (cri) + { + MALLOC(*swd, struct swcr_data *, sizeof(struct swcr_data), M_XDATA, + M_DONTWAIT); + if (*swd == NULL) + { + swcr_freesession(i); + return ENOBUFS; + } + + bzero(*swd, sizeof(struct swcr_data)); + + switch (cri->cri_alg) + { + case CRYPTO_DES_CBC: + txf = &enc_xform_des; + goto enccommon; + + case CRYPTO_3DES_CBC: + txf = &enc_xform_3des; + goto enccommon; + + case CRYPTO_BLF_CBC: + txf = &enc_xform_blf; + goto enccommon; + + case CRYPTO_CAST_CBC: + txf = &enc_xform_cast5; + goto enccommon; + + case CRYPTO_SKIPJACK_CBC: + txf = &enc_xform_skipjack; + + enccommon: + txf->setkey(&((*swd)->sw_kschedule), cri->cri_key, + cri->cri_klen / 8); + MALLOC((*swd)->sw_iv, u_int8_t *, txf->blocksize, M_XDATA, + M_DONTWAIT); + if ((*swd)->sw_iv == NULL) + { + swcr_freesession(i); + return ENOBUFS; + } + + (*swd)->sw_exf = txf; + + get_random_bytes((*swd)->sw_iv, txf->blocksize); + break; + + case CRYPTO_MD5_HMAC96: + axf = &auth_hash_hmac_md5_96; + goto authcommon; + + case CRYPTO_SHA1_HMAC96: + axf = &auth_hash_hmac_sha1_96; + goto authcommon; + + case CRYPTO_RIPEMD160_HMAC96: + axf = &auth_hash_hmac_ripemd_160_96; + + authcommon: + MALLOC((*swd)->sw_ictx, u_int8_t *, axf->ctxsize, M_XDATA, + M_DONTWAIT); + if ((*swd)->sw_ictx == NULL) + { + swcr_freesession(i); + return ENOBUFS; + } + + MALLOC((*swd)->sw_octx, u_int8_t *, axf->ctxsize, M_XDATA, + M_DONTWAIT); + if ((*swd)->sw_octx == NULL) + { + swcr_freesession(i); + return ENOBUFS; + } + + for (k = 0; k < cri->cri_klen / 8; k++) + cri->cri_key[k] ^= HMAC_IPAD_VAL; + + axf->Init((*swd)->sw_ictx); + axf->Update((*swd)->sw_ictx, cri->cri_key, + cri->cri_klen / 8); + axf->Update((*swd)->sw_ictx, hmac_ipad_buffer, + HMAC_BLOCK_LEN - (cri->cri_klen / 8)); + + for (k = 0; k < cri->cri_klen / 8; k++) + cri->cri_key[k] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL); + + axf->Init((*swd)->sw_octx); + axf->Update((*swd)->sw_octx, cri->cri_key, + cri->cri_klen / 8); + axf->Update((*swd)->sw_octx, hmac_opad_buffer, + HMAC_BLOCK_LEN - (cri->cri_klen / 8)); + + for (k = 0; k < cri->cri_klen / 8; k++) + cri->cri_key[k] ^= HMAC_OPAD_VAL; + + (*swd)->sw_axf = axf; + break; + + case CRYPTO_MD5_KPDK: + axf = &auth_hash_key_md5; + goto auth2common; + + case CRYPTO_SHA1_KPDK: + axf = &auth_hash_key_sha1; + + auth2common: + MALLOC((*swd)->sw_ictx, u_int8_t *, axf->ctxsize, M_XDATA, + M_DONTWAIT); + if ((*swd)->sw_ictx == NULL) + { + swcr_freesession(i); + return ENOBUFS; + } + + axf->Init((*swd)->sw_ictx); + axf->Update((*swd)->sw_ictx, cri->cri_key, + cri->cri_klen / 8); + axf->Final(NULL, (*swd)->sw_ictx); + + (*swd)->sw_axf = axf; + break; + + default: + swcr_freesession(i); + return EINVAL; + } + + (*swd)->sw_alg = cri->cri_alg; + cri = cri->cri_next; + swd = &((*swd)->sw_next); + } + + return 0; +} + +/* + * Free a session. + */ +int +swcr_freesession(u_int32_t sid) +{ + struct swcr_data *swd; + struct enc_xform *txf; + struct auth_hash *axf; + + if ((sid > swcr_sesnum) || (swcr_sessions == NULL) || + (swcr_sessions[sid] == NULL)) + return EINVAL; + + /* Silently accept and return */ + if (sid == 0) + return 0; + + while ((swd = swcr_sessions[sid]) != NULL) + { + swcr_sessions[sid] = swd->sw_next; + + switch (swd->sw_alg) + { + case CRYPTO_DES_CBC: + case CRYPTO_3DES_CBC: + case CRYPTO_BLF_CBC: + case CRYPTO_CAST_CBC: + case CRYPTO_SKIPJACK_CBC: + txf = swd->sw_exf; + + if (swd->sw_kschedule) + txf->zerokey(&(swd->sw_kschedule)); + + if (swd->sw_iv) + FREE(swd->sw_iv, M_XDATA); + break; + + case CRYPTO_MD5_HMAC96: + case CRYPTO_SHA1_HMAC96: + case CRYPTO_RIPEMD160_HMAC96: + case CRYPTO_MD5_KPDK: + case CRYPTO_SHA1_KPDK: + axf = swd->sw_axf; + + if (swd->sw_ictx) + { + bzero(swd->sw_ictx, axf->ctxsize); + FREE(swd->sw_ictx, M_XDATA); + } + + if (swd->sw_octx) + { + bzero(swd->sw_octx, axf->ctxsize); + FREE(swd->sw_octx, M_XDATA); + } + break; + } + + FREE(swd, M_XDATA); + } + + return 0; +} + +/* + * Process a software request. + */ +int +swcr_process(struct cryptop *crp) +{ + struct cryptodesc *crd; + struct swcr_data *sw; + u_int32_t lid; + u_int64_t nid; + int type; + + /* Some simple sanity checks */ + if ((crp == NULL) || (crp->crp_callback == NULL)) + return EINVAL; + + if ((crp->crp_desc == NULL) || (crp->crp_buf == NULL)) + { + crp->crp_etype = EINVAL; + goto done; + } + + lid = crp->crp_sid & 0xffffffff; + if ((lid >= swcr_sesnum) || (lid == 0) || (swcr_sessions[lid] == NULL)) + { + crp->crp_etype = ENOENT; + goto done; + } + + if (crp->crp_flags & CRYPTO_F_IMBUF) + type = CRYPTO_BUF_MBUF; + else + type = CRYPTO_BUF_CONTIG; + + /* Go through crypto descriptors, processing as we go */ + for (crd = crp->crp_desc; crd; crd = crd->crd_next) + { + /* + * Find the crypto context. + * + * XXX Note that the logic here prevents us from having + * XXX the same algorithm multiple times in a session + * XXX (or rather, we can but it won't give us the right + * XXX results). To do that, we'd need some way of differentiating + * XXX between the various instances of an algorithm (so we can + * XXX locate the correct crypto context). + */ + for (sw = swcr_sessions[lid]; + sw && sw->sw_alg != crd->crd_alg; + sw = sw->sw_next) + ; + + /* No such context ? */ + if (sw == NULL) + { + crp->crp_etype = EINVAL; + goto done; + } + + switch (sw->sw_alg) + { + case CRYPTO_DES_CBC: + case CRYPTO_3DES_CBC: + case CRYPTO_BLF_CBC: + case CRYPTO_CAST_CBC: + case CRYPTO_SKIPJACK_CBC: + if ((crp->crp_etype = swcr_encdec(crd, sw, crp->crp_buf, + type)) != 0) + goto done; + break; + + case CRYPTO_MD5_HMAC96: + case CRYPTO_SHA1_HMAC96: + case CRYPTO_RIPEMD160_HMAC96: + case CRYPTO_MD5_KPDK: + case CRYPTO_SHA1_KPDK: + if ((crp->crp_etype = swcr_authcompute(crd, sw, crp->crp_buf, + type)) != 0) + goto done; + break; + + default: /* Unknown/unsupported algorithm */ + crp->crp_etype = EINVAL; + goto done; + } + } + + done: + if (crp->crp_etype == ENOENT) + { + crypto_freesession(crp->crp_sid); /* Just in case */ + + /* Migrate session */ + for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next) + crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI); + if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI)) == 0) + crp->crp_sid = nid; + } + + return crp->crp_callback(crp); +} + +/* + * Initialize the driver, called from the kernel main(). + */ +void +swcr_init(void) +{ + swcr_id = crypto_get_driverid(); + if (swcr_id >= 0) + { + crypto_register(swcr_id, CRYPTO_DES_CBC, swcr_newsession, + swcr_freesession, swcr_process); + crypto_register(swcr_id, CRYPTO_3DES_CBC, NULL, NULL, NULL); + crypto_register(swcr_id, CRYPTO_BLF_CBC, NULL, NULL, NULL); + crypto_register(swcr_id, CRYPTO_CAST_CBC, NULL, NULL, NULL); + crypto_register(swcr_id, CRYPTO_SKIPJACK_CBC, NULL, NULL, NULL); + crypto_register(swcr_id, CRYPTO_MD5_HMAC96, NULL, NULL, NULL); + crypto_register(swcr_id, CRYPTO_SHA1_HMAC96, NULL, NULL, NULL); + crypto_register(swcr_id, CRYPTO_RIPEMD160_HMAC96, NULL, NULL, NULL); + crypto_register(swcr_id, CRYPTO_MD5_KPDK, NULL, NULL, NULL); + crypto_register(swcr_id, CRYPTO_SHA1_KPDK, NULL, NULL, NULL); + return; + } + + /* This should never happen */ + panic("Software crypto device cannot initialize!"); +} diff --git a/sys/crypto/crypto.h b/sys/crypto/crypto.h new file mode 100644 index 00000000000..ba1ad389c23 --- /dev/null +++ b/sys/crypto/crypto.h @@ -0,0 +1,200 @@ +/* + * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) + * + * This code was written by Angelos D. Keromytis in Athens, Greece, in + * February 2000. Network Security Technologies Inc. (NSTI) kindly + * supported the development of this code. + * + * Copyright (c) 2000 Angelos D. Keromytis + * + * Permission to use, copy, and modify this software without fee + * is hereby granted, provided that this entire notice is included in + * all source code copies of any software which is or includes a copy or + * modification of this software. + * + * 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. + */ + +#ifndef _CRYPTO_CRYPTO_H_ +#define _CRYPTO_CRYPTO_H_ + +/* Some initial values */ +#define CRYPTO_DRIVERS_INITIAL 4 +#define CRYPTO_SW_SESSIONS 32 + +#ifndef CRYPTO_MAX_CACHED +#define CRYPTO_MAX_CACHED 128 +#endif + +/* HMAC values */ +#define HMAC_BLOCK_LEN 64 +#define HMAC_IPAD_VAL 0x36 +#define HMAC_OPAD_VAL 0x5C + +/* Encryption algorithm block sizes */ +#define DES_BLOCK_LEN 8 +#define DES3_BLOCK_LEN 8 +#define BLOWFISH_BLOCK_LEN 8 +#define SKIPJACK_BLOCK_LEN 8 +#define CAST128_BLOCK_LEN 8 +#define EALG_MAX_BLOCK_LEN 8 /* Keep this updated */ + +/* Maximum hash algorithm result length */ +#define AALG_MAX_RESULT_LEN 20 /* Keep this updated */ + +#define CRYPTO_DES_CBC 1 +#define CRYPTO_3DES_CBC 2 +#define CRYPTO_BLF_CBC 3 +#define CRYPTO_CAST_CBC 4 +#define CRYPTO_SKIPJACK_CBC 5 +#define CRYPTO_MD5_HMAC96 6 +#define CRYPTO_SHA1_HMAC96 7 +#define CRYPTO_RIPEMD160_HMAC96 8 +#define CRYPTO_MD5_KPDK 9 +#define CRYPTO_SHA1_KPDK 10 + +#define CRYPTO_ALGORITHM_MAX 10 /* Keep this updated */ + +/* Standard initialization structure beginning */ +struct cryptoini +{ + int cri_alg; /* Algorithm to use */ + int cri_klen; /* Key length, in bits */ + int cri_rnd; /* Algorithm rounds, where relevant */ + caddr_t cri_key; /* key to use */ + struct cryptoini *cri_next; +}; + +/* Describe boundaries of a single crypto operation */ +struct cryptodesc +{ + int crd_skip; /* How many bytes to ignore from start */ + int crd_len; /* How many bytes to process */ + int crd_inject; /* Where to inject results, if applicable */ + int crd_flags; + +#define CRD_F_ENCRYPT 0x1 /* Set when doing encryption */ +#define CRD_F_HALFIV 0x2 + + struct cryptoini CRD_INI; /* Initialization/context data */ +#define crd_key CRD_INI.cri_key +#define crd_rnd CRD_INI.cri_rnd +#define crd_alg CRD_INI.cri_alg +#define crd_klen CRD_INI.cri_klen + + struct cryptodesc *crd_next; +}; + +/* Structure describing complete operation */ +struct cryptop +{ + u_int64_t crp_sid; /* Session ID */ + int crp_ilen; /* Input data total length */ + int crp_olen; /* Result total length (unused for now) */ + int crp_alloctype; /* Type of buf to allocate if needed */ + + int crp_etype; /* Error type (zero means no error). + * All error codes except EAGAIN + * indicate possible data corruption (as in, + * the data have been touched). On all + * errors, the crp_sid may have changed + * (reset to a new one), so the caller + * should always check and use the new + * value on future requests. + */ + int crp_flags; + +#define CRYPTO_F_IMBUF 0x0001 /* Input is an mbuf chain, otherwise contig */ + + caddr_t crp_buf; /* Data to be processed */ + + caddr_t crp_opaque1;/* Opaque pointer, passed along */ + caddr_t crp_opaque2;/* Opaque pointer, passed along */ + caddr_t crp_opaque3;/* Opaque pointer, passed along */ + caddr_t crp_opaque4;/* Opaque pointer, passed along */ + + struct cryptodesc *crp_desc; /* Linked list of processing descriptors */ + + int (*crp_callback) (struct cryptop *); /* Callback function */ + + struct cryptop *crp_next; +}; + +#define CRYPTO_BUF_CONTIG 0x1 +#define CRYPTO_BUF_MBUF 0x2 + +#define CRYPTO_OP_DECRYPT 0x0 +#define CRYPTO_OP_ENCRYPT 0x1 + +/* Crypto capabilities structure */ +struct cryptocap +{ + u_int32_t cc_sessions; + + u_int8_t cc_alg[CRYPTO_ALGORITHM_MAX + 1]; /* Supported */ + u_int8_t cc_flags; +#define CRYPTOCAP_F_CLEANUP 0x1 + + int (*cc_newsession) (u_int32_t *, struct cryptoini *); + int (*cc_process) (struct cryptop *); + int (*cc_freesession) (u_int32_t); +}; + +/* Software session entry */ +struct swcr_data +{ + int sw_alg; /* Algorithm */ + union + { + struct + { + u_int8_t *SW_ictx; + u_int8_t *SW_octx; + struct auth_hash *SW_axf; + } SWCR_AUTH; + + struct + { + u_int8_t *SW_kschedule; + u_int8_t *SW_iv; + struct enc_xform *SW_exf; + } SWCR_ENC; + } SWCR_UN; + +#define sw_ictx SWCR_UN.SWCR_AUTH.SW_ictx +#define sw_octx SWCR_UN.SWCR_AUTH.SW_octx +#define sw_axf SWCR_UN.SWCR_AUTH.SW_axf +#define sw_kschedule SWCR_UN.SWCR_ENC.SW_kschedule +#define sw_iv SWCR_UN.SWCR_ENC.SW_iv +#define sw_exf SWCR_UN.SWCR_ENC.SW_exf + + struct swcr_data *sw_next; +}; + +#ifdef _KERNEL +extern u_int8_t hmac_ipad_buffer[64]; +extern u_int8_t hmac_opad_buffer[64]; + +extern int swcr_encdec(struct cryptodesc *, struct swcr_data *, caddr_t, int); +extern int swcr_authcompute(struct cryptodesc *, struct swcr_data *, + caddr_t, int); +extern int swcr_process(struct cryptop *); +extern int swcr_newsession(u_int32_t *, struct cryptoini *); +extern int swcr_freesession(u_int32_t); +extern void swcr_init(void); + +extern int crypto_newsession(u_int64_t *, struct cryptoini *); +extern int crypto_freesession(u_int64_t); +extern int crypto_dispatch(struct cryptop *); +extern int crypto_register(u_int32_t, int, void *, void *, void *); +extern int crypto_unregister(u_int32_t, int); +extern int32_t crypto_get_driverid(void); + +extern struct cryptop *crypto_getreq(int); +extern void crypto_freereq(struct cryptop *); +#endif /* _KERNEL */ +#endif /* _CRYPTO_CRYPTO_H_ */ diff --git a/sys/crypto/xform.c b/sys/crypto/xform.c new file mode 100644 index 00000000000..98bb2017494 --- /dev/null +++ b/sys/crypto/xform.c @@ -0,0 +1,354 @@ +/* $OpenBSD: xform.c,v 1.1 2000/03/17 10:25:21 angelos 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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); + +void des_set_key(caddr_t, caddr_t); +void des1_setkey(u_int8_t **, u_int8_t *, int); +void des3_setkey(u_int8_t **, u_int8_t *, int); +void blf_setkey(u_int8_t **, u_int8_t *, int); +void cast5_setkey(u_int8_t **, u_int8_t *, int); +void skipjack_setkey(u_int8_t **, u_int8_t *, int); +void des1_encrypt(caddr_t, u_int8_t *); +void des3_encrypt(caddr_t, u_int8_t *); +void blf_encrypt(caddr_t, u_int8_t *); +void cast5_encrypt(caddr_t, u_int8_t *); +void skipjack_encrypt(caddr_t, u_int8_t *); +void des1_decrypt(caddr_t, u_int8_t *); +void des3_decrypt(caddr_t, u_int8_t *); +void blf_decrypt(caddr_t, u_int8_t *); +void cast5_decrypt(caddr_t, u_int8_t *); +void skipjack_decrypt(caddr_t, u_int8_t *); +void des1_zerokey(u_int8_t **); +void des3_zerokey(u_int8_t **); +void blf_zerokey(u_int8_t **); +void cast5_zerokey(u_int8_t **); +void skipjack_zerokey(u_int8_t **); + +int MD5Update_int(void *, u_int8_t *, u_int16_t); +int SHA1Update_int(void *, u_int8_t *, u_int16_t); +int RMD160Update_int(void *, u_int8_t *, u_int16_t); + +/* Encryption instances */ +struct enc_xform enc_xform_des = +{ + CRYPTO_DES_CBC, "DES", + 8, 8, 8, 8, + des1_encrypt, + des1_decrypt, + des1_setkey, + des1_zerokey, +}; + +struct enc_xform enc_xform_3des = +{ + CRYPTO_3DES_CBC, "3DES", + 8, 24, 24, 8, + des3_encrypt, + des3_decrypt, + des3_setkey, + des3_zerokey +}; + +struct enc_xform enc_xform_blf = +{ + CRYPTO_BLF_CBC, "Blowfish", + 8, 5, 56 /* 448 bits, max key */, 8, + blf_encrypt, + blf_decrypt, + blf_setkey, + blf_zerokey +}; + +struct enc_xform enc_xform_cast5 = +{ + CRYPTO_CAST_CBC, "CAST-128", + 8, 5, 16, 8, + cast5_encrypt, + cast5_decrypt, + cast5_setkey, + cast5_zerokey +}; + +struct enc_xform enc_xform_skipjack = +{ + CRYPTO_SKIPJACK_CBC, "Skipjack", + 8, 10, 10, 8, + skipjack_encrypt, + skipjack_decrypt, + skipjack_setkey, + skipjack_zerokey +}; + +/* Authentication instances */ +struct auth_hash auth_hash_hmac_md5_96 = +{ + CRYPTO_MD5_HMAC96, "HMAC-MD5-96", + 16, 16, 12, sizeof(MD5_CTX), + (void (*) (void *)) MD5Init, MD5Update_int, + (void (*) (u_int8_t *, void *)) MD5Final +}; + +struct auth_hash auth_hash_hmac_sha1_96 = +{ + CRYPTO_SHA1_HMAC96, "HMAC-SHA1-96", + 20, 20, 12, sizeof(SHA1_CTX), + (void (*) (void *)) SHA1Init, SHA1Update_int, + (void (*) (u_int8_t *, void *)) SHA1Final +}; + +struct auth_hash auth_hash_hmac_ripemd_160_96 = +{ + CRYPTO_RIPEMD160_HMAC96, "HMAC-RIPEMD-160-96", + 20, 20, 12, sizeof(RMD160_CTX), + (void (*)(void *)) RMD160Init, RMD160Update_int, + (void (*)(u_int8_t *, void *)) RMD160Final +}; + +struct auth_hash auth_hash_key_md5 = +{ + CRYPTO_MD5_KPDK, "Keyed MD5", + 0, 16, 16, sizeof(MD5_CTX), + (void (*)(void *)) MD5Init, MD5Update_int, + (void (*)(u_int8_t *, void *)) MD5Final +}; + +struct auth_hash auth_hash_key_sha1 = +{ + CRYPTO_SHA1_KPDK, "Keyed SHA1", + 0, 20, 20, sizeof(SHA1_CTX), + (void (*)(void *)) SHA1Init, SHA1Update_int, + (void (*)(u_int8_t *, void *)) SHA1Final +}; + +/* + * Encryption wrapper routines. + */ +void +des1_encrypt(caddr_t key, u_int8_t *blk) +{ + des_ecb_encrypt(blk, blk, key, 1); +} + +void +des1_decrypt(caddr_t key, u_int8_t *blk) +{ + des_ecb_encrypt(blk, blk, key, 0); +} + +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); +} + +void +des1_zerokey(u_int8_t **sched) +{ + bzero(*sched, 128); + FREE(*sched, M_XDATA); + *sched = NULL; +} + +void +des3_encrypt(caddr_t key, u_int8_t *blk) +{ + des_ecb3_encrypt(blk, blk, key, key + 128, key + 256, 1); +} + +void +des3_decrypt(caddr_t key, u_int8_t *blk) +{ + des_ecb3_encrypt(blk, blk, key + 256, key + 128, key, 0); +} + +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); +} + +void +des3_zerokey(u_int8_t **sched) +{ + bzero(*sched, 384); + FREE(*sched, M_XDATA); + *sched = NULL; +} + +void +blf_encrypt(caddr_t key, u_int8_t *blk) +{ + blf_ecb_encrypt((blf_ctx *) key, blk, 8); +} + +void +blf_decrypt(caddr_t key, u_int8_t *blk) +{ + blf_ecb_decrypt((blf_ctx *) key, blk, 8); +} + +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); +} + +void +blf_zerokey(u_int8_t **sched) +{ + bzero(*sched, sizeof(blf_ctx)); + FREE(*sched, M_XDATA); + *sched = NULL; +} + +void +cast5_encrypt(caddr_t key, u_int8_t *blk) +{ + cast_encrypt((cast_key *) key, blk, blk); +} + +void +cast5_decrypt(caddr_t key, u_int8_t *blk) +{ + cast_decrypt((cast_key *) key, blk, blk); +} + +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); +} + +void +cast5_zerokey(u_int8_t **sched) +{ + bzero(*sched, sizeof(cast_key)); + FREE(*sched, M_XDATA); + *sched = NULL; +} + +void +skipjack_encrypt(caddr_t key, u_int8_t *blk) +{ + skipjack_forwards(blk, blk, (u_int8_t **) key); +} + +void +skipjack_decrypt(caddr_t key, u_int8_t *blk) +{ + skipjack_backwards(blk, blk, (u_int8_t **) key); +} + +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); +} + +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; +} + +/* + * And now for auth. + */ + +int +RMD160Update_int(void *ctx, u_int8_t *buf, u_int16_t len) +{ + RMD160Update(ctx, buf, len); + return 0; +} + +int +MD5Update_int(void *ctx, u_int8_t *buf, u_int16_t len) +{ + MD5Update(ctx, buf, len); + return 0; +} + +int +SHA1Update_int(void *ctx, u_int8_t *buf, u_int16_t len) +{ + SHA1Update(ctx, buf, len); + return 0; +} + diff --git a/sys/crypto/xform.h b/sys/crypto/xform.h new file mode 100644 index 00000000000..7e220789a56 --- /dev/null +++ b/sys/crypto/xform.h @@ -0,0 +1,73 @@ +/* + * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) + * + * This code was written by Angelos D. Keromytis in Athens, Greece, in + * February 2000. Network Security Technologies Inc. (NSTI) kindly + * supported the development of this code. + * + * Copyright (c) 2000 Angelos D. Keromytis + * + * Permission to use, copy, and modify this software without fee + * is hereby granted, provided that this entire notice is included in + * all source code copies of any software which is or includes a copy or + * modification of this software. + * + * 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. + */ + +#ifndef _CRYPTO_XFORM_H_ +#define _CRYPTO_XFORM_H_ + +#include +#include +#include + +/* Declarations */ +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 *); + int (*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; + u_int16_t minkey, maxkey; + u_int32_t ivmask; /* Or all possible modes, zero iv = 1 */ + void (*encrypt) (caddr_t, u_int8_t *); + void (*decrypt) (caddr_t, u_int8_t *); + void (*setkey) (u_int8_t **, u_int8_t *, int len); + void (*zerokey) (u_int8_t **); +}; + +union authctx { + MD5_CTX md5ctx; + SHA1_CTX sha1ctx; + RMD160_CTX rmd160ctx; +}; + +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; + +extern struct auth_hash auth_hash_key_md5; +extern struct auth_hash auth_hash_key_sha1; +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; +#endif /* _CRYPTO_XFORM_H_ */ diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 17101988893..81d2e8d52ad 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: init_main.c,v 1.47 2000/02/28 18:04:08 provos Exp $ */ +/* $OpenBSD: init_main.c,v 1.48 2000/03/17 10:25:21 angelos Exp $ */ /* $NetBSD: init_main.c,v 1.84.4.1 1996/06/02 09:08:06 mrg Exp $ */ /* @@ -96,6 +96,10 @@ #include #include +#if defined(CRYPTO) +#include +#endif + #if defined(NFSSERVER) || defined(NFSCLIENT) extern void nfs_init __P((void)); #endif @@ -349,6 +353,10 @@ main(framep) for (pdev = pdevinit; pdev->pdev_attach != NULL; pdev++) (*pdev->pdev_attach)(pdev->pdev_count); +#ifdef CRYPTO + swcr_init(); +#endif /* CRYPTO */ + /* * Initialize protocols. Block reception of incoming packets * until everything is ready. diff --git a/sys/net/if_enc.c b/sys/net/if_enc.c index c944154cb5b..60a6d83145e 100644 --- a/sys/net/if_enc.c +++ b/sys/net/if_enc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_enc.c,v 1.20 2000/02/07 06:09:08 itojun Exp $ */ +/* $OpenBSD: if_enc.c,v 1.21 2000/03/17 10:25:21 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -263,32 +263,7 @@ struct ifnet *ifp; protoflag = tdb->tdb_dst.sa.sa_family; /* IPsec packet processing -- skip encapsulation */ - err = ipsp_process_packet(m, &mp, tdb, &protoflag, 1); - if ((mp == NULL) || err) - { - IF_DROP(&ifp->if_snd); - if (mp) - m_freem(mp); - continue; - } - else - { - m = mp; - mp = NULL; - } - -#ifdef INET - /* Send the packet on its way, no point checking for errors here */ - if (protoflag == AF_INET) - ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, NULL, NULL); -#endif /* INET */ - -#ifdef INET6 - /* Send the packet on its way, no point checking for errors here */ - if (protoflag == AF_INET6) - ip6_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, - NULL, NULL); -#endif /* INET6 */ + ipsp_process_packet(m, tdb, protoflag, 1); /* XXX Should find a way to avoid bridging-loops, some mbuf flag ? */ } diff --git a/sys/net/pfkeyv2.c b/sys/net/pfkeyv2.c index 844d2493350..4e8a6018b26 100644 --- a/sys/net/pfkeyv2.c +++ b/sys/net/pfkeyv2.c @@ -31,6 +31,9 @@ you didn't get a copy, you may request one from . #include #include #include +#include +#include +#include #define PFKEYV2_PROTOCOL 2 #define GETSPI_TRIES 10 @@ -208,10 +211,56 @@ export_sa(void **p, struct tdb *tdb) sadb_sa->sadb_sa_state = SADB_SASTATE_LARVAL; if (tdb->tdb_authalgxform) - sadb_sa->sadb_sa_auth = tdb->tdb_authalgxform->type; + { + switch (tdb->tdb_authalgxform->type) + { + case CRYPTO_MD5_HMAC96: + sadb_sa->sadb_sa_auth = SADB_AALG_MD5HMAC96; + break; + + case CRYPTO_SHA1_HMAC96: + sadb_sa->sadb_sa_auth = SADB_AALG_SHA1HMAC96; + break; + + case CRYPTO_RIPEMD160_HMAC96: + sadb_sa->sadb_sa_auth = SADB_X_AALG_RIPEMD160HMAC96; + break; + + case CRYPTO_MD5_KPDK: + sadb_sa->sadb_sa_auth = SADB_X_AALG_MD5; + break; + + case CRYPTO_SHA1_KPDK: + sadb_sa->sadb_sa_auth = SADB_X_AALG_SHA1; + break; + } + } if (tdb->tdb_encalgxform) - sadb_sa->sadb_sa_encrypt = tdb->tdb_encalgxform->type; + { + switch (tdb->tdb_encalgxform->type) + { + case CRYPTO_DES_CBC: + sadb_sa->sadb_sa_encrypt = SADB_EALG_DESCBC; + break; + + case CRYPTO_3DES_CBC: + sadb_sa->sadb_sa_encrypt = SADB_EALG_3DESCBC; + break; + + case CRYPTO_CAST_CBC: + sadb_sa->sadb_sa_encrypt = SADB_X_EALG_BLF; + break; + + case CRYPTO_BLF_CBC: + sadb_sa->sadb_sa_encrypt = SADB_X_EALG_CAST; + break; + + case CRYPTO_SKIPJACK_CBC: + sadb_sa->sadb_sa_encrypt = SADB_X_EALG_SKIPJACK; + break; + } + } if (tdb->tdb_flags & TDBF_PFS) sadb_sa->sadb_sa_flags |= SADB_SAFLAGS_PFS; @@ -2042,7 +2091,29 @@ pfkeyv2_acquire(struct tdb *tdb, int rekey) if (tdb->tdb_authalgxform) { - sadb_comb->sadb_comb_auth = tdb->tdb_authalgxform->type; + switch (tdb->tdb_authalgxform->type) + { + case CRYPTO_MD5_HMAC96: + sadb_comb->sadb_comb_auth = SADB_AALG_MD5HMAC96; + break; + + case CRYPTO_SHA1_HMAC96: + sadb_comb->sadb_comb_auth = SADB_AALG_SHA1HMAC96; + break; + + case CRYPTO_RIPEMD160_HMAC96: + sadb_comb->sadb_comb_auth = SADB_X_AALG_RIPEMD160HMAC96; + break; + + case CRYPTO_MD5_KPDK: + sadb_comb->sadb_comb_auth = SADB_X_AALG_MD5; + break; + + case CRYPTO_SHA1_KPDK: + sadb_comb->sadb_comb_auth = SADB_X_AALG_SHA1; + break; + } + sadb_comb->sadb_comb_auth_minbits = tdb->tdb_authalgxform->keysize * 8; sadb_comb->sadb_comb_auth_maxbits = @@ -2057,7 +2128,29 @@ pfkeyv2_acquire(struct tdb *tdb, int rekey) if (tdb->tdb_encalgxform) { - sadb_comb->sadb_comb_encrypt = tdb->tdb_encalgxform->type; + switch (tdb->tdb_encalgxform->type) + { + case CRYPTO_DES_CBC: + sadb_comb->sadb_comb_encrypt = SADB_EALG_DESCBC; + break; + + case CRYPTO_3DES_CBC: + sadb_comb->sadb_comb_encrypt = SADB_EALG_3DESCBC; + break; + + case CRYPTO_CAST_CBC: + sadb_comb->sadb_comb_encrypt = SADB_X_EALG_BLF; + break; + + case CRYPTO_BLF_CBC: + sadb_comb->sadb_comb_encrypt = SADB_X_EALG_CAST; + break; + + case CRYPTO_SKIPJACK_CBC: + sadb_comb->sadb_comb_encrypt = SADB_X_EALG_SKIPJACK; + break; + } + sadb_comb->sadb_comb_encrypt_minbits = tdb->tdb_encalgxform->minkey * 8; sadb_comb->sadb_comb_encrypt_maxbits = 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 #include +#include +#include +#include +#include +#include + #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; - - for (i = sizeof(ah_hash) / sizeof(ah_hash[0]) - 1; i >= 0; i--) - if (ii->ii_authalg == ah_hash[i]->type) - break; + struct cryptoini cria; - 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; + + 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; - thash = ah_hash[i]; + 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); - - bcopy(mtod(m, unsigned char *), (unsigned char *) &ipo, - sizeof(struct ip)); + /* Fix the IP header */ + ip = mtod(m, struct ip *); + ip->ip_tos = 0; + ip->ip_ttl = 0; + ip->ip_sum = 0; - 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; - if (m0 == NULL) + /* Authentication operation */ + crda->crd_alg = ahx->type; + crda->crd_key = tdb->tdb_amxkey; + crda->crd_klen = tdb->tdb_amxkeylen * 8; + + /* + * 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); + + /* Zeroize the authenticator on the packet */ + m_copyback(m, skip + rplen, ahx->authsize, ipseczeroes); + + /* "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); + + 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; - /* All-zeroes for the authenticator */ - ahx->Update(&ctx, ipseczeroes, ahx->authsize); + /* 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; - /* Amount of data to be verified */ - len = m->m_pkthdr.len - skip - AH_FLENGTH - rplen - ahx->authsize; + return crypto_dispatch(crp); +} - /* Loop through the mbuf chain computing the HMAC */ - while (len > 0) +/* + * 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; - } - - /* 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); + /* 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; - /* Free, if we allocated */ - if (m->m_len < skip) - FREE(ptr, M_XDATA); - return EMSGSIZE; - } + /* "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; + } - /* - * 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]; + /* 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; - /* Calculate "zeroed-out" immutables */ - ahx->Update(&ctx, ipseczeroes, - ptr[count + 1] - 2); - } - - 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; - /* 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 #include +#include +#include +#include +#include +#include + #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 with 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 #include #include +#include #endif /* INET */ #ifdef INET6 @@ -74,6 +75,9 @@ #include #include +#include +#include + #include #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 #include #include -#include -#include -#include -#include -#include -#include 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -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 */ -- 2.20.1