diff options
Diffstat (limited to 'sys/netinet/ip_ah_old.c')
-rw-r--r-- | sys/netinet/ip_ah_old.c | 778 |
1 files changed, 778 insertions, 0 deletions
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; +} |