summaryrefslogtreecommitdiff
path: root/sys/netinet/ip_ahhmacmd5.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/ip_ahhmacmd5.c')
-rw-r--r--sys/netinet/ip_ahhmacmd5.c459
1 files changed, 459 insertions, 0 deletions
diff --git a/sys/netinet/ip_ahhmacmd5.c b/sys/netinet/ip_ahhmacmd5.c
new file mode 100644
index 00000000000..008fef0e575
--- /dev/null
+++ b/sys/netinet/ip_ahhmacmd5.c
@@ -0,0 +1,459 @@
+/*
+ * 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-ah-hmac-md5-04.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 <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>
+
+/*
+ * ahhmacmd5_attach() is called from the transformation initialization code.
+ * It just returns.
+ */
+
+int
+ahhmacmd5_attach()
+{
+ return 0;
+}
+
+/*
+ * ahhmacmd5_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
+ahhmacmd5_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m)
+{
+ struct ahhmacmd5_xdata *xd;
+ struct ahhmacmd5_xencap txd;
+ struct encap_msghdr *em;
+ int len;
+
+ tdbp->tdb_xform = xsp;
+
+ MALLOC(tdbp->tdb_xdata, caddr_t, sizeof (struct ahhmacmd5_xdata),
+ M_XDATA, M_WAITOK);
+ if (tdbp->tdb_xdata == NULL)
+ return ENOBUFS;
+ bzero(tdbp->tdb_xdata, sizeof (struct ahhmacmd5_xdata));
+ bzero(&txd, sizeof(struct ahhmacmd5_xencap));
+ xd = (struct ahhmacmd5_xdata *)tdbp->tdb_xdata;
+
+ em = mtod(m, struct encap_msghdr *);
+ if (em->em_msglen - EMT_SETSPI_FLEN > sizeof (struct ahhmacmd5_xencap))
+ {
+ free((caddr_t)tdbp->tdb_xdata, M_XDATA);
+ tdbp->tdb_xdata = NULL;
+ return EINVAL;
+ }
+
+ m_copydata(m, EMT_SETSPI_FLEN, em->em_msglen - EMT_SETSPI_FLEN, (caddr_t)&txd);
+
+ xd->amx_rpl = 1;
+ xd->amx_alen = txd.amx_alen;
+ xd->amx_bitmap = 0;
+ xd->amx_wnd = txd.amx_wnd;
+
+ realMD5Init(&(xd->amx_ictx));
+ realMD5Init(&(xd->amx_octx));
+
+ for (len = 0; len < AHHMACMD5_KMAX; len++)
+ txd.amx_key[len] ^= HMACMD5_IPAD_VAL;
+
+ MD5Update(&(xd->amx_ictx), txd.amx_key, AHHMACMD5_KMAX);
+
+ for (len = 0; len < AHHMACMD5_KMAX; len++)
+ txd.amx_key[len] ^= (HMACMD5_IPAD_VAL ^ HMACMD5_OPAD_VAL);
+
+ MD5Update(&(xd->amx_octx), txd.amx_key, AHHMACMD5_KMAX);
+ bzero(&txd, sizeof(struct ahhmacmd5_xencap));
+ bzero(ipseczeroes, IPSEC_ZEROES_SIZE); /* paranoid */
+
+ return 0;
+}
+
+/*
+ * Free memory
+ */
+
+int
+ahhmacmd5_zeroize(struct tdb *tdbp)
+{
+ FREE(tdbp->tdb_xdata, M_XDATA);
+ return 0;
+}
+
+/*
+ * ahhmacmd5_input() gets called to verify that an input packet
+ * passes authentication.
+ */
+
+extern struct ifnet loif;
+
+struct mbuf *
+ahhmacmd5_input(struct mbuf *m, struct tdb *tdb)
+{
+ struct ahhmacmd5_xdata *xd;
+ struct ip *ip, ipo;
+ struct ah *ah;
+ struct ahhmacmd5 aho, *ahp;
+ struct ifnet *rcvif;
+ int ohlen, len, count, off, ado, errc;
+ u_int64_t btsx;
+ struct mbuf *m0;
+ MD5_CTX ctx;
+
+ xd = (struct ahhmacmd5_xdata *)tdb->tdb_xdata;
+ ohlen = sizeof (struct ip) + AH_FLENGTH + xd->amx_alen;
+ if (xd->amx_wnd >= 0)
+ ohlen += HMACMD5_RPLENGTH;
+
+ rcvif = m->m_pkthdr.rcvif;
+ if (rcvif == NULL)
+ {
+#ifdef ENCDEBUG
+ if (encdebug)
+ printf("ahhmacmd5_input: receive interface is NULL!!!\n");
+#endif
+ rcvif = &loif;
+ }
+
+ if (m->m_len < ohlen)
+ {
+ if ((m = m_pullup(m, ohlen)) == NULL)
+ {
+ ahstat.ahs_hdrops++;
+ return NULL;
+ }
+ }
+
+ ip = mtod(m, struct ip *);
+ ah = (struct ah *)(ip + 1);
+ ahp = (struct ahhmacmd5 *)ah;
+
+ if (xd->amx_wnd >= 0)
+ ado = HMACMD5_RPLENGTH;
+ else
+ ado = 0;
+
+ if (ah->ah_hl != xd->amx_alen + ado)
+ {
+#ifdef ENCDEBUG
+ if (encdebug)
+ printf("ahhmacmd5_input: bad authenticator length\n");
+#endif
+ ahstat.ahs_badauthl++;
+ m_freem(m);
+ return NULL;
+ }
+
+ ipo = *ip;
+ ipo.ip_tos = 0;
+ ipo.ip_len += sizeof (struct ip); /* 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;
+
+ ctx = xd->amx_ictx;
+ MD5Update(&ctx, (unsigned char *)&ipo, sizeof (struct ip));
+ if (xd->amx_wnd >= 0)
+ MD5Update(&ctx, (unsigned char *)ahp, AH_FLENGTH + HMACMD5_RPLENGTH);
+ else
+ MD5Update(&ctx, (unsigned char *)ahp, AH_FLENGTH);
+ MD5Update(&ctx, ipseczeroes, xd->amx_alen);
+
+ /*
+ * Code shamelessly stolen from m_copydata
+ */
+ off = ohlen;
+ len = m->m_pkthdr.len - off;
+ m0 = m;
+
+ while (off > 0)
+ {
+ if (m0 == 0)
+ panic("ahhmacmd5_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("ahhmacmd5_input: m_copydata (copy)");
+ count = min(m0->m_len - off, len);
+ MD5Update(&ctx, mtod(m0, unsigned char *) + off, count);
+ len -= count;
+ off = 0;
+ m0 = m0->m_next;
+ }
+
+ MD5Final((unsigned char *)(&(aho.ah_data[0])), &ctx);
+ ctx = xd->amx_octx;
+ MD5Update(&ctx, (unsigned char *)(&(aho.ah_data[0])), HMACMD5_HASHLEN);
+ MD5Final((unsigned char *)(&(aho.ah_data[0])), &ctx);
+
+ if (bcmp(aho.ah_data, ah->ah_data + ado, xd->amx_alen))
+ {
+#ifdef ENCDEBUG
+ if (encdebug)
+ printf("ahhmacmd5_input: bad auth\n"); /* XXX */
+#endif
+ ahstat.ahs_badauth++;
+ m_freem(m);
+ return NULL;
+ }
+
+ if (xd->amx_wnd >= 0)
+ {
+ btsx = ntohq(ahp->ah_rpl);
+ if ((errc = checkreplaywindow64(btsx, &(xd->amx_rpl),
+ xd->amx_wnd, &(xd->amx_bitmap)))
+ != 0)
+ {
+ switch(errc)
+ {
+ case 1:
+#ifdef ENCDEBUG
+ printf("ahhmacmd5_input: replay counter wrapped\n");
+#endif
+ ahstat.ahs_wrap++;
+ break;
+ case 2:
+#ifdef ENCDEBUG
+ printf("ahhmacmd5_input: received old packet\n");
+#endif
+ ahstat.ahs_replay++;
+ break;
+ case 3:
+#ifdef ENCDEBUG
+ printf("ahhmacmd5_input: packet already received\n");
+#endif
+ ahstat.ahs_replay++;
+ break;
+ }
+ m_freem(m);
+ return NULL;
+ }
+ }
+
+ ipo = *ip;
+ ipo.ip_p = ah->ah_nh;
+
+ m->m_len -= (ohlen - sizeof(struct ip));
+ m->m_data += (ohlen - sizeof(struct ip));
+ m->m_pkthdr.len -= (ohlen - sizeof(struct ip));
+ m->m_pkthdr.rcvif = rcvif; /* this should not be necessary */
+
+ ip = mtod(m, struct ip *);
+ *ip = ipo;
+ ip->ip_len = htons(ip->ip_len - ohlen + 2 * sizeof (struct ip));
+ HTONS(ip->ip_id);
+ HTONS(ip->ip_off);
+ ip->ip_sum = 0;
+ ip->ip_sum = in_cksum(m, sizeof (struct ip));
+
+ return m;
+}
+
+
+#define AHXPORT
+
+int
+ahhmacmd5_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, struct mbuf **mp)
+{
+ struct ahhmacmd5_xdata *xd;
+ struct ip *ip, ipo;
+ struct ah *ah;
+ struct ahhmacmd5 *ahp, aho;
+ register int len, off, count;
+ register struct mbuf *m0;
+ MD5_CTX ctx;
+ int ilen, ohlen;
+
+
+ m = m_pullup(m, sizeof (struct ip));
+ if (m == NULL)
+ return ENOBUFS;
+
+ ip = mtod(m, struct ip *);
+
+ xd = (struct ahhmacmd5_xdata *)tdb->tdb_xdata;
+
+ ilen = ntohs(ip->ip_len);
+
+#ifdef AHXPORT
+ ohlen = AH_FLENGTH + xd->amx_alen;
+#else
+ ohlen = sizeof (struct ip) + AH_FLENGTH + xd->amx_alen;
+#endif
+ if (xd->amx_wnd >= 0)
+ ohlen += HMACMD5_RPLENGTH;
+
+ ipo.ip_v = IPVERSION;
+ ipo.ip_hl = 5;
+ 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;
+#ifdef AHXPORT
+ ipo.ip_src = ip->ip_src;
+ ipo.ip_dst = ip->ip_dst;
+ aho.ah_nh = ip->ip_p;
+#else
+ ipo.ip_src = gw->sen_ipsp_src;
+ ipo.ip_dst = gw->sen_ipsp_dst;
+ aho.ah_nh = IPPROTO_IP4;
+#endif
+ aho.ah_hl = (xd->amx_alen >> 2);
+ if (xd->amx_wnd >= 0)
+ aho.ah_hl += (HMACMD5_RPLENGTH / sizeof(u_int32_t));
+ aho.ah_rv = 0;
+ aho.ah_spi = tdb->tdb_spi;
+
+ if (xd->amx_wnd >= 0)
+ {
+ if (xd->amx_rpl == 0)
+ {
+#ifdef ENCDEBUG
+ printf("ahhmacmd5_output: key should have changed long ago\n");
+#endif
+ ahstat.ahs_wrap++;
+ return NULL;
+ }
+ }
+
+ aho.ah_rpl = htonq(xd->amx_rpl++);
+
+ ctx = xd->amx_ictx;
+ MD5Update(&ctx, (unsigned char *)&ipo, sizeof (struct ip));
+ if (xd->amx_wnd >= 0)
+ MD5Update(&ctx, (unsigned char *)&aho, AH_FLENGTH + HMACMD5_RPLENGTH);
+ else
+ MD5Update(&ctx, (unsigned char *)&aho, AH_FLENGTH);
+ MD5Update(&ctx, ipseczeroes, xd->amx_alen);
+
+#ifdef AHXPORT
+ off = sizeof (struct ip);
+#else
+ off = 0;
+#endif
+
+ /*
+ * Code shamelessly stolen from m_copydata
+ */
+ len = m->m_pkthdr.len - off;
+
+ m0 = m;
+
+ while (len > 0)
+ {
+ if (m0 == 0)
+ panic("ahhmacmd5_output: m_copydata");
+ count = min(m0->m_len - off, len);
+ MD5Update(&ctx, mtod(m0, unsigned char *) + off, count);
+
+ len -= count;
+ off = 0;
+ m0 = m0->m_next;
+ }
+
+ MD5Final((unsigned char *)(&(aho.ah_data[0])), &ctx);
+ ctx = xd->amx_octx;
+ MD5Update(&ctx, (unsigned char *)(&(aho.ah_data[0])), HMACMD5_HASHLEN);
+ MD5Final((unsigned char *)(&(aho.ah_data[0])), &ctx);
+
+ 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)
+ return ENOBUFS;
+
+ m = m_pullup(m, ohlen + sizeof (struct ip));
+ if (m == NULL)
+ return ENOBUFS;
+
+ ip = mtod(m, struct ip *);
+ ah = (struct ah *)(ip + 1);
+ ahp = (struct ahhmacmd5 *)ah;
+ *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;
+ if (xd->amx_wnd >= 0)
+ {
+ ahp->ah_rpl = aho.ah_rpl;
+ bcopy((unsigned char *)(&(aho.ah_data[0])),
+ ahp->ah_data, xd->amx_alen);
+ }
+ else
+ bcopy((unsigned char *)(&(aho.ah_data[0])),
+ ah->ah_data, xd->amx_alen);
+
+ *mp = m;
+
+ return 0;
+}