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