diff options
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/ip_ah.c | 62 | ||||
-rw-r--r-- | sys/netinet/ip_ah.h | 168 | ||||
-rw-r--r-- | sys/netinet/ip_ah_new.c | 894 | ||||
-rw-r--r-- | sys/netinet/ip_ah_old.c | 778 | ||||
-rw-r--r-- | sys/netinet/ip_esp.c | 57 | ||||
-rw-r--r-- | sys/netinet/ip_esp.h | 172 | ||||
-rw-r--r-- | sys/netinet/ip_esp_new.c | 767 | ||||
-rw-r--r-- | sys/netinet/ip_esp_old.c | 809 | ||||
-rw-r--r-- | sys/netinet/ip_ip4.c | 72 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.c | 241 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.h | 154 | ||||
-rw-r--r-- | sys/netinet/ip_output.c | 53 | ||||
-rw-r--r-- | sys/netinet/ip_sha1.h | 7 |
13 files changed, 3744 insertions, 490 deletions
diff --git a/sys/netinet/ip_ah.c b/sys/netinet/ip_ah.c index 1e5cdddc26b..a003c5ab71f 100644 --- a/sys/netinet/ip_ah.c +++ b/sys/netinet/ip_ah.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ah.c,v 1.7 1997/07/01 22:12:41 provos Exp $ */ +/* $OpenBSD: ip_ah.c,v 1.8 1997/07/11 23:37:54 provos Exp $ */ /* * The author of this code is John Ioannidis, ji@tla.org, @@ -59,6 +59,8 @@ #include <netinet/ip_ipsp.h> #include <netinet/ip_ah.h> +#include <sys/syslog.h> + void ah_input __P((struct mbuf *, int)); /* @@ -68,42 +70,35 @@ void ah_input __P((struct mbuf *, int)); void ah_input(register struct mbuf *m, int iphlen) { - struct ip *ipo; - struct ah *ahp; - struct tdb *tdbp; struct ifqueue *ifq = NULL; + struct ip *ipo, ipn; + struct ah_old *ahp, ahn; + struct tdb *tdbp; int s; ahstat.ahs_input++; -#if 0 /* We need them to verify the packet */ - /* - * Strip IP options, if any. - */ - - if (iphlen > sizeof (struct ip)) - { - ip_stripoptions(m, (struct mbuf *)0); - iphlen = sizeof (struct ip); - } -#endif - /* * Make sure that at least the fixed part of the AH header is * in the first mbuf. */ ipo = mtod(m, struct ip *); - if (m->m_len < iphlen + AH_FLENGTH) + if (m->m_len < iphlen + AH_OLD_FLENGTH) { - if ((m = m_pullup(m, iphlen + AH_FLENGTH)) == 0) + if ((m = m_pullup(m, iphlen + AH_OLD_FLENGTH)) == 0) { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_input(): (possibly too short) packet from %x to %x dropped\n", ipo->ip_src, ipo->ip_dst); +#endif /* ENCDEBUG */ ahstat.ahs_hdrops++; return; } ipo = mtod(m, struct ip *); } - ahp = (struct ah *)((caddr_t)ipo + iphlen); + + ahp = (struct ah_old *) ((caddr_t) ipo + iphlen); /* * Find tunnel control block and (indirectly) call the appropriate @@ -111,13 +106,10 @@ ah_input(register struct mbuf *m, int iphlen) * IP packet ready to go through input processing. */ - tdbp = gettdb(ahp->ah_spi, ipo->ip_dst); + tdbp = gettdb(ahp->ah_spi, ipo->ip_dst, IPPROTO_AH); if (tdbp == NULL) { -#ifdef ENCDEBUG - if (encdebug) - printf("ah_input: no tdb for spi=%x\n", ahp->ah_spi); -#endif ENCDEBUG + log(LOG_ERR, "ah_input(): could not find SA for AH packet from %x to %x, spi %08x", ipo->ip_src, ipo->ip_dst, ahp->ah_spi); m_freem(m); ahstat.ahs_notdb++; return; @@ -125,10 +117,9 @@ ah_input(register struct mbuf *m, int iphlen) if (tdbp->tdb_flags & TDBF_INVALID) { -#ifdef ENCDEBUG - if (encdebug) - printf("ah_input: spi=%x is no longer/yet valid\n", ahp->ah_spi); -#endif /* ENCDEBUG */ + log(LOG_ALERT, + "ah_input(): attempted to use invalid AH SA %08x, packet %x->%x", + ahp->ah_spi, ipo->ip_src, ipo->ip_dst); m_freem(m); ahstat.ahs_invalid++; return; @@ -136,10 +127,7 @@ ah_input(register struct mbuf *m, int iphlen) if (tdbp->tdb_xform == NULL) { -#ifdef ENCDEBUG - if (encdebug) - printf("ah_input: no xform for spi=%x\n", ahp->ah_spi); -#endif ENCDEBUG + log(LOG_ALERT, "ah_input(): attempted to use uninitialized AH SA %08x, packet from %x to %x", ahp->ah_spi, ipo->ip_src, ipo->ip_dst); m_freem(m); ahstat.ahs_noxform++; return; @@ -151,10 +139,13 @@ ah_input(register struct mbuf *m, int iphlen) if (tdbp->tdb_first_use == 0) tdbp->tdb_first_use = time.tv_sec; + ipn = *ipo; + ahn = *ahp; + m = (*(tdbp->tdb_xform->xf_input))(m, tdbp); - if (m == NULL) { + log(LOG_ALERT, "ah_input(): authentication failed for AH packet from %x to %x, spi %08x", ipn.ip_src, ipn.ip_dst, ahn.ah_spi); ahstat.ahs_badkcr++; return; } @@ -173,8 +164,13 @@ ah_input(register struct mbuf *m, int iphlen) m_freem(m); ahstat.ahs_qfull++; splx(s); +#ifdef ENCDEBUG + if (encdebug) + printf("ah_input(): dropped packet because of full IP queue\n"); +#endif /* ENCDEBUG */ return; } + IF_ENQUEUE(ifq, m); schednetisr(NETISR_IP); splx(s); diff --git a/sys/netinet/ip_ah.h b/sys/netinet/ip_ah.h index da9840e14e7..4781fbd6eaa 100644 --- a/sys/netinet/ip_ah.h +++ b/sys/netinet/ip_ah.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ah.h,v 1.7 1997/06/25 07:53:21 provos Exp $ */ +/* $OpenBSD: ip_ah.h,v 1.8 1997/07/11 23:37:54 provos Exp $ */ /* * The author of this code is John Ioannidis, ji@tla.org, @@ -31,16 +31,22 @@ #include <sys/md5k.h> #include <netinet/ip_sha1.h> -struct ah +struct ah_old { u_int8_t ah_nh; /* Next header (protocol) */ u_int8_t ah_hl; /* AH length, in 32-bit words */ u_int16_t ah_rv; /* reserved, must be 0 */ u_int32_t ah_spi; /* Security Parameters Index */ - u_int8_t ah_data[1]; /* More, really*/ + u_int8_t ah_data[1]; /* More, really */ }; -#define AH_FLENGTH 8 /* size of fixed part */ +#define AH_OLD_FLENGTH 8 /* size of fixed part */ + +/* Authenticator lengths */ +#define AH_MD5_ALEN 16 +#define AH_SHA1_ALEN 20 + +#define AH_ALEN_MAX AH_SHA1_ALEN /* Keep this updated */ struct ahstat { @@ -58,131 +64,93 @@ struct ahstat u_int32_t ahs_invalid; /* Trying to use an invalid TDB */ }; -#define AHHMACMD5_KMAX 64 /* max 512 bits key */ -#define AHHMACMD5_AMAX 64 /* up to 512 bits of authenticator */ -#define AHHMACMD5_RPLS 2 /* 64 bits of replay counter */ - -#define HMACMD5_HASHLEN 16 -#define HMACMD5_RPLENGTH 8 - -#define HMACMD5_IPAD_VAL 0x36 -#define HMACMD5_OPAD_VAL 0x5C - -#define AHHMACMD5_KMAX 64 /* max 512 bits key */ -#define AHHMACMD5_AMAX 64 /* up to 512 bits of authenticator */ -#define AHHMACMD5_RPLS 2 /* 64 bits of replay counter */ +#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 */ -#define HMACMD5_HASHLEN 16 -#define HMACMD5_RPLENGTH 8 +#define HMAC_IPAD_VAL 0x36 +#define HMAC_OPAD_VAL 0x5C +#define HMAC_BLOCK_LEN 64 -#define HMACMD5_IPAD_VAL 0x36 -#define HMACMD5_OPAD_VAL 0x5C - -struct ahhmacmd5 +struct ah_new { u_int8_t ah_nh; /* Next header (protocol) */ u_int8_t ah_hl; /* AH length, in 32-bit words */ u_int16_t ah_rv; /* reserved, must be 0 */ u_int32_t ah_spi; /* Security Parameters Index */ - u_int64_t ah_rpl; /* Replay prevention */ - u_int8_t ah_data[AHHMACMD5_AMAX];/* Authenticator */ + u_int32_t ah_rpl; /* Replay prevention */ + u_int8_t ah_data[AH_HMAC_HASHLEN];/* Authenticator */ }; -struct ahhmacmd5_xencap -{ - u_int16_t amx_alen; - u_int16_t amx_rpl; - int32_t amx_wnd; - u_int8_t amx_key[AHHMACMD5_KMAX]; -}; +#define AH_NEW_FLENGTH (sizeof(struct ah_new)) -struct ahhmacmd5_xdata +struct ah_new_xencap { - u_int32_t amx_alen; /* authenticator length */ + u_int32_t amx_hash_algorithm; int32_t amx_wnd; - u_int64_t amx_rpl; /* Replay counter */ - u_int64_t amx_bitmap; - MD5_CTX amx_ictx; /* Internal key+padding */ - MD5_CTX amx_octx; /* External key+padding */ + u_int32_t amx_keylen; + u_int8_t amx_key[1]; }; -#define AHHMACSHA1_KMAX 64 /* max 512 bits key */ -#define AHHMACSHA1_AMAX 64 /* up to 512 bits of authenticator */ -#define AHHMACSHA1_RPLS 2 /* 64 bits of replay counter */ +#define AH_NEW_XENCAP_LEN (3 * sizeof(u_int32_t)) -#define HMACSHA1_HASHLEN 20 -#define HMACSHA1_RPLENGTH 8 - -#define HMACSHA1_IPAD_VAL 0x36 -#define HMACSHA1_OPAD_VAL 0x5C - -struct ahhmacsha1 -{ - u_int8_t ah_nh; /* Next header (protocol) */ - u_int8_t ah_hl; /* AH length, in 32-bit words */ - u_int16_t ah_rv; /* reserved, must be 0 */ - u_int32_t ah_spi; /* Security Parameters Index */ - u_int64_t ah_rpl; /* Replay prevention */ - u_int8_t ah_data[AHHMACSHA1_AMAX];/* Authenticator */ -}; - -struct ahhmacsha1_xencap +struct ah_new_xdata { - u_int32_t amx_alen; + u_int32_t amx_hash_algorithm; int32_t amx_wnd; - u_int8_t amx_key[AHHMACSHA1_KMAX]; + u_int32_t amx_rpl; /* Replay counter */ + u_int32_t amx_bitmap; + union + { + struct + { + MD5_CTX amx_ictx; /* Internal key+padding */ + MD5_CTX amx_octx; /* External key+padding */ + } MD5stuff; + + struct + { + SHA1_CTX amx_ictx; + SHA1_CTX amx_octx; + } SHA1stuff; + } Hashes; }; -struct ahhmacsha1_xdata -{ - u_int32_t amx_alen; /* authenticator length */ - int32_t amx_wnd; - u_int64_t amx_rpl; /* Replay counter */ - u_int64_t amx_bitmap; - SHA1_CTX amx_ictx; /* Internal key+padding */ - SHA1_CTX amx_octx; /* External key+padding */ -}; - -#define AHMD5_KMAX 32 /* max 256 bits key */ -#define AHMD5_AMAX 64 /* up to 512 bits of authenticator */ -#define AHSHA1_KMAX 32 -#define AHSHA1_AMAX 64 - -struct ahmd5 -{ - u_int8_t ah_nh; /* Next header (protocol) */ - u_int8_t ah_hl; /* AH length, in 32-bit words */ - u_int16_t ah_rv; /* reserved, must be 0 */ - u_int32_t ah_spi; /* Security Parameters Index */ - u_int8_t ah_data[AHMD5_AMAX]; /* */ -}; - -struct ahsha1 -{ - u_int8_t ah_nh; /* Next header (protocol) */ - u_int8_t ah_hl; /* AH length, in 32-bit words */ - u_int16_t ah_rv; /* reserved, must be 0 */ - u_int32_t ah_spi; /* Security Parameters Index */ - u_int8_t ah_data[AHSHA1_AMAX]; /* */ -}; +#define amx_md5_ictx Hashes.MD5stuff.amx_ictx +#define amx_md5_octx Hashes.MD5stuff.amx_octx +#define amx_sha1_ictx Hashes.SHA1stuff.amx_ictx +#define amx_sha1_octx Hashes.SHA1stuff.amx_octx #define AHMD5_ALEN 16 /* Size of MD5 digest */ #define AHSHA1_ALEN 20 /* Size of SHA-1 digest */ -struct ahmd5_xdata +struct ah_old_xdata { - u_int16_t amx_klen; /* Key material length */ - u_int16_t amx_alen; /* authenticator length */ - u_int8_t amx_key[AHMD5_KMAX]; /* Key material */ + u_int32_t amx_hash_algorithm; + u_int32_t amx_keylen; /* Key material length */ + union + { + MD5_CTX amx_MD5_ctx; + SHA1_CTX amx_SHA1_ctx; + } Hashes; + u_int8_t amx_key[1]; /* Key material */ }; -struct ahsha1_xdata +#define amx_md5_ctx Hashes.amx_MD5_ctx +#define amx_sha1_ctx Hashes.amx_SHA1_ctx + +struct ah_old_xencap { - u_int16_t amx_klen; /* Key material length */ - u_int16_t amx_alen; /* authenticator length */ - u_int8_t amx_key[AHSHA1_KMAX]; /* Key material */ + u_int32_t amx_hash_algorithm; + u_int32_t amx_keylen; + u_int8_t amx_key[1]; }; +#define AH_OLD_XENCAP_LEN (2 * sizeof(u_int32_t)) + +#define AH_HMAC_IPAD_VAL 0x36 +#define AH_HMAC_OPAD_VAL 0x5C + #ifdef _KERNEL struct ahstat ahstat; #endif diff --git a/sys/netinet/ip_ah_new.c b/sys/netinet/ip_ah_new.c new file mode 100644 index 00000000000..e5aabc7a04f --- /dev/null +++ b/sys/netinet/ip_ah_new.c @@ -0,0 +1,894 @@ +/* $OpenBSD: ip_ah_new.c,v 1.1 1997/07/11 23:37:55 provos Exp $ */ + +/* + * The author of this code is John Ioannidis, ji@tla.org, + * (except when noted otherwise). + * + * This code was written for BSD/OS in Athens, Greece, in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis, kermit@forthnet.gr. + * + * Copyright (C) 1995, 1996, 1997 by John Ioannidis and 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 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, NEITHER AUTHOR MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +/* + * Based on RFC 2085. + */ + +#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/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/socketvar.h> + +#include <machine/cpu.h> +#include <machine/endian.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/netisr.h> +#include <net/raw_cb.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 <netinet/ip_icmp.h> + +#include <net/encap.h> + +#include <netinet/ip_ipsp.h> +#include <netinet/ip_ah.h> +#include <sys/syslog.h> + +/* + * ah_new_attach() is called from the transformation initialization code. + * It just returns. + */ + +int +ah_new_attach() +{ +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_attach(): setting up\n"); +#endif /* ENCDEBUG */ + return 0; +} + +/* + * ah_new_init() is called when an SPI is being set up. It interprets the + * encap_msghdr present in m, and sets up the transformation data. + */ + +int +ah_new_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) +{ + struct ah_new_xdata *xd; + struct ah_new_xencap txd; + struct encap_msghdr *em; + caddr_t buffer = NULL; + int blocklen, i; + + if (m->m_len < ENCAP_MSG_FIXED_LEN) + { + if ((m = m_pullup(m, ENCAP_MSG_FIXED_LEN)) == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_init(): m_pullup failed\n"); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + } + + em = mtod(m, struct encap_msghdr *); + if (em->em_msglen - EMT_SETSPI_FLEN <= AH_NEW_XENCAP_LEN) + { + log(LOG_WARNING, "ah_new_init() initialization failed"); + return EINVAL; + } + + /* Just copy the standard fields */ + m_copydata(m, EMT_SETSPI_FLEN, AH_NEW_XENCAP_LEN, (caddr_t) &txd); + + /* Check whether the hash algorithm is supposed */ + switch (txd.amx_hash_algorithm) + { + case ALG_AUTH_MD5: + case ALG_AUTH_SHA1: +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_init(): initalized TDB with hash algorithm %d\n", + txd.amx_hash_algorithm); +#endif /* ENCDEBUG */ + blocklen = HMAC_BLOCK_LEN; + break; + + default: + log(LOG_WARNING, "ah_new_init(): unsupported authentication algorithm %d specified", txd.amx_hash_algorithm); + return EINVAL; + } + + if (txd.amx_keylen + EMT_SETSPI_FLEN + AH_NEW_XENCAP_LEN != em->em_msglen) + { + log(LOG_WARNING, "ah_new_init(): message length (%d) doesn't match", + em->em_msglen); + return EINVAL; + } + + MALLOC(tdbp->tdb_xdata, caddr_t, sizeof(struct ah_new_xdata), + M_XDATA, M_WAITOK); + if (tdbp->tdb_xdata == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_init(): MALLOC failed\n"); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + MALLOC(buffer, caddr_t, + (txd.amx_keylen < blocklen ? blocklen : txd.amx_keylen), + M_TEMP, M_WAITOK); + if (buffer == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_init(): MALLOC failed\n"); +#endif /* ENCDEBUG */ + free(tdbp->tdb_xdata, M_XDATA); + return ENOBUFS; + } + + bzero(buffer, (txd.amx_keylen < blocklen ? blocklen : txd.amx_keylen)); + bzero(tdbp->tdb_xdata, sizeof(struct ah_new_xdata)); + xd = (struct ah_new_xdata *) tdbp->tdb_xdata; + + /* Copy the key to the buffer */ + m_copydata(m, EMT_SETSPI_FLEN + AH_NEW_XENCAP_LEN, txd.amx_keylen, buffer); + + /* Shorten the key if necessary */ + if (txd.amx_keylen > blocklen) + { + switch (txd.amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Init(&(xd->amx_md5_ictx)); + MD5Update(&(xd->amx_md5_ictx), buffer, txd.amx_keylen); + bzero(buffer, + (txd.amx_keylen < blocklen ? blocklen : txd.amx_keylen)); + MD5Final(buffer, &(xd->amx_md5_ictx)); + break; + + case ALG_AUTH_SHA1: + SHA1Init(&(xd->amx_sha1_ictx)); + SHA1Update(&(xd->amx_sha1_ictx), buffer, txd.amx_keylen); + bzero(buffer, + (txd.amx_keylen < blocklen ? blocklen : txd.amx_keylen)); + SHA1Final(buffer, &(xd->amx_sha1_ictx)); + break; + } + } + + /* Pointer to the transform */ + tdbp->tdb_xform = xsp; + + xd->amx_hash_algorithm = txd.amx_hash_algorithm; + xd->amx_rpl = AH_HMAC_INITIAL_RPL; + xd->amx_wnd = txd.amx_wnd; + xd->amx_bitmap = 0; + + /* Precompute the I and O pads of the HMAC */ + for (i = 0; i < blocklen; i++) + buffer[i] ^= HMAC_IPAD_VAL; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Init(&(xd->amx_md5_ictx)); + MD5Update(&(xd->amx_md5_ictx), buffer, blocklen); + break; + + case ALG_AUTH_SHA1: + SHA1Init(&(xd->amx_sha1_ictx)); + SHA1Update(&(xd->amx_sha1_ictx), buffer, blocklen); + break; + } + + for (i = 0; i < blocklen; i++) + buffer[i] ^= (HMAC_IPAD_VAL ^ HMAC_IPAD_VAL); + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Init(&(xd->amx_md5_octx)); + MD5Update(&(xd->amx_md5_octx), buffer, blocklen); + break; + + case ALG_AUTH_SHA1: + SHA1Init(&(xd->amx_sha1_octx)); + SHA1Update(&(xd->amx_sha1_octx), buffer, blocklen); + break; + } + + bzero(buffer, blocklen); /* paranoid */ + free(buffer, M_TEMP); + + bzero(ipseczeroes, IPSEC_ZEROES_SIZE); /* paranoid */ + + return 0; +} + +/* Free memory */ +int +ah_new_zeroize(struct tdb *tdbp) +{ +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_zeroize(): freeing memory\n"); +#endif /* ENCDEBUG */ + FREE(tdbp->tdb_xdata, M_XDATA); + return 0; +} + +/* + * ah_new_input() gets called to verify that an input packet + * passes authentication. + */ + +struct mbuf * +ah_new_input(struct mbuf *m, struct tdb *tdb) +{ + struct ah_new_xdata *xd; + struct ip *ip, ipo; + struct ah_new *aho, *ah; + struct ifnet *rcvif; + int ohlen, len, count, off, errc; + u_int32_t btsx; + struct mbuf *m0; + MD5_CTX md5ctx; + SHA1_CTX sha1ctx; + u_int8_t optval; + u_char buffer[40]; + + aho = (struct ah_new *) buffer; + + xd = (struct ah_new_xdata *) tdb->tdb_xdata; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + case ALG_AUTH_SHA1: + break; + + default: + log(LOG_ALERT, + "ah_new_input(): unsupported algorithm %d in SA %x/%08x", + xd->amx_hash_algorithm, tdb->tdb_dst, tdb->tdb_spi); + m_freem(m); + return NULL; + } + + ohlen = sizeof(struct ip) + AH_NEW_FLENGTH; + + rcvif = m->m_pkthdr.rcvif; + if (rcvif == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_input(): receive interface is NULL!!!\n"); +#endif /* ENCDEBUG */ + rcvif = &enc_softc; + } + + if (m->m_len < ohlen) + { + if ((m = m_pullup(m, ohlen)) == NULL) + { + ahstat.ahs_hdrops++; +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_input(): (possibly too short) packet dropped\n"); +#endif /* ENCDEBUG */ + return NULL; + } + } + + ip = mtod(m, struct ip *); + + /* Adjust, if options are present */ + if ((ip->ip_hl << 2) > sizeof(struct ip)) + { + if ((m = m_pullup(m, ohlen - sizeof (struct ip) + + (ip->ip_hl << 2))) == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_input(): m_pullup() failed\n"); +#endif /* ENCDEBUG */ + ahstat.ahs_hdrops++; + return NULL; + } + + ip = mtod(m, struct ip *); + ah = (struct ah_new *) ((u_int8_t *) ip + (ip->ip_hl << 2)); + ohlen += ((ip->ip_hl << 2) - sizeof(struct ip)); + } + else + ah = (struct ah_new *) (ip + 1); + + if (ah->ah_hl * sizeof(u_int32_t) != AH_HMAC_HASHLEN) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_input(): bad authenticator length for packet from %x to %x, spi %08x\n", ip->ip_src, ip->ip_dst, ah->ah_spi); +#endif /* ENCDEBUG */ + ahstat.ahs_badauthl++; + m_freem(m); + return NULL; + } + + /* Replay window checking */ + if (xd->amx_wnd >= 0) + { + btsx = ntohl(ah->ah_rpl); + if ((errc = checkreplaywindow32(btsx, 0, &(xd->amx_rpl), xd->amx_wnd, + &(xd->amx_bitmap))) != 0) + { + switch(errc) + { + case 1: + log(LOG_ERR, "ah_new_input(): replay counter wrapped for packets from %x to %x, spi %08x\n", ip->ip_src, ip->ip_dst, ah->ah_spi); + ahstat.ahs_wrap++; + break; + + case 2: + case 3: + log(LOG_WARNING, "ahhmachmd5_input(): duplicate packet received, %x->%x spi %08x", ip->ip_src, ip->ip_dst, ah->ah_spi); + ahstat.ahs_replay++; + break; + } + + m_freem(m); + return NULL; + } + } + + ipo = *ip; + ipo.ip_tos = 0; + ipo.ip_len += (ip->ip_hl << 2); /* adjusted in ip_intr() */ + HTONS(ipo.ip_len); + HTONS(ipo.ip_id); + ipo.ip_off = htons(ipo.ip_off & IP_DF); /* XXX -- and the C bit? */ + ipo.ip_ttl = 0; + ipo.ip_sum = 0; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + md5ctx = xd->amx_md5_ictx; + MD5Update(&md5ctx, (unsigned char *) &ipo, sizeof(struct ip)); + break; + + case ALG_AUTH_SHA1: + sha1ctx = xd->amx_sha1_ictx; + SHA1Update(&sha1ctx, (unsigned char *) &ipo, sizeof(struct ip)); + break; + } + + /* Options */ + if ((ip->ip_hl << 2 > sizeof(struct ip))) + for (off = sizeof(struct ip); off < (ip->ip_hl << 2);) + { + optval = ((u_int8_t *) ip)[off]; + switch (optval) + { + case IPOPT_EOL: + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, 1); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, 1); + break; + } + + off = ip->ip_hl << 2; + break; + + case IPOPT_NOP: + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, 1); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, 1); + break; + } + + off++; + break; + + case IPOPT_SECURITY: + case 133: + case 134: + optval = ((u_int8_t *) ip)[off + 1]; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, (u_int8_t *) ip + off, optval); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, (u_int8_t *) ip + off, optval); + break; + } + + off += optval; + break; + + default: + optval = ((u_int8_t *) ip)[off + 1]; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, optval); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, optval); + break; + } + + off += optval; + break; + } + } + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, (unsigned char *) ah, AH_NEW_FLENGTH - + AH_HMAC_HASHLEN); + MD5Update(&md5ctx, ipseczeroes, AH_HMAC_HASHLEN); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, (unsigned char *) ah, AH_NEW_FLENGTH - + AH_HMAC_HASHLEN); + SHA1Update(&sha1ctx, ipseczeroes, AH_HMAC_HASHLEN); + break; + } + + + /* + * Code shamelessly stolen from m_copydata + */ + off = ohlen; + len = m->m_pkthdr.len - off; + m0 = m; + + while (off > 0) + { + if (m0 == 0) + panic("ah_new_input(): m_copydata (off)"); + + if (off < m0->m_len) + break; + + off -= m0->m_len; + m0 = m0->m_next; + } + + while (len > 0) + { + if (m0 == 0) + panic("ah_new_input(): m_copydata (copy)"); + + count = min(m0->m_len - off, len); + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, mtod(m0, unsigned char *) + off, count); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, mtod(m0, unsigned char *) + off, count); + break; + } + + len -= count; + off = 0; + m0 = m0->m_next; + } + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Final((unsigned char *) (aho->ah_data), &md5ctx); + md5ctx = xd->amx_md5_octx; + MD5Update(&md5ctx, (unsigned char *) (aho->ah_data), + AH_HMAC_HASHLEN); + MD5Final((unsigned char *) (aho->ah_data), &md5ctx); + break; + + case ALG_AUTH_SHA1: + SHA1Final((unsigned char *) (aho->ah_data), &sha1ctx); + sha1ctx = xd->amx_sha1_octx; + SHA1Update(&sha1ctx, (unsigned char *) (aho->ah_data), + AH_HMAC_HASHLEN); + SHA1Final((unsigned char *) (aho->ah_data), &sha1ctx); + break; + } + + + if (bcmp(aho->ah_data, ah->ah_data, AH_HMAC_HASHLEN)) + { + log(LOG_ALERT, + "ah_new_input(): authentication failed for packet from %x to %x, spi %08x", ip->ip_src, ip->ip_dst, ah->ah_spi); + ahstat.ahs_badauth++; + m_freem(m); + return NULL; + } + + ipo = *ip; + ipo.ip_p = ah->ah_nh; + + /* Save options */ + m_copydata(m, sizeof(struct ip), (ip->ip_hl << 2) - sizeof(struct ip), + (caddr_t) buffer); + + m->m_len -= AH_NEW_FLENGTH; + m->m_data += AH_NEW_FLENGTH; + m->m_pkthdr.len -= AH_NEW_FLENGTH; + m->m_pkthdr.rcvif = rcvif; /* this should not be necessary */ + + ip = mtod(m, struct ip *); + *ip = ipo; + ip->ip_len = htons(ip->ip_len - AH_NEW_FLENGTH + (ip->ip_hl << 2)); + HTONS(ip->ip_id); + HTONS(ip->ip_off); + ip->ip_sum = 0; + + /* Copy the options back */ + m_copyback(m, sizeof(struct ip), (ip->ip_hl << 2) - sizeof(struct ip), + (caddr_t) buffer); + + ip->ip_sum = in_cksum(m, (ip->ip_hl << 2)); + + /* Update the counters */ + tdb->tdb_cur_packets++; + tdb->tdb_cur_bytes += ntohs(ip->ip_len) - (ip->ip_hl << 2); + + return m; +} + +int +ah_new_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, + struct mbuf **mp) +{ + struct ah_new_xdata *xd; + struct ip *ip, ipo; + struct ah_new aho, *ah; + register int len, off, count; + register struct mbuf *m0; + MD5_CTX md5ctx; + SHA1_CTX sha1ctx; + int ilen, ohlen; + u_int8_t optval; + u_char buffer[AH_ALEN_MAX], opts[40]; + + ahstat.ahs_output++; + m = m_pullup(m, sizeof(struct ip)); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_output(): m_pullup() failed, SA %x/%08x\n", + tdb->tdb_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + ip = mtod(m, struct ip *); + + xd = (struct ah_new_xdata *) tdb->tdb_xdata; + + if ((ip->ip_hl << 2) > sizeof(struct ip)) + { + if ((m = m_pullup(m, ip->ip_hl << 2)) == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_output(): m_pullup() failed, SA &x/%08x\n", + tdb->tdb_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + ahstat.ahs_hdrops++; + return NULL; + } + + ip = mtod(m, struct ip *); + } + + /* Save options */ + m_copydata(m, sizeof(struct ip), (ip->ip_hl << 2) - sizeof(struct ip), + (caddr_t) opts); + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + case ALG_AUTH_SHA1: +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_output(): using hash algorithm %d\n", + xd->amx_hash_algorithm); +#endif /* ENCDEBUG */ + break; + + default: + log(LOG_ALERT, + "ah_new_output(): unsupported algorithm %d in SA %x/%08x", + xd->amx_hash_algorithm, tdb->tdb_dst, tdb->tdb_spi); + m_freem(m); + return NULL; + } + + ilen = ntohs(ip->ip_len); + + ohlen = AH_NEW_FLENGTH; + + ipo.ip_v = IPVERSION; + ipo.ip_hl = ip->ip_hl; + ipo.ip_tos = 0; + ipo.ip_len = htons(ohlen + ilen); + ipo.ip_id = ip->ip_id; + ipo.ip_off = htons(ntohs(ip->ip_off) & IP_DF); + ipo.ip_ttl = 0; + ipo.ip_p = IPPROTO_AH; + ipo.ip_sum = 0; + ipo.ip_src = ip->ip_src; + ipo.ip_dst = ip->ip_dst; + + bzero(&aho, sizeof(struct ah_new)); + + aho.ah_nh = ip->ip_p; + aho.ah_hl = (AH_HMAC_HASHLEN >> 2); + aho.ah_rv = 0; + aho.ah_spi = tdb->tdb_spi; + + if (xd->amx_rpl == 0) + { + log(LOG_ALERT, "ah_new_output(): SA %x/%0x8 should have expired", + tdb->tdb_dst, tdb->tdb_spi); + m_freem(m); + ahstat.ahs_wrap++; + return NULL; + } + + aho.ah_rpl = htonl(xd->amx_rpl++); + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + md5ctx = xd->amx_md5_ictx; + MD5Update(&md5ctx, (unsigned char *) &ipo, sizeof(struct ip)); + break; + + case ALG_AUTH_SHA1: + sha1ctx = xd->amx_sha1_ictx; + SHA1Update(&sha1ctx, (unsigned char *) &ipo, sizeof(struct ip)); + break; + } + + /* Options */ + if ((ip->ip_hl << 2) > sizeof(struct ip)) + for (off = sizeof(struct ip); off < (ip->ip_hl << 2);) + { + optval = ((u_int8_t *) ip)[off]; + switch (optval) + { + case IPOPT_EOL: + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, 1); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, 1); + break; + } + + off = ip->ip_hl << 2; + break; + + case IPOPT_NOP: + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, 1); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, 1); + break; + } + + off++; + break; + + case IPOPT_SECURITY: + case 133: + case 134: + optval = ((u_int8_t *) ip)[off + 1]; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, (u_int8_t *) ip + off, optval); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, (u_int8_t *) ip + off, optval); + break; + } + + off += optval; + break; + + default: + optval = ((u_int8_t *) ip)[off + 1]; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, optval); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, optval); + break; + } + + off += optval; + break; + } + } + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, (unsigned char *) &aho, AH_NEW_FLENGTH); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, (unsigned char *) &aho, AH_NEW_FLENGTH); + break; + } + + off = ip->ip_hl << 2; + + /* + * Code shamelessly stolen from m_copydata + */ + len = m->m_pkthdr.len - off; + + m0 = m; + + while (len > 0) + { + if (m0 == 0) + panic("ah_new_output(): m_copydata"); + count = min(m0->m_len - off, len); + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, mtod(m0, unsigned char *) + off, count); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, mtod(m0, unsigned char *) + off, count); + break; + } + + len -= count; + off = 0; + m0 = m0->m_next; + } + + ipo.ip_tos = ip->ip_tos; + ipo.ip_id = ip->ip_id; + ipo.ip_off = ip->ip_off; + ipo.ip_ttl = ip->ip_ttl; +/* ipo.ip_len = ntohs(ipo.ip_len); */ + + M_PREPEND(m, ohlen, M_DONTWAIT); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_output(): M_PREPEND() failed for packet from %x to %x, spi %08x\n", ipo.ip_src, ipo.ip_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + m = m_pullup(m, ohlen + (ipo.ip_hl << 2)); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_output(): m_pullup() failed for packet from %x to %x, spi %08x\n", ipo.ip_src, ipo.ip_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + ip = mtod(m, struct ip *); + ah = (struct ah_new *) ((u_int8_t *) ip + (ipo.ip_hl << 2)); + *ip = ipo; + ah->ah_nh = aho.ah_nh; + ah->ah_hl = aho.ah_hl; + ah->ah_rv = aho.ah_rv; + ah->ah_spi = aho.ah_spi; + ah->ah_rpl = aho.ah_rpl; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Final(buffer, &md5ctx); + md5ctx = xd->amx_md5_octx; + MD5Update(&md5ctx, buffer, AH_MD5_ALEN); + MD5Final(buffer, &md5ctx); + break; + + case ALG_AUTH_SHA1: + SHA1Final(buffer, &sha1ctx); + sha1ctx = xd->amx_sha1_octx; + SHA1Update(&sha1ctx, buffer, AH_SHA1_ALEN); + SHA1Final(buffer, &sha1ctx); + break; + } + + /* Restore the options */ + m_copyback(m, sizeof(struct ip), (ip->ip_hl << 2) - sizeof(struct ip), + (caddr_t) opts); + + /* Copy the authenticator */ + bcopy(buffer, ah->ah_data, AH_HMAC_HASHLEN); + + *mp = m; + + /* Update the counters */ + tdb->tdb_cur_packets++; + tdb->tdb_cur_bytes += ip->ip_len - (ip->ip_hl << 2) - AH_NEW_FLENGTH; + + return 0; +} diff --git a/sys/netinet/ip_ah_old.c b/sys/netinet/ip_ah_old.c new file mode 100644 index 00000000000..0cf653c62b1 --- /dev/null +++ b/sys/netinet/ip_ah_old.c @@ -0,0 +1,778 @@ + +/* + * The author of this code is John Ioannidis, ji@tla.org, + * (except when noted otherwise). + * + * This code was written for BSD/OS in Athens, Greece, in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis, kermit@forthnet.gr. + * + * Copyright (C) 1995, 1996, 1997 by John Ioannidis and 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 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, NEITHER AUTHOR MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +/* + * Authentication Header Processing + * Per RFCs 1828/1852 (Metzger & Simpson) + */ + +#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/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 <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 <netinet/ip_icmp.h> + +#include <sys/socketvar.h> +#include <net/raw_cb.h> +#include <net/encap.h> + +#include <netinet/ip_ipsp.h> +#include <netinet/ip_ah.h> +#include <sys/syslog.h> + +/* + * ah_old_attach() is called from the transformation initialization code. + */ + +int +ah_old_attach() +{ +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_attach(): setting up\n"); +#endif /* ENCDEBUG */ + return 0; +} + +/* + * ah_old_init() is called when an SPI is being set up. It interprets the + * encap_msghdr present in m, and sets up the transformation data. + */ + +int +ah_old_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) +{ + struct ah_old_xencap xenc; + struct ah_old_xdata *xd; + struct encap_msghdr *em; + + if (m->m_len < ENCAP_MSG_FIXED_LEN) + { + if ((m = m_pullup(m, ENCAP_MSG_FIXED_LEN)) == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_init(): m_pullup failed\n"); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + } + + em = mtod(m, struct encap_msghdr *); + if (em->em_msglen - EMT_SETSPI_FLEN <= AH_OLD_XENCAP_LEN) + { + log(LOG_WARNING, "ah_old_init(): initialization failed"); + return EINVAL; + } + + /* Just copy the standard fields */ + m_copydata(m, EMT_SETSPI_FLEN, AH_OLD_XENCAP_LEN, (caddr_t) &xenc); + + /* Check whether the hash algorithm is supported */ + switch (xenc.amx_hash_algorithm) + { + case ALG_AUTH_MD5: + case ALG_AUTH_SHA1: +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_init(): initialized TDB with hash algorithm %d\n", + xenc.amx_hash_algorithm); +#endif /* ENCDEBUG */ + break; + + default: + log(LOG_WARNING, "ah_old_init(): unsupported authentication algorithm %d specified", xenc.amx_hash_algorithm); + m_freem(m); + return EINVAL; + } + + if (xenc.amx_keylen + EMT_SETSPI_FLEN + AH_OLD_XENCAP_LEN != em->em_msglen) + { + log(LOG_WARNING, "ah_old_init(): message length (%d) doesn't match", + em->em_msglen); + return EINVAL; + } + + MALLOC(tdbp->tdb_xdata, caddr_t, sizeof(struct ah_old_xdata) + + xenc.amx_keylen, M_XDATA, M_WAITOK); + if (tdbp->tdb_xdata == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_init(): MALLOC() failed\n"); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + bzero(tdbp->tdb_xdata, sizeof(struct ah_old_xdata) + xenc.amx_keylen); + xd = (struct ah_old_xdata *) tdbp->tdb_xdata; + + /* Pointer to the transform */ + tdbp->tdb_xform = xsp; + + xd->amx_keylen = xenc.amx_keylen; + xd->amx_hash_algorithm = xenc.amx_hash_algorithm; + + /* Copy the key material */ + m_copydata(m, EMT_SETSPI_FLEN + AH_OLD_XENCAP_LEN, xd->amx_keylen, + (caddr_t) xd->amx_key); + + /* Save us some time in processing */ + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Init(&(xd->amx_md5_ctx)); + MD5Update(&(xd->amx_md5_ctx), xd->amx_key, xd->amx_keylen); + MD5Final(NULL, &(xd->amx_md5_ctx)); + break; + + case ALG_AUTH_SHA1: + SHA1Init(&(xd->amx_sha1_ctx)); + SHA1Update(&(xd->amx_sha1_ctx), xd->amx_key, xd->amx_keylen); + SHA1Final(NULL, &(xd->amx_sha1_ctx)); + break; + } + + bzero(ipseczeroes, IPSEC_ZEROES_SIZE); /* paranoid */ + + return 0; +} + +/* + * Free memory + */ + +int +ah_old_zeroize(struct tdb *tdbp) +{ +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_zeroize(): freeing memory\n"); +#endif /* ENCDEBUG */ + FREE(tdbp->tdb_xdata, M_XDATA); + return 0; +} + +/* + * ah_old_input() gets called to verify that an input packet + * passes authentication. + */ + +struct mbuf * +ah_old_input(struct mbuf *m, struct tdb *tdb) +{ + struct ah_old_xdata *xd; + struct ip *ip, ipo; + struct ah_old *ah, *aho; + struct ifnet *rcvif; + int ohlen, len, count, off, alen; + struct mbuf *m0; + MD5_CTX md5ctx; + SHA1_CTX sha1ctx; + u_int8_t optval; + u_char buffer[40]; + + aho = (struct ah_old *) buffer; + + xd = (struct ah_old_xdata *) tdb->tdb_xdata; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + alen = AH_MD5_ALEN; + break; + + case ALG_AUTH_SHA1: + alen = AH_SHA1_ALEN; + break; + + default: + log(LOG_ALERT, + "ah_old_input(): unsupported algorithm %d in SA %x/%08x", + xd->amx_hash_algorithm, tdb->tdb_dst, tdb->tdb_spi); + m_freem(m); + return NULL; + } + + ohlen = sizeof(struct ip) + AH_OLD_FLENGTH + alen; + + rcvif = m->m_pkthdr.rcvif; + if (rcvif == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_input(): receive interface is NULL!\n"); +#endif /* ENCDEBUG */ + rcvif = &enc_softc; + } + + if (m->m_len < ohlen) + { + if ((m = m_pullup(m, ohlen)) == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_input(): m_pullup() failed\n"); +#endif /* ENCDEBUG */ + ahstat.ahs_hdrops++; + return NULL; + } + } + + ip = mtod(m, struct ip *); + + if ((ip->ip_hl << 2) > sizeof(struct ip)) + { + if ((m = m_pullup(m, ohlen - sizeof (struct ip) + + (ip->ip_hl << 2))) == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_input(): m_pullup() failed\n"); +#endif /* ENCDEBUG */ + ahstat.ahs_hdrops++; + return NULL; + } + + ip = mtod(m, struct ip *); + ah = (struct ah_old *)((u_int8_t *) ip + (ip->ip_hl << 2)); + ohlen += ((ip->ip_hl << 2) - sizeof(struct ip)); + } + else + ah = (struct ah_old *) (ip + 1); + + ipo = *ip; + ipo.ip_tos = 0; + ipo.ip_len += (ip->ip_hl << 2); /* adjusted in ip_intr() */ + HTONS(ipo.ip_len); + HTONS(ipo.ip_id); + ipo.ip_off = htons(ipo.ip_off & IP_DF); /* XXX -- and the C bit? */ + ipo.ip_ttl = 0; + ipo.ip_sum = 0; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + md5ctx = xd->amx_md5_ctx; + MD5Update(&md5ctx, (unsigned char *) &ipo, sizeof(struct ip)); + break; + + case ALG_AUTH_SHA1: + sha1ctx = xd->amx_sha1_ctx; + SHA1Update(&sha1ctx, (unsigned char *) &ipo, sizeof(struct ip)); + break; + } + + /* Options */ + if ((ip->ip_hl << 2) > sizeof(struct ip)) + for (off = sizeof(struct ip); off < (ip->ip_hl << 2);) + { + optval = ((u_int8_t *) ip)[off]; + switch (optval) + { + case IPOPT_EOL: + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, 1); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, 1); + break; + } + + off = ip->ip_hl << 2; + break; + + case IPOPT_NOP: + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, 1); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, 1); + break; + } + + off++; + break; + + case IPOPT_SECURITY: + case 133: + case 134: + optval = ((u_int8_t *) ip)[off + 1]; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, (u_int8_t *) ip + off, optval); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, (u_int8_t *) ip + off, optval); + break; + } + + off += optval; + break; + + default: + optval = ((u_int8_t *) ip)[off + 1]; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, optval); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, optval); + break; + } + + off += optval; + break; + } + } + + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, (unsigned char *) ah, AH_OLD_FLENGTH); + MD5Update(&md5ctx, ipseczeroes, AH_MD5_ALEN); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, (unsigned char *) ah, AH_OLD_FLENGTH); + SHA1Update(&sha1ctx, ipseczeroes, AH_SHA1_ALEN); + break; + } + + /* + * Code shamelessly stolen from m_copydata + */ + off = ohlen; + len = m->m_pkthdr.len - off; + m0 = m; + + while (off > 0) + { + if (m0 == 0) + panic("ah_old_input(): m_copydata (off)"); + + if (off < m0->m_len) + break; + + off -= m0->m_len; + m0 = m0->m_next; + } + + while (len > 0) + { + if (m0 == 0) + panic("ah_old_input(): m_copydata (copy)"); + + count = min(m0->m_len - off, len); + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, mtod(m0, unsigned char *) + off, count); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, mtod(m0, unsigned char *) + off, count); + } + + len -= count; + off = 0; + m0 = m0->m_next; + } + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, (unsigned char *) xd->amx_key, xd->amx_keylen); + MD5Final((unsigned char *) (aho->ah_data), &md5ctx); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, (unsigned char *) xd->amx_key, xd->amx_keylen); + SHA1Final((unsigned char *) (aho->ah_data), &sha1ctx); + break; + } + + if (bcmp(aho->ah_data, ah->ah_data, alen)) + { + log(LOG_ALERT, "ah_old_input(): authentication failed for packet from %x to %x, spi %08x", ipo.ip_src, ipo.ip_dst, tdb->tdb_spi); + ahstat.ahs_badauth++; + m_freem(m); + return NULL; + } + + ipo = *ip; + ipo.ip_p = ah->ah_nh; + + /* Save options */ + m_copydata(m, sizeof(struct ip), (ip->ip_hl << 2) - sizeof(struct ip), + (caddr_t) buffer); + + m->m_len -= (AH_OLD_FLENGTH + alen); + m->m_data += (AH_OLD_FLENGTH + alen); + m->m_pkthdr.len -= (AH_OLD_FLENGTH + alen); + m->m_pkthdr.rcvif = rcvif; /* this should not be necessary */ + + ip = mtod(m, struct ip *); + *ip = ipo; + ip->ip_len = htons(ip->ip_len - AH_OLD_FLENGTH - alen + (ip->ip_hl << 2)); + HTONS(ip->ip_id); + HTONS(ip->ip_off); + ip->ip_sum = 0; + + /* Copy the options back */ + m_copyback(m, sizeof(struct ip), (ip->ip_hl << 2) - sizeof(struct ip), + (caddr_t) buffer); + + ip->ip_sum = in_cksum(m, (ip->ip_hl << 2)); + + /* Update the counters */ + tdb->tdb_cur_packets++; + tdb->tdb_cur_bytes += ntohs(ip->ip_len) - (ip->ip_hl << 2); + + return m; +} + +int +ah_old_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, + struct mbuf **mp) +{ + struct ah_old_xdata *xd; + struct ip *ip, ipo; + struct ah_old *ah, aho; + register int len, off, count; + register struct mbuf *m0; + MD5_CTX md5ctx; + SHA1_CTX sha1ctx; + int ilen, ohlen, alen; + u_int8_t optval; + u_char opts[40]; + + ahstat.ahs_output++; + m = m_pullup(m, sizeof(struct ip)); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_output(): m_pullup() failed, SA %x/%08x\n", + tdb->tdb_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + ip = mtod(m, struct ip *); + + xd = (struct ah_old_xdata *) tdb->tdb_xdata; + + if ((ip->ip_hl << 2) > sizeof(struct ip)) + { + if ((m = m_pullup(m, ip->ip_hl << 2)) == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_output(): m_pullup() failed, SA &x/%08x\n", + tdb->tdb_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + ahstat.ahs_hdrops++; + return NULL; + } + + ip = mtod(m, struct ip *); + } + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + alen = AH_MD5_ALEN; + break; + + case ALG_AUTH_SHA1: + alen = AH_SHA1_ALEN; + break; + + default: + log(LOG_ALERT, + "ah_old_output(): unsupported algorithm %d in SA %x/%08x", + xd->amx_hash_algorithm, tdb->tdb_dst, tdb->tdb_spi); + m_freem(m); + return NULL; + } + + /* Save the options */ + m_copydata(m, sizeof(struct ip), (ip->ip_hl << 2) - sizeof(struct ip), + (caddr_t) opts); + + ilen = ntohs(ip->ip_len); + + ohlen = AH_OLD_FLENGTH + alen; + + ipo.ip_v = IPVERSION; + ipo.ip_hl = ip->ip_hl; + ipo.ip_tos = 0; + ipo.ip_len = htons(ohlen + ilen); + ipo.ip_id = ip->ip_id; + ipo.ip_off = htons(ntohs(ip->ip_off) & IP_DF); + ipo.ip_ttl = 0; + ipo.ip_p = IPPROTO_AH; + ipo.ip_sum = 0; + ipo.ip_src = ip->ip_src; + ipo.ip_dst = ip->ip_dst; + + aho.ah_nh = ip->ip_p; + aho.ah_hl = alen >> 2; + aho.ah_rv = 0; + aho.ah_spi = tdb->tdb_spi; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + md5ctx = xd->amx_md5_ctx; + MD5Update(&md5ctx, (unsigned char *) &ipo, sizeof(struct ip)); + break; + + case ALG_AUTH_SHA1: + sha1ctx = xd->amx_sha1_ctx; + SHA1Update(&sha1ctx, (unsigned char *) &ipo, sizeof(struct ip)); + break; + } + + /* Options */ + if ((ip->ip_hl << 2) > sizeof(struct ip)) + for (off = sizeof(struct ip); off < (ip->ip_hl << 2);) + { + optval = ((u_int8_t *) ip)[off]; + switch (optval) + { + case IPOPT_EOL: + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, 1); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, 1); + break; + } + + off = ip->ip_hl << 2; + break; + + case IPOPT_NOP: + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, 1); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, 1); + break; + } + + off++; + break; + + case IPOPT_SECURITY: + case 133: + case 134: + optval = ((u_int8_t *) ip)[off + 1]; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, (u_int8_t *) ip + off, optval); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, (u_int8_t *) ip + off, optval); + break; + } + + off += optval; + break; + + default: + optval = ((u_int8_t *) ip)[off + 1]; + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, ipseczeroes, optval); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, ipseczeroes, optval); + break; + } + + off += optval; + break; + } + } + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, (unsigned char *) &aho, AH_OLD_FLENGTH); + MD5Update(&md5ctx, ipseczeroes, alen); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, (unsigned char *) &aho, AH_OLD_FLENGTH); + SHA1Update(&sha1ctx, ipseczeroes, alen); + break; + } + + /* Skip the IP header and any options */ + off = ip->ip_hl << 2; + + /* + * Code shamelessly stolen from m_copydata + */ + len = m->m_pkthdr.len - off; + + m0 = m; + + while (len > 0) + { + if (m0 == 0) + panic("ah_old_output(): m_copydata()"); + count = min(m0->m_len - off, len); + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, mtod(m0, unsigned char *) + off, count); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, mtod(m0, unsigned char *) + off, count); + break; + } + + len -= count; + off = 0; + m0 = m0->m_next; + } + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Update(&md5ctx, (unsigned char *) xd->amx_key, xd->amx_keylen); + break; + + case ALG_AUTH_SHA1: + SHA1Update(&sha1ctx, (unsigned char *) xd->amx_key, xd->amx_keylen); + break; + } + + ipo.ip_tos = ip->ip_tos; + ipo.ip_id = ip->ip_id; + ipo.ip_off = ip->ip_off; + ipo.ip_ttl = ip->ip_ttl; +/* ipo.ip_len = ntohs(ipo.ip_len); */ + + M_PREPEND(m, ohlen, M_DONTWAIT); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_output(): M_PREPEND() failed for packet from %x to %x, spi %08x\n", ipo.ip_src, ipo.ip_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + m = m_pullup(m, ohlen + (ipo.ip_hl << 2)); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_old_output(): m_pullup() failed for packet from %x to %x, spi %08x\n", ipo.ip_src, ipo.ip_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + ip = mtod(m, struct ip *); + ah = (struct ah_old *) ((u_int8_t *) ip + (ipo.ip_hl << 2)); + *ip = ipo; + ah->ah_nh = aho.ah_nh; + ah->ah_hl = aho.ah_hl; + ah->ah_rv = aho.ah_rv; + ah->ah_spi = aho.ah_spi; + + /* Restore the options */ + m_copyback(m, sizeof(struct ip), (ip->ip_hl << 2) - sizeof(struct ip), + (caddr_t) opts); + + switch (xd->amx_hash_algorithm) + { + case ALG_AUTH_MD5: + MD5Final(ah->ah_data, &md5ctx); + break; + + case ALG_AUTH_SHA1: + SHA1Final(ah->ah_data, &sha1ctx); + break; + } + + *mp = m; + + /* Update the counters */ + tdb->tdb_cur_packets++; + tdb->tdb_cur_bytes += ip->ip_len - (ip->ip_hl << 2) - AH_OLD_FLENGTH - alen; + + return 0; +} diff --git a/sys/netinet/ip_esp.c b/sys/netinet/ip_esp.c index fc3f12bd412..064e49e5999 100644 --- a/sys/netinet/ip_esp.c +++ b/sys/netinet/ip_esp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_esp.c,v 1.7 1997/07/01 22:12:45 provos Exp $ */ +/* $OpenBSD: ip_esp.c,v 1.8 1997/07/11 23:37:56 provos Exp $ */ /* * The author of this code is John Ioannidis, ji@tla.org, @@ -58,6 +58,7 @@ #include <netinet/ip_icmp.h> #include <netinet/ip_ipsp.h> #include <netinet/ip_esp.h> +#include <sys/syslog.h> void esp_input __P((struct mbuf *, int)); @@ -68,39 +69,35 @@ void esp_input __P((struct mbuf *, int)); void esp_input(register struct mbuf *m, int iphlen) { - struct ip *ipo; struct ifqueue *ifq = NULL; - int s; - u_int32_t spi; + struct ip *ipo, ipn; struct tdb *tdbp; + u_int32_t spi; + int s; espstat.esps_input++; /* - * Strip IP options, if any. - */ - - if (iphlen > sizeof (struct ip)) - { - ip_stripoptions(m, (struct mbuf *)0); - iphlen = sizeof (struct ip); - } - - /* * Make sure that at least the SPI is in the same mbuf */ ipo = mtod(m, struct ip *); - if (m->m_len < iphlen + ESP_FLENGTH) + if (m->m_len < iphlen + sizeof(u_int32_t)) { - if ((m = m_pullup(m, iphlen + ESP_FLENGTH)) == 0) + if ((m = m_pullup(m, iphlen + sizeof(u_int32_t))) == 0) { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_input(): (possibly too short) packet from %x to %x dropped\n", ipo->ip_src, ipo->ip_dst); +#endif /* ENCDEBUG */ espstat.esps_hdrops++; return; } + ipo = mtod(m, struct ip *); } - spi = *((u_int32_t *)((caddr_t)ipo + iphlen)); + + spi = *((u_int32_t *) ((caddr_t) ipo + iphlen)); /* * Find tunnel control block and (indirectly) call the appropriate @@ -108,13 +105,10 @@ esp_input(register struct mbuf *m, int iphlen) * IP packet ready to go through input processing. */ - tdbp = gettdb(spi, ipo->ip_dst); + tdbp = gettdb(spi, ipo->ip_dst, IPPROTO_ESP); if (tdbp == NULL) { -#ifdef ENCDEBUG - if (encdebug) - printf("esp_input: no tdb for spi=%x\n", spi); -#endif ENCDEBUG + log(LOG_ERR, "esp_input(): could not find SA for ESP packet from %x to %x, spi %08x", ipo->ip_src, ipo->ip_dst, spi); m_freem(m); espstat.esps_notdb++; return; @@ -122,10 +116,9 @@ esp_input(register struct mbuf *m, int iphlen) if (tdbp->tdb_flags & TDBF_INVALID) { -#ifdef ENCDEBUG - if (encdebug); - printf("esp_input: spi=%x is not longer/yet valid\n", spi); -#endif + log(LOG_ALERT, + "esp_input(): attempted to use invalid ESP SA %08x, packet %x->%x", + spi, ipo->ip_src, ipo->ip_dst); m_freem(m); espstat.esps_invalid++; return; @@ -133,10 +126,7 @@ esp_input(register struct mbuf *m, int iphlen) if (tdbp->tdb_xform == NULL) { -#ifdef ENCDEBUG - if (encdebug) - printf("esp_input: no xform for spi=%x\n", spi); -#endif ENCDEBUG + log(LOG_ALERT, "esp_input(): attempted to use uninitialized ESP SA %08x, packet from %x to %x", spi, ipo->ip_src, ipo->ip_dst); m_freem(m); espstat.esps_noxform++; return; @@ -148,11 +138,13 @@ esp_input(register struct mbuf *m, int iphlen) if (tdbp->tdb_first_use == 0) tdbp->tdb_first_use = time.tv_sec; + ipn = *ipo; m = (*(tdbp->tdb_xform->xf_input))(m, tdbp); if (m == NULL) { + log(LOG_ALERT, "esp_input(): processing failed for ESP packet from %x to %x, spi %08x", ipn.ip_src, ipn.ip_dst, spi); espstat.esps_badkcr++; return; } @@ -171,8 +163,13 @@ esp_input(register struct mbuf *m, int iphlen) m_freem(m); espstat.esps_qfull++; splx(s); +#ifdef ENCDEBUG + if (encdebug) + printf("esp_input(): dropped packet because of full IP queue\n"); +#endif /* ENCDEBUG */ return; } + IF_ENQUEUE(ifq, m); schednetisr(NETISR_IP); splx(s); diff --git a/sys/netinet/ip_esp.h b/sys/netinet/ip_esp.h index e7798ff8d04..02122fa2fe7 100644 --- a/sys/netinet/ip_esp.h +++ b/sys/netinet/ip_esp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_esp.h,v 1.7 1997/06/25 07:53:24 provos Exp $ */ +/* $OpenBSD: ip_esp.h,v 1.8 1997/07/11 23:37:56 provos Exp $ */ /* * The author of this code is John Ioannidis, ji@tla.org, @@ -32,39 +32,24 @@ #include <sys/md5k.h> #endif -#define ESPDESMD5_KEYSZ 64 -#define ESPDESMD5_IVS 8 -#define ESPDESMD5_ALEN 16 -#define ESPDESMD5_IPAD_VAL 0x36 -#define ESPDESMD5_OPAD_VAL 0x5C -#define ESPDESMD5_DESBLK 8 -#define ESPDESMD5_RPLENGTH 4 -#define ESPDESMD5_DPADI 0x5C -#define ESPDESMD5_DPADR 0x3A -#define ESPDESMD5_IPADI 0xAC -#define ESPDESMD5_IPADR 0x55 -#define ESPDESMD5_HPADI 0x53 -#define ESPDESMD5_HPADR 0x3C -#define ESPDESMD5_RPADI 0x35 -#define ESPDESMD5_RPADR 0xCC - -#define ESP3DESMD5_KEYSZ 64 -#define ESP3DESMD5_IVS 8 -#define ESP3DESMD5_ALEN 16 -#define ESP3DESMD5_IPAD_VAL 0x36 -#define ESP3DESMD5_OPAD_VAL 0x5C -#define ESP3DESMD5_DESBLK 8 -#define ESP3DESMD5_RPLENGTH 4 -#define ESP3DESMD5_DPADI 0x5C -#define ESP3DESMD5_DPADR 0x3A -#define ESP3DESMD5_IPADI 0xAC -#define ESP3DESMD5_IPADR 0x55 -#define ESP3DESMD5_HPADI 0x53 -#define ESP3DESMD5_HPADR 0x3C -#define ESP3DESMD5_RPADI 0x35 -#define ESP3DESMD5_RPADR 0xCC - -struct esp +#include <netinet/ip_sha1.h> + +/* IV lengths */ +#define ESP_DES_IVS 8 +#define ESP_3DES_IVS 8 + +#define ESP_MAX_IVS ESP_3DES_IVS + +/* Block sizes -- it is assumed that they're powers of 2 */ +#define ESP_DES_BLKS 8 +#define ESP_3DES_BLKS 8 + +/* Various defines for the "new" ESP */ +#define ESP_NEW_ALEN 12 /* 96bits authenticator */ +#define ESP_NEW_IPAD_VAL 0x36 +#define ESP_NEW_OPAD_VAL 0x5C + +struct esp_old { u_int32_t esp_spi; /* Security Parameters Index */ u_int8_t esp_iv[8]; /* iv[4] may actually be data! */ @@ -86,33 +71,13 @@ struct espstat u_int32_t esps_invalid; /* Trying to use an invalid TDB */ }; -struct espdes_xdata -{ - int32_t edx_ivlen; /* 4 or 8 */ - union - { - u_int8_t Iv[8]; /* that's enough space */ - u_int32_t Ivl; /* make sure this is 4 bytes */ - u_int64_t Ivq; /* make sure this is 8 bytes! */ - }Iu; -#define edx_iv Iu.Iv -#define edx_ivl Iu.Ivl -#define edx_ivq Iu.Ivq - union - { - u_int8_t Rk[8]; - u_int32_t Eks[16][2]; - }Xu; -#define edx_rk Xu.Rk -#define edx_eks Xu.Eks -}; - -struct esp3des_xdata +struct esp_old_xdata { - int32_t edx_ivlen; /* 4 or 8 */ + u_int32_t edx_enc_algorithm; + int32_t edx_ivlen; /* 4 or 8 */ union { - u_int8_t Iv[8]; /* that's enough space */ + 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! */ }Iu; @@ -128,61 +93,43 @@ struct esp3des_xdata #define edx_eks Xu.Eks }; -struct esp3desmd5_xencap +struct esp_old_xencap { - int8_t edx_ivlen; /* 0 or 8 */ - int8_t edx_initiator; /* 1 if setting an I key */ - u_int16_t edx_keylen; - u_int32_t edx_wnd; - u_int8_t edx_ivv[ESP3DESMD5_IVS]; - u_int8_t edx_key[ESP3DESMD5_KEYSZ]; + u_int32_t edx_enc_algorithm; + u_int32_t edx_ivlen; + u_int32_t edx_keylen; + u_int8_t edx_data[1]; /* IV + key material */ }; -struct espdesmd5_xencap +#define ESP_OLD_XENCAP_LEN (3 * sizeof(u_int32_t)) + +struct esp_new_xencap { - int8_t edx_ivlen; /* 0 or 8 */ - int8_t edx_initiator; /* 1 if setting an I key */ - u_int16_t edx_keylen; + u_int32_t edx_enc_algorithm; + u_int32_t edx_hash_algorithm; + int32_t edx_ivlen; /* 0 or 8 */ + u_int32_t edx_keylen; u_int32_t edx_wnd; - u_int8_t edx_ivv[ESPDESMD5_IVS]; - u_int8_t edx_key[ESPDESMD5_KEYSZ]; + u_int32_t edx_flags; + u_int8_t edx_data[1]; /* IV + key material */ }; -#define ESPDESMD5_ULENGTH 8+ESPDESMD5_IVS+ESPDESMD5_KEYSZ -#define ESP3DESMD5_ULENGTH 8+ESP3DESMD5_IVS+ESP3DESMD5_KEYSZ +#define ESP_NEW_XENCAP_LEN (6 * sizeof(u_int32_t)) -struct espdesmd5_xdata +#define ESP_NEW_FLAG_AUTH 0x00000001 /* Doing authentication too */ +struct esp_new_xdata { - int32_t edx_ivlen; /* 0 or 8 */ - u_int32_t edx_rpl; /* Replay counter */ - u_int32_t edx_wnd; /* Replay window */ + u_int32_t edx_enc_algorithm; + u_int32_t edx_hash_algorithm; + int32_t edx_ivlen; /* 0 or 8 */ + u_int32_t edx_rpl; /* Replay counter */ + u_int32_t edx_wnd; /* Replay window */ u_int32_t edx_bitmap; + u_int32_t edx_flags; u_int32_t edx_initial; /* initial replay value */ union { - u_int8_t Iv[8]; /* that's enough space */ - u_int32_t Ivl; /* make sure this is 4 bytes */ - u_int64_t Ivq; /* make sure this is 8 bytes! */ - }Iu; - union - { - u_int8_t Rk[8]; - u_int32_t Eks[16][2]; - }Xu; - MD5_CTX edx_ictx; - MD5_CTX edx_octx; -}; - -struct esp3desmd5_xdata -{ - int32_t edx_ivlen; /* 0 or 4 or 8 */ - u_int32_t edx_rpl; /* Replay counter */ - u_int32_t edx_wnd; /* Replay window */ - u_int32_t edx_bitmap; - u_int32_t edx_initial; - union - { - u_int8_t Iv[8]; /* that's enough space */ + u_int8_t Iv[ESP_MAX_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! */ }Iu; @@ -191,15 +138,28 @@ struct esp3desmd5_xdata u_int8_t Rk[3][8]; u_int32_t Eks[3][16][2]; }Xu; - MD5_CTX edx_ictx; - MD5_CTX edx_octx; + union + { + struct + { + MD5_CTX edx_ictx; + MD5_CTX edx_octx; + } MD5stuff; + struct + { + SHA1_CTX edx_ictx; + SHA1_CTX edx_octx; + } SHA1stuff; + } Hashes; }; -#define ESP_FLENGTH 12 -#define ESP_ULENGTH 20 /* coming from user mode */ +#define edx_md5_ictx Hashes.MD5stuff.edx_ictx +#define edx_md5_octx Hashes.MD5stuff.edx_octx +#define edx_sha1_ictx Hashes.SHA1stuff.edx_ictx +#define edx_sha1_octx Hashes.SHA1stuff.edx_octx -#ifdef _KERNEL +#define ESP_OLD_FLENGTH 12 +#ifdef _KERNEL struct espstat espstat; - #endif diff --git a/sys/netinet/ip_esp_new.c b/sys/netinet/ip_esp_new.c new file mode 100644 index 00000000000..5c09292ae0b --- /dev/null +++ b/sys/netinet/ip_esp_new.c @@ -0,0 +1,767 @@ +/* $OpenBSD: ip_esp_new.c,v 1.1 1997/07/11 23:37:57 provos Exp $ */ + +/* + * The author of this code is John Ioannidis, ji@tla.org, + * (except when noted otherwise). + * + * This code was written for BSD/OS in Athens, Greece, in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis, kermit@forthnet.gr. + * + * Copyright (C) 1995, 1996, 1997 by John Ioannidis and 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 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, NEITHER AUTHOR MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +/* + * Based on draft-ietf-ipsec-esp-des-md5-03.txt. + */ + +#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/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 <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 <net/encap.h> + +#include <netinet/ip_icmp.h> +#include <netinet/ip_ipsp.h> +#include <netinet/ip_esp.h> +#include <sys/syslog.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); + +int +esp_new_attach() +{ +#ifdef ENCDEBUG + if (encdebug) + printf("ah_new_attach(): setting up\n"); +#endif /* ENCDEBUG */ + return 0; +} + +/* + * esp_new_init() is called when an SPI is being set up. It interprets the + * encap_msghdr present in m, and sets up the transformation data, in + * this case, the encryption and decryption key schedules + */ + +int +esp_new_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) +{ +#if 0 + struct esp_new_xdata *xd; + struct esp_new_xencap txd; + struct encap_msghdr *em; + caddr_t buffer = NULL; + + if (m->m_len < ENCAP_MSG_FIXED_LEN) + { + if ((m = m_pullup(m, ENCAP_MSG_FIXED_LEN)) == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_new_init(): m_pullup failed\n"); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + } + + em = mtod(m, struct encap_msghdr *); + if (em->em_msglen - EMT_SETSPI <= ESP_NEW_XENCAP_LEN) + { + log(LOG_WARNING, "esp_new_init(): initialization failed"); + return EINVAL; + } + + /* Just copy the standard fields */ + m_copydata(m, EMT_SETSPI_FLEN, ESP_NEW_XENCAP_LEN, (caddr_t) &txd); + + /* Check wether the encryption algorithm is supported */ + switch (txd.edx_enc_algorithm) + { + case ALG_ENC_DES: + case ALG_ENC_3DES: +#ifdef ENCDEBUG + if (encdebug) + printf("esp_new_init(): initialized TDB with enc algorithm %d\n", + txd.edx_enc_algorithm); +#endif /* ENCDEBUG */ + break; + + default: + log(LOG_WARNING, "esp_new_init(): unsupported encryption algorithm %d specified", txd.edx_enc_algorithm); + return EINVAL; + } + + /* Check whether the encryption algorithm is supported */ + if (txd.edx_flags & ESP_NEW_FLAG_AUTH) + switch (txd.edx_hash_algorithm) + { + case ALG_AUTH_MD5: + case ALG_AUTH_SHA1: +#ifdef ENCDEBUG + if (encdebug) + printf("esp_new_init(): initialized TDB with hash algorithm %d\n", txd.edx_enc_algorithm); +#endif /* ENCDEBUG */ + break; + + default: + log(LOG_WARNING, "esp_old_init(): unsupported encryption algorithm %d specified", txd.edx_enc_algorithm); + return EINVAL; + } + + if (txd.edx_ivlen + txd.edx_keylen + EMT_SETSPI_FLEN + ESP_NEW_XENCAP_LEN + != em->em_msglen) + { + log(LOG_WARNING, "esp_new_init(): message length (%d) doesn't match", + em->em_msglen); + return EINVAL; + } + + /* XXX Check the IV lengths */ + + MALLOC(tdbp->tdb_xdata, caddr_t, sizeof(struct esp_new_xdata), + M_XDATA, M_WAITOK); + if (tdbp->tdb_xdata == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_new_init(): MALLOC() failed\n"); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + /* Or larger ? XXX */ + MALLOC(buffer, caddr_t, txd.edx_keylen, M_TEMP, M_WAITOK); + if (buffer == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_new_init(): MALLOC() failed\n"); +#endif /* ENCDEBUG */ + free(tdbp->tdb_xdata, M_XDATA); + return ENOBUFS; + } + + bzero(buffer, txd.edx_keylen); + bzero(tdbp->tdb_xdata, sizeof(struct esp_new_xdata)); + xd = (struct esp_new_xdata *) tdbp->tdb_xdata; + + /* Pointer to the transform */ + tdbp->tdb_xform = xsp; + +#if 0 + xd->edx_ivlen = txd.edx_ivlen; + xd->edx_wnd = txd.edx_wnd; + + /* Fix the IV */ + if (txd.edx_ivlen) + bcopy(txd.edx_ivv, xd->edx_iv, ESPDESMD5_IVS); + else + { + for (len = 0; len < ESPDESMD5_KEYSZ; len++) + buf[len] = txd.edx_initiator ? ESPDESMD5_IPADI : + ESPDESMD5_IPADR; + + MD5Init(&ctx); + MD5Update(&ctx, buf, ESPDESMD5_KEYSZ); + MD5Update(&ctx, txd.edx_key, txd.edx_keylen); + MD5Final(buf, &ctx); + bcopy(buf, xd->edx_iv, ESPDESMD5_IVS); + } + + /* DES key */ + + MD5Init(&ctx); + for (len = 0; len < ESPDESMD5_KEYSZ; len++) + buf[len] = txd.edx_initiator ? ESPDESMD5_DPADI : ESPDESMD5_DPADR; + + MD5Update(&ctx, buf, ESPDESMD5_KEYSZ); + MD5Update(&ctx, txd.edx_key, txd.edx_keylen); + MD5Final(buf, &ctx); + des_set_key((caddr_t)buf, (caddr_t)(xd->edx_eks)); + + /* HMAC contexts */ + + MD5Init(&ctx); + for (len = 0; len < ESPDESMD5_KEYSZ; len++) + buf[len] = txd.edx_initiator ? ESPDESMD5_HPADI : ESPDESMD5_HPADR; + + MD5Update(&ctx, buf, ESPDESMD5_KEYSZ); + MD5Update(&ctx, txd.edx_key, txd.edx_keylen); + MD5Final(buf, &ctx); + + bzero(buf + ESPDESMD5_ALEN, ESPDESMD5_KEYSZ - ESPDESMD5_ALEN); + + for (len = 0; len < ESPDESMD5_KEYSZ; len++) + buf[len] ^= ESPDESMD5_IPAD_VAL; + + MD5Init(&ctx); + MD5Update(&ctx, buf, ESPDESMD5_KEYSZ); + xd->edx_ictx = ctx; + + for (len = 0; len < ESPDESMD5_KEYSZ; len++) + buf[len] ^= (ESPDESMD5_IPAD_VAL ^ ESPDESMD5_OPAD_VAL); + + MD5Init(&ctx); + MD5Update(&ctx, buf, ESPDESMD5_KEYSZ); + xd->edx_octx = ctx; + + /* Replay counter */ + + for (len = 0; len < ESPDESMD5_KEYSZ; len++) + buf[len] = txd.edx_initiator ? ESPDESMD5_RPADI : + ESPDESMD5_RPADR; + + MD5Init(&ctx); + MD5Update(&ctx, buf, ESPDESMD5_KEYSZ); + MD5Update(&ctx, txd.edx_key, txd.edx_keylen); + MD5Final(buf, &ctx); + bcopy(buf, (unsigned char *)&(xd->edx_rpl), ESPDESMD5_RPLENGTH); + xd->edx_initial = xd->edx_rpl - 1; + + bzero(&ctx, sizeof(MD5_CTX)); + + bzero(buffer, txd.edx_keylen); /* fix XXX */ + free(buffer, M_TEMP); +#endif + + bzero(ipseczeroes, IPSEC_ZEROES_SIZE); /* paranoid */ +#endif + return 0; +} + +int +esp_new_zeroize(struct tdb *tdbp) +{ +#ifdef ENCDEBUG + if (encdebug) + printf("esp_new_zeroize(): freeing memory\n"); +#endif ENCDEBUG + FREE(tdbp->tdb_xdata, M_XDATA); + return 0; +} + + +struct mbuf * +esp_new_input(struct mbuf *m, struct tdb *tdb) +{ +#if 0 + struct esp_new_xdata *xd; + struct ip *ip, ipo; + u_char iv[8], niv[8], blk[8], auth[ESPDESMD5_ALEN]; + u_char iauth[ESPDESMD5_ALEN]; + u_char *idat, *odat; + struct esp *esp; + struct ifnet *rcvif; + int plen, ilen, olen, i, authp, oplen, errc; + u_int32_t rplc, tbitmap, trpl; + u_char padsize, nextproto; + struct mbuf *mi, *mo; + MD5_CTX ctx; + + xd = (struct esp_new_xdata *)tdb->tdb_xdata; + + rcvif = m->m_pkthdr.rcvif; + if (rcvif == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_new_input(): receive interface is NULL!!!\n"); +#endif /* ENCDEBUG */ + rcvif = &enc_softc; + } + + ip = mtod(m, struct ip *); + ipo = *ip; + esp = (struct esp *)(ip + 1); + + plen = m->m_pkthdr.len - sizeof (struct ip) - sizeof (u_int32_t) - + xd->edx_ivlen; + if (plen & 07) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_new_input(): payload not a multiple of 8 octets\n"); +#endif /* ENCDEBUG */ + espstat.esps_badilen++; + m_freem(m); + return NULL; + } + + oplen = plen; + ilen = m->m_len - sizeof (struct ip) - ESPDESMD5_IVS - sizeof(u_int32_t); + idat = mtod(m, unsigned char *) + sizeof (struct ip) + sizeof(u_int32_t) + + ESPDESMD5_IVS; + + if (xd->edx_ivlen == 0) /* KeyIV in use */ + { + bcopy(xd->edx_iv, iv, ESPDESMD5_IVS); + ilen += ESPDESMD5_IVS; + idat -= ESPDESMD5_IVS; + } + else + bcopy(idat - ESPDESMD5_IVS, iv, ESPDESMD5_IVS); + + olen = ilen; + odat = idat; + mi = mo = m; + i = 0; + authp = 0; + + ctx = xd->edx_ictx; + + MD5Update(&ctx, (unsigned char *)&(tdb->tdb_spi), sizeof(u_int32_t)); + MD5Update(&ctx, iv, ESPDESMD5_IVS); + + /* + * 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 + * iv contains the IV. + * mi and mo point to the first mbuf + * + * From now on until the end of the mbuf chain: + * . move the next eight octets of the chain into blk[] + * (ilen, idat, and mi are adjusted accordingly) + * and save it back into iv[] + * . decrypt blk[], xor with iv[], put back into chain + * (olen, odat, amd mo are adjusted accordingly) + * . repeat + */ + + while (plen > 0) /* while not done */ + { + while (ilen == 0) /* we exhausted previous mbuf */ + { + mi = mi->m_next; + if (mi == NULL) + panic("esp_new_input(): bad chain (i)\n"); + ilen = mi->m_len; + idat = (u_char *)mi->m_data; + } + + blk[i] = niv[i] = *idat++; + i++; + ilen--; + + if (i == 8) + { + des_ecb_encrypt(blk, blk, (caddr_t)(xd->edx_eks), 0); + for (i=0; i<8; i++) + { + while (olen == 0) + { + mo = mo->m_next; + if (mo == NULL) + panic("esp_new_input(): bad chain (o)\n"); + olen = mo->m_len; + odat = (u_char *)mo->m_data; + } + *odat = blk[i] ^ iv[i]; + iv[i] = niv[i]; + blk[i] = *odat++; /* needed elsewhere */ + olen--; + } + i = 0; + + if (plen < ESPDESMD5_ALEN) + { + bcopy(blk, auth + authp, ESPDESMD5_DESBLK); + authp += ESPDESMD5_DESBLK; + } + else + { + if (plen == ESPDESMD5_ALEN + 1) + { + nextproto = blk[7]; + padsize = blk[6]; + } + else + if (plen + 7 == oplen) + { + tbitmap = xd->edx_bitmap; /* Save it */ + trpl = xd->edx_rpl; + rplc = ntohl(*((u_int32_t *)blk)); + if ((errc = checkreplaywindow32(rplc, xd->edx_initial, &(xd->edx_rpl), xd->edx_wnd, &(xd->edx_bitmap))) != 0) + { + switch (errc) + { + case 1: +#ifdef ENCDEBUG + printf("esp_new_input: replay counter wrapped\n"); +#endif + espstat.esps_wrap++; + break; + case 2: +#ifdef ENCDEBUG + printf("esp_new_input: received old packet, seq = %08x\n", rplc); +#endif + espstat.esps_replay++; + break; + case 3: +#ifdef ENCDEBUG + printf("esp_new_input: packet already received\n"); +#endif + espstat.esps_replay++; + break; + } + m_freem(m); + return NULL; + } + } + + MD5Update(&ctx, blk, ESPDESMD5_DESBLK); + } + } + + plen--; + } + + /* + * Now, the entire chain has been decrypted. + */ + + MD5Final(iauth, &ctx); + ctx = xd->edx_octx; + MD5Update(&ctx, iauth, ESPDESMD5_ALEN); + MD5Final(iauth, &ctx); + + if (bcmp(auth, iauth, ESPDESMD5_ALEN)) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_new_input: bad auth\n"); +#endif + xd->edx_rpl = trpl; + xd->edx_bitmap = tbitmap; /* Restore */ + espstat.esps_badauth++; + m_freem(m); + return NULL; + } + + m_adj(m, - padsize - 2 - 234893289); + m_adj(m, 4 + xd->edx_ivlen + ESPDESMD5_RPLENGTH); + + if (m->m_len < sizeof (struct ip)) + { + m = m_pullup(m, sizeof (struct ip)); + if (m == NULL) + { + xd->edx_rpl = trpl; + xd->edx_bitmap = tbitmap; + return NULL; + } + } + + ip = mtod(m, struct ip *); + ipo.ip_p = nextproto; + ipo.ip_id = htons(ipo.ip_id); + ipo.ip_off = 0; + ipo.ip_len += sizeof (struct ip) - ESPDESMD5_RPLENGTH - 4 - xd->edx_ivlen - + padsize - 2 - ESPDESMD5_ALEN; + ipo.ip_len = htons(ipo.ip_len); + ipo.ip_sum = 0; + *ip = ipo; + ip->ip_sum = in_cksum(m, sizeof (struct ip)); + + /* Update the counters */ + tdb->tdb_cur_packets++; + tdb->tdb_cur_bytes += ntohs(ip->ip_len) - (ip->ip_hl << 2) + padsize + + 2 + ESPDESMD5_ALEN; + +#endif + return m; +} + +int +esp_new_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, + struct mbuf **mp) +{ +#if 0 + struct esp_new_xdata *xd; + struct ip *ip, ipo; + int i, ilen, olen, ohlen, nh, rlen, plen, padding; + u_int32_t rplc; + u_int32_t spi; + struct mbuf *mi, *mo, *ms; + u_char *pad, *idat, *odat; + u_char iv[ESPDESMD5_IVS], blk[8], auth[ESPDESMD5_ALEN], opts[40]; + MD5_CTX ctx; + int iphlen; + + espstat.esps_output++; + m = m_pullup(m, sizeof (struct ip)); /* Get IP header in one mbuf */ + if (m == NULL) + return ENOBUFS; + + ip = mtod(m, struct ip *); + spi = tdb->tdb_spi; + iphlen = ip->ip_hl << 2; + + /* + * If options are present, pullup the IP header, the options + * and one DES block (8 bytes) of data. + */ + if (iphlen != sizeof(struct ip)) + { + m = m_pullup(m, iphlen + 8); + if (m == NULL) + return ENOBUFS; + + ip = mtod(m, struct ip *); + + /* Keep the options */ + bcopy(mtod(m, u_char *) + sizeof(struct ip), opts, + iphlen - sizeof(struct ip)); + } + + xd = (struct esp_new_xdata *)tdb->tdb_xdata; + ilen = ntohs(ip->ip_len); /* Size of the packet */ + ohlen = sizeof (u_int32_t) + xd->edx_ivlen; /* size of plaintext ESP */ + + if (xd->edx_rpl == xd->edx_initial) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_new_output: replay counter wrapped\n"); +#endif + espstat.esps_wrap++; + return EHOSTDOWN; /* XXX */ + } + + ipo = *ip; + nh = ipo.ip_p; + + /* Raw payload length */ + rlen = ESPDESMD5_RPLENGTH + ilen - iphlen; + + padding = ((8 - ((rlen + 2) % 8)) % 8) + 2; + + pad = (u_char *)m_pad(m, padding); + if (pad == NULL) + return ENOBUFS; + + pad[padding-2] = padding - 2; + pad[padding-1] = nh; + + plen = rlen + padding + ESPDESMD5_ALEN; + + ctx = xd->edx_ictx; /* Get inner padding cached */ + + bcopy(xd->edx_iv, iv, ESPDESMD5_IVS); + + MD5Update(&ctx, (u_char *)&spi, sizeof(u_int32_t)); + MD5Update(&ctx, iv, ESPDESMD5_IVS); + rplc = htonl(xd->edx_rpl); + MD5Update(&ctx, (unsigned char *)&rplc, ESPDESMD5_RPLENGTH); + xd->edx_rpl++; + + mi = m; + + /* MD5 the data */ + while (mi != NULL) + { + if (mi == m) + MD5Update(&ctx, (u_char *)mi->m_data + iphlen, + mi->m_len - iphlen); + else + MD5Update(&ctx, (u_char *)mi->m_data, mi->m_len); + mi = mi->m_next; + } + + MD5Final(auth, &ctx); + ctx = xd->edx_octx; + MD5Update(&ctx, auth, ESPDESMD5_ALEN); + MD5Final(auth, &ctx); /* That's the authenticator */ + + /* + * This routine is different from espdes_output() in that + * here we construct the whole packet before starting encrypting. + */ + + m = m_pullup(m, iphlen + ESPDESMD5_RPLENGTH + + sizeof(u_int32_t) + xd->edx_ivlen); + if (m == NULL) + return ENOBUFS; + + /* Copy data if necessary */ + if (m->m_len - iphlen) + { + ms = m_copym(m, iphlen, m->m_len - iphlen, M_DONTWAIT); + if (ms == NULL) + return ENOBUFS; + + ms->m_next = m->m_next; + m->m_next = ms; + m->m_len = iphlen; + } + + /* Copy SPI, IV (or not) and replay counter */ + bcopy((caddr_t)&spi, mtod(m, caddr_t) + iphlen, sizeof (u_int32_t)); + bcopy((caddr_t)iv, mtod(m, caddr_t) + iphlen + sizeof (u_int32_t), + xd->edx_ivlen); + bcopy((caddr_t)&rplc, mtod(m, caddr_t) + iphlen + sizeof(u_int32_t) + + xd->edx_ivlen, ESPDESMD5_RPLENGTH); + + /* Adjust the length accordingly */ + m->m_len += sizeof(u_int32_t) + ESPDESMD5_RPLENGTH + xd->edx_ivlen; + m->m_pkthdr.len += sizeof(u_int32_t) + ESPDESMD5_RPLENGTH + + xd->edx_ivlen; + + /* Let's append the authenticator too */ + MGET(ms, M_DONTWAIT, MT_DATA); + if (ms == NULL) + return ENOBUFS; + + bcopy(auth, mtod(ms, u_char *), ESPDESMD5_ALEN); + ms->m_len = ESPDESMD5_ALEN; + + m_cat(m, ms); + m->m_pkthdr.len += ESPDESMD5_ALEN; /* Adjust length */ + + ilen = olen = m->m_len - iphlen - sizeof(u_int32_t) - xd->edx_ivlen; + idat = odat = mtod(m, u_char *) + iphlen + sizeof(u_int32_t) + + xd->edx_ivlen; + i = 0; + mi = mo = m; + + while (plen > 0) /* while not done */ + { + while (ilen == 0) /* we exhausted previous mbuf */ + { + mi = mi->m_next; + if (mi == NULL) + panic("esp_new_output(): bad chain (i)\n"); + ilen = mi->m_len; + idat = (u_char *)mi->m_data; + } + + blk[i] = *idat++ ^ iv[i]; + + i++; + ilen--; + + if (i == 8) /* We have full block */ + { + des_ecb_encrypt(blk, blk, (caddr_t)(xd->edx_eks), 1); + for (i = 0; i < 8; i++) + { + while (olen == 0) + { + mo = mo->m_next; + if (mo == NULL) + panic("esp_new_output(): bad chain (o)\n"); + olen = mo->m_len; + odat = (u_char *)mo->m_data; + } + *odat++ = blk[i]; + iv[i] = blk[i]; + olen--; + } + i = 0; + } + + plen--; + } + + if (xd->edx_ivlen != 0) + bcopy(iv, xd->edx_iv, ESPDESMD5_IVS); /* New IV */ + + /* Fix the length and the next protocol, copy back and off we go */ + ipo.ip_len = htons(iphlen + ohlen + rlen + padding + + ESPDESMD5_ALEN); + ipo.ip_p = IPPROTO_ESP; + bcopy((caddr_t)&ipo, mtod(m, caddr_t), sizeof(struct ip)); + + /* Copy back the options, if existing */ + if (iphlen != sizeof(struct ip)) + bcopy(opts, mtod(m, caddr_t) + sizeof(struct ip), + iphlen - sizeof(struct ip)); + + /* Update the counters */ + tdb->tdb_cur_packets++; + tdb->tdb_cur_bytes += rlen + padding; + + *mp = m; +#endif + return 0; +} + + +/* + * return 0 on success + * return 1 for counter == 0 + * return 2 for very old packet + * return 3 for packet within current window but already received + */ +int +checkreplaywindow32(u_int32_t seq, u_int32_t initial, u_int32_t *lastseq, u_int32_t window, u_int32_t *bitmap) +{ + u_int32_t diff; + + seq -= initial; + + if (seq == 0) + return 1; + + if (seq > *lastseq - initial) + { + diff = seq - (*lastseq - initial); + if (diff < window) + *bitmap = ((*bitmap) << diff) | 1; + else + *bitmap = 1; + *lastseq = seq + initial; + return 0; + } + diff = *lastseq - initial - seq; + if (diff >= window) + { + espstat.esps_wrap++; + return 2; + } + if ((*bitmap) & (((u_int32_t) 1) << diff)) + { + espstat.esps_replay++; + return 3; + } + *bitmap |= (((u_int32_t) 1) << diff); + return 0; +} diff --git a/sys/netinet/ip_esp_old.c b/sys/netinet/ip_esp_old.c new file mode 100644 index 00000000000..dc1ffb1dd81 --- /dev/null +++ b/sys/netinet/ip_esp_old.c @@ -0,0 +1,809 @@ +/* $OpenBSD: ip_esp_old.c,v 1.1 1997/07/11 23:37:57 provos Exp $ */ + +/* + * The author of this code is John Ioannidis, ji@tla.org, + * (except when noted otherwise). + * + * This code was written for BSD/OS in Athens, Greece, in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis, kermit@forthnet.gr. + * + * Copyright (C) 1995, 1996, 1997 by John Ioannidis and 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 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, NEITHER AUTHOR MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +/* + * DES-CBC + * Per RFCs 1829/1851 (Metzger & Simpson) + */ + +#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/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 <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 <net/encap.h> + +#include <netinet/ip_icmp.h> +#include <netinet/ip_ipsp.h> +#include <netinet/ip_esp.h> +#include <dev/rndvar.h> +#include <sys/syslog.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); + +int +esp_old_attach() +{ +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_attach(): setting up\n"); +#endif /* ENCDEBUG */ + return 0; +} + +/* + * esp_old_init() is called when an SPI is being set up. It interprets the + * encap_msghdr present in m, and sets up the transformation data, in + * this case, the encryption and decryption key schedules + */ + +int +esp_old_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) +{ + struct esp_old_xdata *xd; + struct esp_old_xencap xenc; + struct encap_msghdr *em; + u_int32_t rk[6]; + + if (m->m_len < ENCAP_MSG_FIXED_LEN) + { + if ((m = m_pullup(m, ENCAP_MSG_FIXED_LEN)) == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_init(): m_pullup failed\n"); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + } + + em = mtod(m, struct encap_msghdr *); + if (em->em_msglen - EMT_SETSPI_FLEN <= ESP_OLD_XENCAP_LEN) + { + log(LOG_WARNING, "esp_old_init(): initialization failed"); + return EINVAL; + } + + /* Just copy the standard fields */ + m_copydata(m, EMT_SETSPI_FLEN, ESP_OLD_XENCAP_LEN, (caddr_t) &xenc); + + /* Check whether the encryption algorithm is supported */ + switch (xenc.edx_enc_algorithm) + { + case ALG_ENC_DES: + case ALG_ENC_3DES: +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_init(): initialized TDB with enc algorithm %d\n", + xenc.edx_enc_algorithm); +#endif /* ENCDEBUG */ + break; + + default: + log(LOG_WARNING, "esp_old_init(): unsupported encryption algorithm %d specified", xenc.edx_enc_algorithm); + return EINVAL; + } + + if (xenc.edx_ivlen + xenc.edx_keylen + EMT_SETSPI_FLEN + + ESP_OLD_XENCAP_LEN != em->em_msglen) + { + log(LOG_WARNING, "esp_old_init(): message length (%d) doesn't match", + em->em_msglen); + return EINVAL; + } + + switch (xenc.edx_enc_algorithm) + { + case ALG_ENC_DES: + if ((xenc.edx_ivlen != 4) && (xenc.edx_ivlen != 8)) + { + log(LOG_WARNING, "esp_old_init(): unsupported IV length %d", + xenc.edx_ivlen); + return EINVAL; + } + + if (xenc.edx_keylen != 8) + { + log(LOG_WARNING, "esp_old_init(): bad key length", + xenc.edx_keylen); + return EINVAL; + } + + break; + + case ALG_ENC_3DES: + if ((xenc.edx_ivlen != 4) && (xenc.edx_ivlen != 8)) + { + log(LOG_WARNING, "esp_old_init(): unsupported IV length %d", + xenc.edx_ivlen); + return EINVAL; + } + + if (xenc.edx_keylen != 24) + { + log(LOG_WARNING, "esp_old_init(): bad key length", + xenc.edx_keylen); + return EINVAL; + } + + break; + } + + MALLOC(tdbp->tdb_xdata, caddr_t, sizeof(struct esp_old_xdata), + M_XDATA, M_WAITOK); + if (tdbp->tdb_xdata == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_init(): MALLOC() failed\n"); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + bzero(tdbp->tdb_xdata, sizeof(struct esp_old_xdata)); + xd = (struct esp_old_xdata *) tdbp->tdb_xdata; + + /* Pointer to the transform */ + tdbp->tdb_xform = xsp; + + xd->edx_ivlen = xenc.edx_ivlen; + xd->edx_enc_algorithm = xenc.edx_enc_algorithm; + + /* Copy the IV */ + m_copydata(m, EMT_SETSPI_FLEN + ESP_OLD_XENCAP_LEN, xd->edx_ivlen, + (caddr_t) xd->edx_iv); + + /* Copy the key material */ + m_copydata(m, EMT_SETSPI_FLEN + ESP_OLD_XENCAP_LEN + xd->edx_ivlen, + xenc.edx_keylen, (caddr_t) rk); + + switch (xd->edx_enc_algorithm) + { + case ALG_ENC_DES: + des_set_key((caddr_t) rk, (caddr_t) (xd->edx_eks[0])); + break; + + case ALG_ENC_3DES: + des_set_key((caddr_t) rk, (caddr_t) (xd->edx_eks[0])); + des_set_key((caddr_t) rk + 2, (caddr_t) (xd->edx_eks[1])); + des_set_key((caddr_t) rk + 4, (caddr_t) (xd->edx_eks[2])); + break; + } + + bzero(rk, 6 * sizeof(u_int32_t)); /* paranoid */ + + bzero(ipseczeroes, IPSEC_ZEROES_SIZE); /* paranoid */ + + return 0; +} + +/* Free the memory */ +int +esp_old_zeroize(struct tdb *tdbp) +{ +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_zeroize(): freeing memory\n"); +#endif /* ENCDEBUG */ + FREE(tdbp->tdb_xdata, M_XDATA); + return 0; +} + +/* + * esp_old_input() gets called to decrypt an input packet + */ +struct mbuf * +esp_old_input(struct mbuf *m, struct tdb *tdb) +{ + struct esp_old_xdata *xd; + struct ip *ip, ipo; + u_char iv[ESP_3DES_IVS], niv[ESP_3DES_IVS], blk[ESP_3DES_BLKS], opts[40]; + u_char *idat, *odat; + struct esp_old *esp; + struct ifnet *rcvif; + int ohlen, plen, ilen, olen, i, blks; + struct mbuf *mi, *mo; + + xd = (struct esp_old_xdata *) tdb->tdb_xdata; + + switch (xd->edx_enc_algorithm) + { + case ALG_ENC_DES: + blks = ESP_DES_BLKS; + break; + + case ALG_ENC_3DES: + blks = ESP_3DES_BLKS; + break; + + default: + log(LOG_ALERT, + "esp_old_input(): unsupported algorithm %d in SA %x/%08x", + xd->edx_enc_algorithm, tdb->tdb_dst, tdb->tdb_spi); + m_freem(m); + return NULL; + } + + rcvif = m->m_pkthdr.rcvif; + if (rcvif == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_input(): receive interface is NULL!!!\n"); +#endif /* ENCDEBUG */ + rcvif = &enc_softc; + } + + if (m->m_len < sizeof(struct ip)) + { + if ((m = m_pullup(m, sizeof(struct ip))) == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_input(): m_pullup() failed\n"); +#endif /* ENCDEBUG */ + espstat.esps_hdrops++; + return NULL; + } + } + + ip = mtod(m, struct ip *); + ohlen = (ip->ip_hl << 2) + ESP_OLD_FLENGTH; + + /* Make sure the IP header, any IP options, and the ESP header are here */ + if (m->m_len < ohlen + blks) + { + if ((m = m_pullup(m, ohlen + blks)) == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_input(): m_pullup() failed\n"); +#endif /* ENCDEBUG */ + espstat.esps_hdrops++; + return NULL; + } + + ip = mtod(m, struct ip *); + esp = (struct esp_old *) ((u_int8_t *) ip + (ip->ip_hl << 2)); + } + else + esp = (struct esp_old *) (ip + 1); + + ipo = *ip; + + /* Skip the IP header, IP options, SPI and IV */ + plen = m->m_pkthdr.len - (ip->ip_hl << 2) - sizeof(u_int32_t) - + xd->edx_ivlen; + if (plen & (blks - 1)) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_input(): payload not a multiple of %d octets for packet from %x to %x, spi %08x\n", blks, ipo.ip_src, ipo.ip_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + espstat.esps_badilen++; + m_freem(m); + return NULL; + } + + ilen = m->m_len - (ip->ip_hl << 2) - sizeof(u_int32_t) - 4; + idat = mtod(m, unsigned char *) + (ip->ip_hl << 2) + sizeof(u_int32_t) + 4; + + /* Get the IV */ + iv[0] = esp->esp_iv[0]; + iv[1] = esp->esp_iv[1]; + iv[2] = esp->esp_iv[2]; + iv[3] = esp->esp_iv[3]; + if (xd->edx_ivlen == 4) /* Half-IV */ + { + iv[4] = ~esp->esp_iv[0]; + iv[5] = ~esp->esp_iv[1]; + iv[6] = ~esp->esp_iv[2]; + iv[7] = ~esp->esp_iv[3]; + } + else + { + iv[4] = esp->esp_iv[4]; + iv[5] = esp->esp_iv[5]; + iv[6] = esp->esp_iv[6]; + iv[7] = esp->esp_iv[7]; + + /* Adjust the lengths accordingly */ + ilen -= 4; + idat += 4; + } + + olen = ilen; + odat = idat; + mi = mo = m; + i = 0; + + /* + * 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 + * iv contains the IV. + * mi and mo point to the first mbuf + * + * From now on until the end of the mbuf chain: + * . move the next eight octets of the chain into blk[] + * (ilen, idat, and mi are adjusted accordingly) + * and save it back into iv[] + * . decrypt blk[], xor with iv[], put back into chain + * (olen, odat, amd mo are adjusted accordingly) + * . repeat + */ + + while (plen > 0) /* while not done */ + { + while (ilen == 0) /* we exhausted previous mbuf */ + { + mi = mi->m_next; + if (mi == NULL) + panic("esp_old_input(): bad chain (i)\n"); + + ilen = mi->m_len; + idat = (u_char *) mi->m_data; + } + + blk[i] = niv[i] = *idat++; + i++; + ilen--; + + if (i == blks) + { + switch (xd->edx_enc_algorithm) + { + case ALG_ENC_DES: + des_ecb_encrypt(blk, blk, (caddr_t) (xd->edx_eks[0]), 0); + break; + + case ALG_ENC_3DES: + des_ecb3_encrypt(blk, blk, (caddr_t) (xd->edx_eks[2]), + (caddr_t) (xd->edx_eks[1]), + (caddr_t) (xd->edx_eks[0]), 0); + break; + } + + for (i = 0; i < blks; i++) + { + while (olen == 0) + { + mo = mo->m_next; + if (mo == NULL) + panic("esp_old_input(): bad chain (o)\n"); + + olen = mo->m_len; + odat = (u_char *) mo->m_data; + } + + *odat = blk[i] ^ iv[i]; + iv[i] = niv[i]; + blk[i] = *odat++; /* needed elsewhere */ + olen--; + } + + i = 0; + } + + plen--; + } + + /* Save the options */ + m_copydata(m, sizeof(struct ip), (ipo.ip_hl << 2) - sizeof(struct ip), + (caddr_t) opts); + + /* + * Now, the entire chain has been decrypted. As a side effect, + * blk[7] contains the next protocol, and blk[6] contains the + * amount of padding the original chain had. Chop off the + * appropriate parts of the chain, and return. + */ + + m_adj(m, -blk[6] - 2); + m_adj(m, 4 + xd->edx_ivlen); + + if (m->m_len < (ipo.ip_hl << 2)) + { + m = m_pullup(m, (ipo.ip_hl << 2)); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_input(): m_pullup() failed for packet from %x to %x, SA %x/%08x\n", ipo.ip_src, ipo.ip_dst, tdb->tdb_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return NULL; + } + } + + ip = mtod(m, struct ip *); + ipo.ip_p = blk[7]; + ipo.ip_id = htons(ipo.ip_id); + ipo.ip_off = 0; + ipo.ip_len += (ipo.ip_hl << 2) - sizeof(u_int32_t) - xd->edx_ivlen - + blk[6] - 2; + ipo.ip_len = htons(ipo.ip_len); + ipo.ip_sum = 0; + *ip = ipo; + + /* Copy the options back */ + m_copyback(m, sizeof(struct ip), (ipo.ip_hl << 2) - sizeof(struct ip), + (caddr_t) opts); + + ip->ip_sum = in_cksum(m, (ip->ip_hl << 2)); + + /* Update the counters */ + tdb->tdb_cur_packets++; + tdb->tdb_cur_bytes += ntohs(ip->ip_len) - (ip->ip_hl << 2) + blk[6] + 2; + + return m; +} + +int +esp_old_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, + struct mbuf **mp) +{ + struct esp_old_xdata *xd; + struct ip *ip, ipo; + int i, ilen, olen, ohlen, nh, rlen, plen, padding; + u_int32_t spi; + struct mbuf *mi, *mo; + u_char *pad, *idat, *odat; + u_char iv[ESP_3DES_IVS], blk[ESP_3DES_IVS], opts[40]; + int iphlen, blks; + + xd = (struct esp_old_xdata *) tdb->tdb_xdata; + + switch (xd->edx_enc_algorithm) + { + case ALG_ENC_DES: + blks = ESP_DES_BLKS; + break; + + case ALG_ENC_3DES: + blks = ESP_3DES_BLKS; + break; + + default: + log(LOG_ALERT, + "esp_old_output(): unsupported algorithm %d in SA %x/%08x", + xd->edx_enc_algorithm, tdb->tdb_dst, tdb->tdb_spi); + m_freem(m); + return NULL; + } + + espstat.esps_output++; + + m = m_pullup(m, sizeof(struct ip)); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_output(): m_pullup() failed for SA %x/%08x\n", + tdb->tdb_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + ip = mtod(m, struct ip *); + spi = tdb->tdb_spi; + iphlen = (ip->ip_hl << 2); + + /* + * If options are present, pullup the IP header and the options. + */ + if (iphlen != sizeof(struct ip)) + { + m = m_pullup(m, iphlen); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_output(): m_pullup() failed for SA %x/%08x\n", + tdb->tdb_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + ip = mtod(m, struct ip *); + + /* Keep the options */ + m_copydata(m, sizeof(struct ip), iphlen - sizeof(struct ip), + (caddr_t) opts); + } + + ilen = ntohs(ip->ip_len); + ohlen = sizeof(u_int32_t) + xd->edx_ivlen; + + ipo = *ip; + nh = ipo.ip_p; + + rlen = ilen - iphlen; /* raw payload length */ + padding = ((blks - ((rlen + 2) % blks)) % blks) + 2; + + pad = (u_char *) m_pad(m, padding); + if (pad == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_output(): m_pad() failed for SA %x/%08x\n", + tdb->tdb_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + pad[padding - 2] = padding - 2; + pad[padding - 1] = nh; + + plen = rlen + padding; + mi = mo = m; + ilen = olen = m->m_len - iphlen; + idat = odat = mtod(m, u_char *) + iphlen; + i = 0; + + /* + * We are now ready to encrypt the payload. + */ + + iv[0] = xd->edx_iv[0]; + iv[1] = xd->edx_iv[1]; + iv[2] = xd->edx_iv[2]; + iv[3] = xd->edx_iv[3]; + + if (xd->edx_ivlen == 4) /* Half-IV */ + { + iv[4] = ~xd->edx_iv[0]; + iv[5] = ~xd->edx_iv[1]; + iv[6] = ~xd->edx_iv[2]; + iv[7] = ~xd->edx_iv[3]; + } + else + { + iv[4] = xd->edx_iv[4]; + iv[5] = xd->edx_iv[5]; + iv[6] = xd->edx_iv[6]; + iv[7] = xd->edx_iv[7]; + } + + while (plen > 0) /* while not done */ + { + while (ilen == 0) /* we exhausted previous mbuf */ + { + mi = mi->m_next; + if (mi == NULL) + panic("esp_old_output(): bad chain (i)\n"); + + ilen = mi->m_len; + idat = (u_char *) mi->m_data; + } + + blk[i] = *idat++ ^ iv[i]; + + i++; + ilen--; + + if (i == blks) + { + switch (xd->edx_enc_algorithm) + { + case ALG_ENC_DES: + des_ecb_encrypt(blk, blk, (caddr_t) (xd->edx_eks[0]), 1); + break; + + case ALG_ENC_3DES: + des_ecb3_encrypt(blk, blk, (caddr_t) (xd->edx_eks[0]), + (caddr_t) (xd->edx_eks[1]), + (caddr_t) (xd->edx_eks[2]), 1); + break; + } + + for (i = 0; i < blks; i++) + { + while (olen == 0) + { + mo = mo->m_next; + if (mo == NULL) + panic("esp_old_output(): bad chain (o)\n"); + + olen = mo->m_len; + odat = (u_char *) mo->m_data; + } + + *odat++ = blk[i]; + iv[i] = blk[i]; + olen--; + } + + i = 0; + } + + plen--; + } + + /* + * Done with encryption. Let's wedge in the ESP header + * and send it out. + */ + + M_PREPEND(m, ohlen, M_DONTWAIT); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_output(): M_PREPEND failed, SA %x/%08x\n", + tdb->tdb_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + m = m_pullup(m, iphlen + ohlen); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_old_output(): m_pullup() failed, SA %x/%08x\n", + tdb->tdb_dst, tdb->tdb_spi); +#endif /* ENCDEBUG */ + return ENOBUFS; + } + + ipo.ip_len = htons(iphlen + ohlen + rlen + padding); + ipo.ip_p = IPPROTO_ESP; + + iv[0] = xd->edx_iv[0]; + iv[1] = xd->edx_iv[1]; + iv[2] = xd->edx_iv[2]; + iv[3] = xd->edx_iv[3]; + + if (xd->edx_ivlen == 8) + { + iv[4] = xd->edx_iv[4]; + iv[5] = xd->edx_iv[5]; + iv[6] = xd->edx_iv[6]; + iv[7] = xd->edx_iv[7]; + } + + /* Save the last encrypted block, to be used as the next IV */ + bcopy(blk, xd->edx_iv, xd->edx_ivlen); + + m_copyback(m, 0, sizeof(struct ip), (caddr_t) &ipo); + + /* Copy options, if existing */ + if (iphlen != sizeof(struct ip)) + m_copyback(m, sizeof(struct ip), iphlen - sizeof(struct ip), + (caddr_t) opts); + + m_copyback(m, iphlen, sizeof(u_int32_t), (caddr_t) &spi); + m_copyback(m, iphlen + sizeof(u_int32_t), xd->edx_ivlen, (caddr_t) iv); + + *mp = m; + + /* Update the counters */ + tdb->tdb_cur_packets++; + tdb->tdb_cur_bytes += rlen + padding; + + return 0; +} + +/* + * + * + * 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. + * + */ + +caddr_t +m_pad(struct mbuf *m, int n) +{ + register struct mbuf *m0, *m1; + register int len, pad; + caddr_t retval; + u_int8_t dat; + + if (n <= 0) /* no stupid arguments */ + return NULL; + + len = m->m_pkthdr.len; + pad = n; + + m0 = m; + + while (m0->m_len < len) + { + len -= m0->m_len; + m0 = m0->m_next; + } + + if (m0->m_len != len) + { +#ifdef ENCDEBUG + if (encdebug) + printf("m_pad(): length mismatch (should be %d instead of %d)\n", + m->m_pkthdr.len, m->m_pkthdr.len + m0->m_len - len); +#endif /* ENCDEBUG */ + m_freem(m); + return NULL; + } + + if ((m0->m_flags & M_EXT) || + (m0->m_data + m0->m_len + pad >= &(m0->m_dat[MLEN]))) + { + /* + * Add an mbuf to the chain + */ + + MGET(m1, M_DONTWAIT, MT_DATA); + if (m1 == 0) + { + m_freem(m0); +#ifdef ENCDEBUG + if (encdebug) + printf("m_pad(): cannot append\n"); +#endif /* ENCDEBUG */ + return NULL; + } + + m0->m_next = m1; + m0 = m1; + m0->m_len = 0; + } + + retval = m0->m_data + m0->m_len; + m0->m_len += pad; + m->m_pkthdr.len += pad; + + for (len = 0; len < n; len++) + { + get_random_bytes((void *) &dat, sizeof(u_int8_t)); + retval[len] = len + dat; + } + + return retval; +} diff --git a/sys/netinet/ip_ip4.c b/sys/netinet/ip_ip4.c index ee878138a39..6416a159f92 100644 --- a/sys/netinet/ip_ip4.c +++ b/sys/netinet/ip_ip4.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ip4.c,v 1.10 1997/07/01 22:12:49 provos Exp $ */ +/* $OpenBSD: ip_ip4.c,v 1.11 1997/07/11 23:37:58 provos Exp $ */ /* * The author of this code is John Ioannidis, ji@tla.org, @@ -59,6 +59,7 @@ #include <netinet/ip_ipsp.h> #include <netinet/ip_ip4.h> #include <dev/rndvar.h> +#include <sys/syslog.h> void ip4_input __P((struct mbuf *, int)); @@ -81,10 +82,15 @@ ip4_input(register struct mbuf *m, int iphlen) /* * Strip IP options, if any. */ - if (iphlen > sizeof (struct ip)) + if (iphlen > sizeof(struct ip)) { - ip_stripoptions(m, (struct mbuf *)0); - iphlen = sizeof (struct ip); +#ifdef ENCDEBUG + if (encdebug) + printf("ip4_input(): stripping options\n"); +#endif /* ENCDEBUG */ + + ip_stripoptions(m, (struct mbuf *) 0); + iphlen = sizeof(struct ip); } /* @@ -97,24 +103,35 @@ ip4_input(register struct mbuf *m, int iphlen) ipo = mtod(m, struct ip *); - if (m->m_len < iphlen + sizeof (struct ip)) + if (m->m_len < iphlen + sizeof(struct ip)) { - if ((m = m_pullup(m, iphlen + sizeof (struct ip))) == 0) + if ((m = m_pullup(m, iphlen + sizeof(struct ip))) == 0) { +#ifdef ENCDEBUG + if (encdebug) + printf("ip4_input(): m_pullup() failed\n"); +#endif /* ENCDEBUG */ + ip4stat.ip4s_hdrops++; return; } + ipo = mtod(m, struct ip *); } - ipi = (struct ip *)((caddr_t)ipo + iphlen); + + ipi = (struct ip *) ((caddr_t) ipo + iphlen); /* * RFC 1853 specifies that the inner TTL should not be touched on - * decapsulation. + * decapsulation. There's no reason this comment should be here, but + * this is as good as any a position. */ if (ipi->ip_v != IPVERSION) { + log(LOG_WARNING, + "ip4_input(): wrong version %d on IP packet from %x to %x (%x->%x)", + ipi->ip_v, ipo->ip_src, ipo->ip_dst, ipi->ip_src, ipi->ip_dst); ip4stat.ip4s_notip4++; return; } @@ -145,8 +162,13 @@ ip4_input(register struct mbuf *m, int iphlen) m_freem(m); ip4stat.ip4s_qfull++; splx(s); +#ifdef ENCDEBUG + if (encdebug) + printf("ip4_input(): packet dropped because of full queue\n"); +#endif /* ENCDEBUG */ return; } + IF_ENQUEUE(ifq, m); schednetisr(NETISR_IP); splx(s); @@ -165,18 +187,24 @@ ipe4_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, ipi = mtod(m, struct ip *); ilen = ntohs(ipi->ip_len); - M_PREPEND(m, sizeof (struct ip), M_DONTWAIT); + M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); if (m == 0) - return ENOBUFS; + { +#ifdef ENCDEBUG + if (encdebug) + printf("ipe4_output(): M_PREPEND failed\n"); +#endif /* ENCDEBUG */ + return ENOBUFS; + } ipo = mtod(m, struct ip *); ipo->ip_v = IPVERSION; ipo->ip_hl = 5; ipo->ip_tos = ipi->ip_tos; - ipo->ip_len = htons(ilen + sizeof (struct ip)); - /* ipo->ip_id = htons(ip_id++); */ - get_random_bytes((void *)&(ipo->ip_id), sizeof(ipo->ip_id)); + ipo->ip_len = htons(ilen + sizeof(struct ip)); +/* ipo->ip_id = htons(ip_id++); */ + get_random_bytes((void *) &(ipo->ip_id), sizeof(ipo->ip_id)); ipo->ip_off = ipi->ip_off & ~(IP_MF | IP_OFFMASK); /* keep C and DF */ if (tdb->tdb_flags & TDBF_SAME_TTL) @@ -212,12 +240,16 @@ ipe4_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, return 0; -/* return ip_output(m, NULL, NULL, IP_ENCAPSULATED, NULL);*/ +/* return ip_output(m, NULL, NULL, IP_ENCAPSULATED, NULL); */ } int ipe4_attach() { +#ifdef ENCDEBUG + if (encdebug) + printf("ipe4_attach(): setting up\n"); +#endif /* ENCDEBUG */ return 0; } @@ -226,8 +258,8 @@ ipe4_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) { #ifdef ENCDEBUG if (encdebug) - printf("ipe4_init: setting up\n"); -#endif + printf("ipe4_init(): setting up\n"); +#endif /* ENCDEBUG */ tdbp->tdb_xform = xsp; return 0; } @@ -235,15 +267,17 @@ ipe4_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) int ipe4_zeroize(struct tdb *tdbp) { +#ifdef ENCDEBUG + if (encdebug) + printf("ipe4_zeroize(): nothing to do really...\n"); +#endif /* ENCDEBUG */ return 0; } - - void ipe4_input(struct mbuf *m, ...) { - printf("ipe4_input: should never be called\n"); + log(LOG_ALERT, "ipe4_input(): should never be called"); if (m) m_freem(m); } diff --git a/sys/netinet/ip_ipsp.c b/sys/netinet/ip_ipsp.c index 2c95904174d..c5fde0aab9a 100644 --- a/sys/netinet/ip_ipsp.c +++ b/sys/netinet/ip_ipsp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ipsp.c,v 1.12 1997/07/02 06:58:42 provos Exp $ */ +/* $OpenBSD: ip_ipsp.c,v 1.13 1997/07/11 23:37:58 provos Exp $ */ /* * The author of this code is John Ioannidis, ji@tla.org, @@ -58,13 +58,12 @@ #include <netinet/ip_esp.h> #include <dev/rndvar.h> +#include <sys/syslog.h> int tdb_init __P((struct tdb *, struct mbuf *)); int ipsp_kern __P((int, char **, int)); -#ifdef ENCDEBUG -int encdebug = 1; -#endif +int encdebug = 0; /* * This is the proper place to define the various encapsulation transforms. @@ -75,40 +74,25 @@ struct xformsw xformsw[] = { ipe4_attach, ipe4_init, ipe4_zeroize, (struct mbuf * (*)(struct mbuf *, struct tdb *))ipe4_input, ipe4_output, }, - { XF_AHMD5, XFT_AUTH, "Keyed MD5 Authentication", - ahmd5_attach, ahmd5_init, ahmd5_zeroize, - ahmd5_input, ahmd5_output, }, - { XF_AHSHA1, XFT_AUTH, "Keyed SHA1 Authentication", - ahsha1_attach, ahsha1_init, ahsha1_zeroize, - ahsha1_input, ahsha1_output, }, - { XF_ESPDES, XFT_CONF, "DES-CBC Encryption", - espdes_attach, espdes_init, espdes_zeroize, - espdes_input, espdes_output, }, - { XF_ESP3DES, XFT_CONF, "3DES-CBC Encryption", - esp3des_attach, esp3des_init, esp3des_zeroize, - esp3des_input, esp3des_output, }, - { XF_AHHMACMD5, XFT_AUTH, "HMAC MD5 Authentication", - ahhmacmd5_attach, ahhmacmd5_init, ahhmacmd5_zeroize, - ahhmacmd5_input, ahhmacmd5_output, }, - { XF_AHHMACSHA1, XFT_AUTH, "HMAC SHA1 Authentication", - ahhmacsha1_attach, ahhmacsha1_init, ahhmacsha1_zeroize, - ahhmacsha1_input, ahhmacsha1_output, }, - { XF_ESPDESMD5, XFT_CONF|XFT_AUTH, - "DES-CBC Encryption + MD5 Authentication", - espdesmd5_attach, espdesmd5_init, espdesmd5_zeroize, - espdesmd5_input, espdesmd5_output, }, - { XF_ESP3DESMD5, XFT_CONF|XFT_AUTH, - "3DES-CBC Encryption + MD5 Authentication", - esp3desmd5_attach, esp3desmd5_init, esp3desmd5_zeroize, - esp3desmd5_input, esp3desmd5_output, }, + { XF_OLD_AH, XFT_AUTH, "Keyed Authentication, RFC 1828/1852", + ah_old_attach, ah_old_init, ah_old_zeroize, + ah_old_input, ah_old_output, }, + { XF_OLD_ESP, XFT_CONF, "Simple Encryption, RFC 1829/1851", + esp_old_attach, esp_old_init, esp_old_zeroize, + esp_old_input, esp_old_output, }, + { XF_NEW_AH, XFT_AUTH, "HMAC Authentication", + ah_new_attach, ah_new_init, ah_new_zeroize, + ah_new_input, ah_new_output, }, + { XF_NEW_ESP, XFT_CONF|XFT_AUTH, + "Encryption + Authentication + Replay Protection", + esp_new_attach, esp_new_init, esp_new_zeroize, + esp_new_input, esp_new_output, }, }; struct xformsw *xformswNXFORMSW = &xformsw[sizeof(xformsw)/sizeof(xformsw[0])]; unsigned char ipseczeroes[IPSEC_ZEROES_SIZE]; /* zeroes! */ -static char *ipspkernfs = NULL; -int ipspkernfs_dirty = 1; /* * Reserve an SPI; the SA is not valid yet though. Zero is reserved as @@ -117,7 +101,7 @@ int ipspkernfs_dirty = 1; */ u_int32_t -reserve_spi(u_int32_t tspi, struct in_addr src, int *errval) +reserve_spi(u_int32_t tspi, struct in_addr src, u_int8_t proto, int *errval) { struct tdb *tdbp; u_int32_t spi = tspi; /* Don't change */ @@ -125,10 +109,10 @@ reserve_spi(u_int32_t tspi, struct in_addr src, int *errval) while (1) { while (spi <= 255) /* Get a new SPI */ - get_random_bytes((void *)&spi, sizeof(spi)); + get_random_bytes((void *) &spi, sizeof(spi)); /* Check whether we're using this SPI already */ - if (gettdb(spi, src) != (struct tdb *) NULL) + if (gettdb(spi, src, proto) != (struct tdb *) NULL) { if (tspi != 0) /* If one was proposed, report error */ { @@ -147,10 +131,11 @@ reserve_spi(u_int32_t tspi, struct in_addr src, int *errval) (*errval) = ENOBUFS; } - bzero((caddr_t)tdbp, sizeof(*tdbp)); + bzero((caddr_t) tdbp, sizeof(*tdbp)); tdbp->tdb_spi = spi; tdbp->tdb_dst = src; + tdbp->tdb_sproto = proto; tdbp->tdb_flags |= TDBF_INVALID; puttdb(tdbp); @@ -161,46 +146,130 @@ reserve_spi(u_int32_t tspi, struct in_addr src, int *errval) /* * An IPSP SAID is really the concatenation of the SPI found in the - * packet and the destination address of the packet. When we receive - * an IPSP packet, we need to look up its tunnel descriptor block, - * based on the SPI in the packet and the destination address (which is - * really one of our addresses if we received the packet! + * packet, the destination address of the packet and the IPsec protocol. + * When we receive an IPSP packet, we need to look up its tunnel descriptor + * block, based on the SPI in the packet and the destination address (which + * is really one of our addresses if we received the packet! */ struct tdb * -gettdb(u_int32_t spi, struct in_addr dst) +gettdb(u_int32_t spi, struct in_addr dst, u_int8_t proto) { int hashval; struct tdb *tdbp; - hashval = (spi+dst.s_addr) % TDB_HASHMOD; + hashval = (spi + dst.s_addr + proto) % TDB_HASHMOD; for (tdbp = tdbh[hashval]; tdbp; tdbp = tdbp->tdb_hnext) - if ((tdbp->tdb_spi == spi) && (tdbp->tdb_dst.s_addr == dst.s_addr)) + if ((tdbp->tdb_spi == spi) && (tdbp->tdb_dst.s_addr == dst.s_addr) + && (tdbp->tdb_sproto == proto)) break; return tdbp; } +struct flow * +get_flow(void) +{ + struct flow *flow; + + MALLOC(flow, struct flow *, sizeof(struct flow), M_TDB, M_WAITOK); + if (flow == (struct flow *) NULL) + return (struct flow *) NULL; + + bzero(flow, sizeof(struct flow)); + + return flow; +} + +struct flow * +find_flow(struct in_addr src, struct in_addr srcmask, struct in_addr dst, + struct in_addr dstmask, u_int8_t proto, u_int16_t sport, + u_int16_t dport, struct tdb *tdb) +{ + struct flow *flow; + + for (flow = tdb->tdb_flow; flow; flow = flow->flow_next) + if ((src.s_addr == flow->flow_src.s_addr) && + (dst.s_addr == flow->flow_dst.s_addr) && + (srcmask.s_addr == flow->flow_srcmask.s_addr) && + (dstmask.s_addr == flow->flow_dstmask.s_addr) && + (proto == flow->flow_proto) && + (sport == flow->flow_sport) && (dport == flow->flow_dport)) + return flow; + + return (struct flow *) NULL; +} + +struct flow * +find_global_flow(struct in_addr src, struct in_addr srcmask, + struct in_addr dst, struct in_addr dstmask, + u_int8_t proto, u_int16_t sport, u_int16_t dport) +{ + struct flow *flow; + struct tdb *tdb; + int i; + + for (i = 0; i < TDB_HASHMOD; i++) + for (tdb = tdbh[i]; tdb; tdb = tdb->tdb_hnext) + if ((flow = find_flow(src, srcmask, dst, dstmask, proto, sport, + dport, tdb)) != (struct flow *) NULL) + return flow; + + return (struct flow *) NULL; +} + void puttdb(struct tdb *tdbp) { int hashval; - hashval = ((tdbp->tdb_spi + tdbp->tdb_dst.s_addr) % TDB_HASHMOD); + hashval = ((tdbp->tdb_sproto + tdbp->tdb_spi + tdbp->tdb_dst.s_addr) + % TDB_HASHMOD); tdbp->tdb_hnext = tdbh[hashval]; tdbh[hashval] = tdbp; +} + +void +put_flow(struct flow *flow, struct tdb *tdb) +{ + flow->flow_next = tdb->tdb_flow; + flow->flow_prev = (struct flow *) NULL; + + flow->flow_sa = tdb; + + if (flow->flow_next) + flow->flow_next->flow_prev = flow; +} + +void +delete_flow(struct flow *flow, struct tdb *tdb) +{ + if (tdb->tdb_flow == flow) + { + tdb->tdb_flow = flow->flow_next; + if (tdb->tdb_flow) + tdb->tdb_flow->flow_prev = (struct flow *) NULL; + } + else + { + flow->flow_prev->flow_next = flow->flow_next; + if (flow->flow_next) + flow->flow_next->flow_prev = flow->flow_prev; + } - ipspkernfs_dirty = 1; + FREE(flow, M_TDB); } int tdb_delete(struct tdb *tdbp, int delchain) { struct tdb *tdbpp; + struct flow *flow; int hashval; - hashval = ((tdbp->tdb_spi + tdbp->tdb_dst.s_addr) % TDB_HASHMOD); + hashval = ((tdbp->tdb_sproto + tdbp->tdb_spi + tdbp->tdb_dst.s_addr) + % TDB_HASHMOD); if (tdbh[hashval] == tdbp) { @@ -217,14 +286,21 @@ tdb_delete(struct tdb *tdbp, int delchain) if (tdbp != tdbpp) return EINVAL; /* Should never happen */ - - ipspkernfs_dirty = 1; + + /* If there was something before us in the chain, make it point nowhere */ + if (tdbp->tdb_inext) + tdbp->tdb_inext->tdb_onext = NULL; + tdbpp = tdbp->tdb_onext; if (tdbp->tdb_xform) (*(tdbp->tdb_xform->xf_zeroize))(tdbp); + for (flow = tdbp->tdb_flow; flow; flow = tdbp->tdb_flow) + delete_flow(flow, tdbp); + FREE(tdbp, M_TDB); + if (delchain && tdbpp) return tdb_delete(tdbpp, delchain); else @@ -245,15 +321,12 @@ tdb_init(struct tdb *tdbp, struct mbuf *m) if (xsp->xf_type == alg) return (*(xsp->xf_init))(tdbp, xsp, m); -#ifdef ENCDEBUG - if (encdebug) - printf("tdbinit: no alg %d for spi %x, addr %x\n", alg, tdbp->tdb_spi, - ntohl(tdbp->tdb_dst.s_addr)); -#endif + log(LOG_ERR, "tdb_init(): no alg %d for spi %08x, addr %x, proto %d", alg, + tdbp->tdb_spi, tdbp->tdb_dst.s_addr, tdbp->tdb_sproto); /* Record establishment time */ tdbp->tdb_established = time.tv_sec; - + m_freem(m); return EINVAL; } @@ -264,63 +337,5 @@ tdb_init(struct tdb *tdbp, struct mbuf *m) int ipsp_kern(int off, char **bufp, int len) { - struct tdb *tdbp; - int i, k; - char *b; - - if (off != 0) - return 0; - - if ((!ipspkernfs_dirty) && (ipspkernfs)) - { - *bufp = ipspkernfs; - return strlen(ipspkernfs); - } - else - ipspkernfs_dirty = 0; - - if (ipspkernfs) - { - FREE(ipspkernfs, M_XDATA); - ipspkernfs = NULL; - } - - for (i = 0, k = 0; i < TDB_HASHMOD; i++) - for (tdbp = tdbh[i]; tdbp != (struct tdb *) NULL; tdbp = tdbp->tdb_hnext) - { - /* Being paranoid to avoid buffer overflows */ - - if (tdbp->tdb_xform) - k += 126 + strlen(tdbp->tdb_xform->xf_name); - else - k += 60; - } - - if (k == 0) - return 0; - - MALLOC(ipspkernfs, char *, k + 1, M_XDATA, M_DONTWAIT); - if (!ipspkernfs) - return 0; - - for (i = 0, k = 0; i < TDB_HASHMOD; i++) - for (tdbp = tdbh[i]; tdbp != (struct tdb *) NULL; tdbp = tdbp->tdb_hnext) - { - b = (char *)&(tdbp->tdb_dst.s_addr); - if (!tdbp->tdb_xform) - k += sprintf(ipspkernfs + k, "SPI=%x, destination=%d.%d.%d.%d\n", - tdbp->tdb_spi, ((int)b[0] & 0xff), ((int)b[1] & 0xff), ((int)b[2] & 0xff), ((int)b[3] & 0xff)); - else - k += sprintf(ipspkernfs + k, - "SPI=%x, destination=%d.%d.%d.%d\n algorithm=%d (%s)\n next SPI=%x, previous SPI=%x\n", - ntohl(tdbp->tdb_spi), ((int)b[0] & 0xff), ((int)b[1] & 0xff), - ((int)b[2] & 0xff), ((int)b[3] & 0xff), - tdbp->tdb_xform->xf_type, tdbp->tdb_xform->xf_name, - (tdbp->tdb_onext ? ntohl(tdbp->tdb_onext->tdb_spi) : 0), - (tdbp->tdb_inext ? ntohl(tdbp->tdb_inext->tdb_spi) : 0)); - } - - ipspkernfs[k] = '\0'; - *bufp = ipspkernfs; - return strlen(ipspkernfs); + return 0; } diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h index e78c964f2f4..56cbb2b2701 100644 --- a/sys/netinet/ip_ipsp.h +++ b/sys/netinet/ip_ipsp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ipsp.h,v 1.10 1997/07/02 06:58:43 provos Exp $ */ +/* $OpenBSD: ip_ipsp.h,v 1.11 1997/07/11 23:37:59 provos Exp $ */ /* * The author of this code is John Ioannidis, ji@tla.org, @@ -27,6 +27,21 @@ * IPSP global definitions. */ +struct flow +{ + struct flow *flow_next; /* Next in flow chain */ + struct flow *flow_prev; /* Previous in flow chain */ + struct tdb *flow_sa; /* Pointer to the SA */ + struct in_addr flow_src; /* Source address */ + struct in_addr flow_srcmask; /* Source netmask */ + struct in_addr flow_dst; /* Destination address */ + struct in_addr flow_dstmask; /* Destination netmask */ + u_int16_t flow_sport; /* Source port, if applicable */ + u_int16_t flow_dport; /* Destination port, if applicable */ + u_int8_t flow_proto; /* Transport protocol, if applicable */ + u_int8_t foo[3]; /* Alignment */ +}; + struct tdb /* tunnel descriptor block */ { struct tdb *tdb_hnext; /* next in hash chain */ @@ -73,12 +88,10 @@ struct tdb /* tunnel descriptor block */ * of outter IP header if we're doing * tunneling */ caddr_t tdb_xdata; /* transformation data (opaque) */ - u_int16_t tdb_sport; /* Source port, if applicable */ - u_int16_t tdb_dport; /* Destination port, if applicable */ - + struct flow *tdb_flow; /* Which flows use this SA */ u_int8_t tdb_ttl; /* TTL used in tunneling */ - u_int8_t tdb_proto; /* Protocol carried */ - u_int16_t tdb_foo; /* alignment */ + u_int8_t tdb_sproto; /* IPsec protocol */ + u_int8_t tdb_foo[2]; /* Alignment */ }; #define TDB_HASHMOD 257 @@ -96,16 +109,18 @@ struct xformsw }; #define XF_IP4 1 /* IP inside IP */ -#define XF_AHMD5 2 /* AH MD5 */ -#define XF_AHSHA1 3 /* AH SHA */ -#define XF_ESPDES 4 /* ESP DES-CBC */ -#define XF_ESP3DES 5 /* ESP DES3-CBC */ -#define XF_AHHMACMD5 6 /* AH-HMAC-MD5 with opt replay prot */ -#define XF_AHHMACSHA1 7 /* AH-HMAC-SHA1 with opt replay prot */ -#define XF_ESPDESMD5 8 /* ESP DES-CBC + MD5 */ -#define XF_ESP3DESMD5 9 /* ESP 3DES-CBC + MD5 */ -#define XF_NEWESP 10 /* The new ESP transforms */ -#define XF_NEWAH 11 /* The new AH transforms */ +#define XF_OLD_AH 2 /* RFCs 1828 & 1852 */ +#define XF_OLD_ESP 3 /* RFCs 1829 & 1851 */ +#define XF_NEW_AH 4 /* AH HMAC 96bits */ +#define XF_NEW_ESP 5 /* ESP + auth 96bits + replay counter */ + +/* Supported key hash algorithms */ +#define ALG_AUTH_MD5 1 +#define ALG_AUTH_SHA1 2 + +/* Supported encryption algorithms */ +#define ALG_ENC_DES 1 +#define ALG_ENC_3DES 2 #define XFT_AUTH 0x0001 #define XFT_CONF 0x0100 @@ -136,55 +151,84 @@ htonq(u_int64_t q) extern unsigned char ipseczeroes[]; +/* + * Names for IPsec sysctl objects + */ +#define IPSECCTL_ENCDEBUG 1 /* turn debugging on/off */ +#define IPSECCTL_MAXID 2 + +#define IPSECCTL_NAMES {\ + { 0, 0 }, \ + { "encdebug", CTLTYPE_INT }, \ +} + #ifdef _KERNEL -#undef ENCDEBUG extern int encdebug; struct tdb *tdbh[TDB_HASHMOD]; extern struct xformsw xformsw[], *xformswNXFORMSW; -extern u_int32_t reserve_spi(u_int32_t, struct in_addr, int *); -extern struct tdb *gettdb(u_int32_t, struct in_addr); +/* TDB management routines */ +extern u_int32_t reserve_spi(u_int32_t, struct in_addr, u_int8_t, int *); +extern struct tdb *gettdb(u_int32_t, struct in_addr, u_int8_t); extern void puttdb(struct tdb *); extern int tdb_delete(struct tdb *, int); -extern int ipe4_attach(void), ipe4_init(struct tdb *, struct xformsw *, struct mbuf *), ipe4_zeroize(struct tdb *); -extern int ipe4_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, struct mbuf **); +/* Flow management routines */ +extern struct flow *get_flow(void); +extern void put_flow(struct flow *, struct tdb *); +extern void delete_flow(struct flow *, struct tdb *); +extern struct flow *find_flow(struct in_addr, struct in_addr, struct in_addr, + struct in_addr, u_int8_t, u_int16_t, u_int16_t, + struct tdb *); +extern struct flow *find_global_flow(struct in_addr, struct in_addr, + struct in_addr, struct in_addr, u_int8_t, + u_int16_t, u_int16_t); + +/* XF_IP4 */ +extern int ipe4_attach(void); +extern int ipe4_init(struct tdb *, struct xformsw *, struct mbuf *); +extern int ipe4_zeroize(struct tdb *); +extern int ipe4_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, + struct mbuf **); extern void ipe4_input __P((struct mbuf *, ...)); -extern int ahmd5_attach(void), ahmd5_init(struct tdb *, struct xformsw *, struct mbuf *), ahmd5_zeroize(struct tdb *); -extern int ahmd5_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, struct mbuf **); -extern struct mbuf *ahmd5_input(struct mbuf *, struct tdb *); - -extern int ahsha1_attach(void), ahsha1_init(struct tdb *, struct xformsw *, struct mbuf *), ahsha1_zeroize(struct tdb *); -extern int ahsha1_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, struct mbuf **); -extern struct mbuf *ahsha1_input(struct mbuf *, struct tdb *); - -extern int ahhmacmd5_attach(void), ahhmacmd5_init(struct tdb *, struct xformsw *, struct mbuf *), ahhmacmd5_zeroize(struct tdb *); -extern int ahhmacmd5_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, struct mbuf **); -extern struct mbuf *ahhmacmd5_input(struct mbuf *, struct tdb *); - -extern int ahhmacsha1_attach(void), ahhmacsha1_init(struct tdb *, struct xformsw *, struct mbuf *), ahhmacsha1_zeroize(struct tdb *); -extern int ahhmacsha1_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, struct mbuf **); -extern struct mbuf *ahhmacsha1_input(struct mbuf *, struct tdb *); - -extern int espdes_attach(void), espdes_init(struct tdb *, struct xformsw *, struct mbuf *), espdes_zeroize(struct tdb *); -extern int espdes_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, struct mbuf **); -extern struct mbuf *espdes_input(struct mbuf *, struct tdb *); - -extern int esp3des_attach(void), esp3des_init(struct tdb *, struct xformsw *, struct mbuf *), esp3des_zeroize(struct tdb *); -extern int esp3des_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, struct mbuf **); -extern struct mbuf *esp3des_input(struct mbuf *, struct tdb *); - -extern int espdesmd5_attach(void), espdesmd5_init(struct tdb *, struct xformsw *, struct mbuf *), espdesmd5_zeroize(struct tdb *); -extern int espdesmd5_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, struct mbuf **); -extern struct mbuf *espdesmd5_input(struct mbuf *, struct tdb *); - -extern int esp3desmd5_attach(void), esp3desmd5_init(struct tdb *, struct xformsw *, struct mbuf *), esp3desmd5_zeroize(struct tdb *); -extern int esp3desmd5_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, struct mbuf **); -extern struct mbuf *esp3desmd5_input(struct mbuf *, struct tdb *); - +/* XF_OLD_AH */ +extern int ah_old_attach(void); +extern int ah_old_init(struct tdb *, struct xformsw *, struct mbuf *); +extern int ah_old_zeroize(struct tdb *); +extern int ah_old_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, + struct mbuf **); +extern struct mbuf *ah_old_input(struct mbuf *, struct tdb *); + +/* XF_NEW_AH */ +extern int ah_new_attach(void); +extern int ah_new_init(struct tdb *, struct xformsw *, struct mbuf *); +extern int ah_new_zeroize(struct tdb *); +extern int ah_new_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, + struct mbuf **); +extern struct mbuf *ah_new_input(struct mbuf *, struct tdb *); + +/* XF_OLD_ESP */ +extern int esp_old_attach(void); +extern int esp_old_init(struct tdb *, struct xformsw *, struct mbuf *); +extern int esp_old_zeroize(struct tdb *); +extern int esp_old_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, + struct mbuf **); +extern struct mbuf *esp_old_input(struct mbuf *, struct tdb *); + +/* XF_NEW_ESP */ +extern int esp_new_attach(void); +extern int esp_new_init(struct tdb *, struct xformsw *, struct mbuf *); +extern int esp_new_zeroize(struct tdb *); +extern int esp_new_output(struct mbuf *, struct sockaddr_encap *, struct tdb *, + struct mbuf **); +extern struct mbuf *esp_new_input(struct mbuf *, struct tdb *); + +/* Padding */ extern caddr_t m_pad(struct mbuf *, int); -extern int checkreplaywindow32(u_int32_t, u_int32_t, u_int32_t *, u_int32_t, u_int32_t *); -extern int checkreplaywindow64(u_int64_t, u_int64_t *, u_int64_t, u_int64_t *); + +/* Replay window */ +extern int checkreplaywindow32(u_int32_t, u_int32_t, u_int32_t *, u_int32_t, + u_int32_t *); #endif diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index a282dd5eedb..5b722029c1e 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_output.c,v 1.15 1997/07/01 22:12:53 provos Exp $ */ +/* $OpenBSD: ip_output.c,v 1.16 1997/07/11 23:37:59 provos Exp $ */ /* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */ /* @@ -67,6 +67,7 @@ #include <netinet/ip_ipsp.h> #include <netinet/udp.h> #include <netinet/tcp.h> +#include <sys/syslog.h> #endif static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *)); @@ -153,8 +154,8 @@ ip_output(m0, va_alist) struct sockaddr_encap *dst, *gw; struct tdb *tdb; - bzero((caddr_t)re, sizeof (*re)); - dst = (struct sockaddr_encap *)&re->re_dst; + bzero((caddr_t) re, sizeof(*re)); + dst = (struct sockaddr_encap *) &re->re_dst; dst->sen_family = AF_ENCAP; dst->sen_len = SENT_IP4_LEN; dst->sen_type = SENT_IP4; @@ -174,24 +175,27 @@ ip_output(m0, va_alist) dst->sen_sport = ntohs(udp->uh_sport); dst->sen_dport = ntohs(udp->uh_dport); break; + case IPPROTO_TCP: tcp = (struct tcphdr *) (mtod(m, u_char *) + hlen); dst->sen_sport = ntohs(tcp->th_sport); dst->sen_dport = ntohs(tcp->th_dport); break; + default: dst->sen_sport = 0; dst->sen_dport = 0; } - rtalloc((struct route *)re); + + rtalloc((struct route *) re); if (re->re_rt == NULL) goto no_encap; - gw = (struct sockaddr_encap *)(re->re_rt->rt_gateway); + gw = (struct sockaddr_encap *) (re->re_rt->rt_gateway); if (gw == NULL || gw->sen_type != SENT_IPSP) { #ifdef ENCDEBUG if (encdebug) - printf("ip_output: no gw or gw data not IPSP\n"); + printf("ip_output(): no gw or gw data not IPSP\n"); #endif /* ENCDEBUG */ m_freem(m); RTFREE(re->re_rt); @@ -210,45 +214,27 @@ ip_output(m0, va_alist) * to the appropriate transformation. */ - tdb = (struct tdb *) gettdb(gw->sen_ipsp_spi, gw->sen_ipsp_dst); + tdb = (struct tdb *) gettdb(gw->sen_ipsp_spi, gw->sen_ipsp_dst, + gw->sen_ipsp_sproto); /* Fix the ip_src field if necessary */ if ((ip->ip_src.s_addr == INADDR_ANY) && tdb) ip->ip_src = tdb->tdb_src; - /* - * If we're doing IP-in-IP first, let the options be. - * Otherwise, get rid of them. - * XXX This means we don't send packets with IP options - * XXX unless they're encapsulated (and, presumably, - * XXX subsequently authenticated). - */ - if (tdb && tdb->tdb_xform) - if ((tdb->tdb_xform->xf_type != XF_IP4) || - (tdb->tdb_flags & TDBF_TUNNELING)) - if (hlen > sizeof (struct ip)) { /* XXX IPOPT */ - ip_stripoptions(m, (struct mbuf *)0); - hlen = sizeof (struct ip); - } - /* Now fix the checksum */ ip->ip_sum = in_cksum(m, hlen); #ifdef ENCDEBUG if (encdebug) - printf("ip_output: tdb=0x%x, tdb->tdb_xform=0x%x, tdb->tdb_xform->xf_output=%x\n", tdb, tdb->tdb_xform, tdb->tdb_xform->xf_output); + printf("ip_output(): tdb=%08x, tdb->tdb_xform=0x%x, tdb->tdb_xform->xf_output=%x, sproto=%x\n", tdb, tdb->tdb_xform, tdb->tdb_xform->xf_output, tdb->tdb_sproto); #endif /* ENCDEBUG */ while (tdb && tdb->tdb_xform) { m0 = NULL; /* Check if the SPI is invalid */ - if (tdb->tdb_flags & TDBF_INVALID) - { -#ifdef ENCDEBUG - if (encdebug) - printf("ip_output: attempt to use invalid SPI %08x", tdb->tdb_spi); -#endif /* ENCDEBUG */ + if (tdb->tdb_flags & TDBF_INVALID) { + log(LOG_ALERT, "ip_output(): attempt to use invalid SA %x/%08x/%x", tdb->tdb_dst, tdb->tdb_spi, tdb->tdb_sproto); m_freem(m); RTFREE(re->re_rt); return ENXIO; @@ -258,7 +244,7 @@ ip_output(m0, va_alist) if (tdb->tdb_flags & TDBF_TUNNELING) { #ifdef ENCDEBUG if (encdebug) - printf("ip_output: doing tunneling\n"); + printf("ip_output(): tunneling\n"); #endif /* ENCDEBUG */ /* Register first use */ @@ -277,8 +263,8 @@ ip_output(m0, va_alist) #ifdef ENCDEBUG if (encdebug) - printf("ip_output: calling %s\n", - tdb->tdb_xform->xf_name); + printf("ip_output(): calling %s\n", + tdb->tdb_xform->xf_name); #endif /* ENCDEBUG */ /* Register first use */ @@ -308,7 +294,8 @@ ip_output(m0, va_alist) NTOHS(ip->ip_len); NTOHS(ip->ip_off); - return ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, NULL); + return ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, + NULL); no_encap: /* This is for possible future use, don't move or delete */ diff --git a/sys/netinet/ip_sha1.h b/sys/netinet/ip_sha1.h index 64615eb0e3a..186a774f9a2 100644 --- a/sys/netinet/ip_sha1.h +++ b/sys/netinet/ip_sha1.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_sha1.h,v 1.3 1997/06/21 00:09:20 deraadt Exp $ */ +/* $OpenBSD: ip_sha1.h,v 1.4 1997/07/11 23:38:00 provos Exp $ */ /* SHA-1 in C @@ -6,6 +6,9 @@ By Steve Reid <steve@edmweb.com> 100% Public Domain */ +#ifndef _SHA1_H_ +#define _SHA1_H_ + typedef struct { u_int32_t state[5]; u_int32_t count[2]; @@ -16,3 +19,5 @@ void SHA1Transform(u_int32_t state[5], unsigned char buffer[64]); void SHA1Init(SHA1_CTX* context); void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len); void SHA1Final(unsigned char digest[20], SHA1_CTX* context); + +#endif /* _SHA1_H_ */ |