summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/GENERIC4
-rw-r--r--sys/conf/files5
-rw-r--r--sys/crypto/crypto.c1140
-rw-r--r--sys/crypto/crypto.h200
-rw-r--r--sys/crypto/xform.c354
-rw-r--r--sys/crypto/xform.h73
-rw-r--r--sys/kern/init_main.c10
-rw-r--r--sys/net/if_enc.c29
-rw-r--r--sys/net/pfkeyv2.c101
-rw-r--r--sys/netinet/ip_ah.c1356
-rw-r--r--sys/netinet/ip_ah.h23
-rw-r--r--sys/netinet/ip_esp.c975
-rw-r--r--sys/netinet/ip_esp.h15
-rw-r--r--sys/netinet/ip_ipsp.c432
-rw-r--r--sys/netinet/ip_ipsp.h117
-rw-r--r--sys/netinet/ip_output.c130
-rw-r--r--sys/netinet/ip_xform.c341
-rw-r--r--sys/netinet/ipsec_input.c319
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 */