diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1997-02-20 01:08:13 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1997-02-20 01:08:13 +0000 |
commit | 27f7e7b789913e142fd048ad0f5e9fe6fee94c27 (patch) | |
tree | 92f475f4da1f7ac2c6a9fac20881c666173e7600 | |
parent | 64c38b22e9e85f411969b697297f6dfd609dc83c (diff) |
IPSEC package by John Ioannidis and Angelos D. Keromytis. Written in
Greece. From ftp.funet.fi:/pub/unix/security/net/ip/BSDipsec.tar.gz
33 files changed, 6481 insertions, 18 deletions
diff --git a/sys/conf/files b/sys/conf/files index dbcb9628c28..f3c3d77442b 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.49 1996/12/23 02:42:23 deraadt Exp $ +# $OpenBSD: files,v 1.50 1997/02/20 01:07:37 deraadt Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -86,6 +86,7 @@ pseudo-device tun: ifnet pseudo-device bpfilter: ifnet pseudo-device strip: ifnet pseudo-device random +pseudo-device enc: ifnet # XXX machine-independent SCSI files should live somewhere here, maybe @@ -252,6 +253,8 @@ file net/raw_usrreq.c file net/route.c file net/rtsock.c file net/slcompress.c sl | ppp | strip +file net/if_enc.c inet +file net/encap.c inet file netccitt/ccitt_proto.c ccitt file netccitt/hd_debug.c hdlc file netccitt/hd_input.c hdlc @@ -294,6 +297,20 @@ file netinet/fil.c ipfilter file netinet/ip_nat.c ipfilter file netinet/ip_frag.c ipfilter file netinet/ip_state.c ipfilter +file netinet/ip_ah.c inet | ipsec +file netinet/ip_esp.c inet | ipsec +file netinet/ip_espdes.c inet | ipsec +file netinet/ip_espdesmd5.c inet | ipsec +file netinet/ip_ipsp.c inet | ipsec +file netinet/ip_ahmd5.c inet | ipsec +file netinet/ip_ip4.c inet | ipsec +file netinet/ip_ahhmacmd5.c inet | ipsec +file netinet/ip_ahhmacsha1.c inet | ipsec +file netinet/ip_sha1.c inet | ipsec +file netinet/ip_md5c.c inet | ipsec +file netinet/libdeslite/ecb_enc.c inet | ipsec +file netinet/libdeslite/set_key.c inet | ipsec +file netinet/ip_esp3desmd5.c inet | ipsec file netiso/clnp_debug.c iso file netiso/clnp_er.c iso file netiso/clnp_frag.c iso diff --git a/sys/kern/uipc_domain.c b/sys/kern/uipc_domain.c index 406a420d352..790eceafddf 100644 --- a/sys/kern/uipc_domain.c +++ b/sys/kern/uipc_domain.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_domain.c,v 1.3 1996/04/27 13:21:06 mickey Exp $ */ +/* $OpenBSD: uipc_domain.c,v 1.4 1997/02/20 01:07:26 deraadt Exp $ */ /* $NetBSD: uipc_domain.c,v 1.14 1996/02/09 19:00:44 christos Exp $ */ /* @@ -69,6 +69,9 @@ domaininit() ADDDOMAIN(route); #ifdef INET ADDDOMAIN(inet); +#ifdef IPSEC + ADDDOMAIN(encap); +#endif #endif #ifdef IPX ADDDOMAIN(ipx); diff --git a/sys/miscfs/kernfs/kernfs_vnops.c b/sys/miscfs/kernfs/kernfs_vnops.c index f77a606a13e..0a94d88248b 100644 --- a/sys/miscfs/kernfs/kernfs_vnops.c +++ b/sys/miscfs/kernfs/kernfs_vnops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kernfs_vnops.c,v 1.6 1997/01/15 03:06:28 kstailey Exp $ */ +/* $OpenBSD: kernfs_vnops.c,v 1.7 1997/02/20 01:08:12 deraadt Exp $ */ /* $NetBSD: kernfs_vnops.c,v 1.43 1996/03/16 23:52:47 christos Exp $ */ /* @@ -75,6 +75,10 @@ static int ncpu = 1; /* XXX */ extern char machine[], cpu_model[]; extern char ostype[], osrelease[]; +#ifdef IPSEC +extern int ipsp_kern __P((int, char **, int)); +#endif + struct kern_target { u_char kt_type; u_char kt_namlen; @@ -90,6 +94,9 @@ struct kern_target { #define KTT_MSGBUF 89 #define KTT_USERMEM 91 #define KTT_DOMAIN 95 +#ifdef IPSEC +#define KTT_IPSECSPI 107 +#endif u_char kt_tag; u_char kt_vtype; mode_t kt_mode; @@ -124,6 +131,9 @@ struct kern_target { { DT_REG, N("time"), 0, KTT_TIME, VREG, READ_MODE }, { DT_REG, N("usermem"), 0, KTT_USERMEM, VREG, READ_MODE }, { DT_REG, N("version"), version, KTT_STRING, VREG, READ_MODE }, +#ifdef IPSEC + { DT_REG, N("ipsec"), 0, KTT_IPSECSPI, VREG, READ_MODE }, +#endif #undef N }; static int nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]); @@ -304,7 +314,10 @@ kernfs_xread(kt, off, bufp, len) case KTT_USERMEM: sprintf(*bufp, "%u\n", physmem - cnt.v_wire_count); break; - +#ifdef IPSEC + case KTT_IPSECSPI: + return(ipsp_kern(off, bufp, len)); +#endif default: return (0); } diff --git a/sys/net/encap.c b/sys/net/encap.c new file mode 100644 index 00000000000..a8e1de64073 --- /dev/null +++ b/sys/net/encap.c @@ -0,0 +1,393 @@ +/* + * 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/raw_cb.h> +#include <machine/stdarg.h> + +#ifdef INET +#include <netinet/in.h> +#endif + +#include <net/encap.h> +#include <netinet/ip_ipsp.h> + +extern struct ifnet loif; + +extern int ipspkernfs_dirty; + +void encap_init(void); +int encap_output __P((struct mbuf *, ...)); +int encap_usrreq(struct socket *, int, struct mbuf *, struct mbuf *, struct mbuf *); + +extern int tdb_init(struct tdb *, struct mbuf *); + +extern struct domain encapdomain; + +struct sockaddr encap_dst = { 2, PF_ENCAP, }; +struct sockaddr encap_src = { 2, PF_ENCAP, }; +struct sockproto encap_proto = { PF_ENCAP, }; + +struct protosw encapsw[] = { +{ SOCK_RAW, &encapdomain, 0, PR_ATOMIC|PR_ADDR, + raw_input, encap_output, raw_ctlinput, 0, + encap_usrreq, + encap_init, 0, 0, 0, +}, +}; + +struct domain encapdomain = + { AF_ENCAP, "encapsulation", 0, 0, 0, + encapsw, &encapsw[sizeof(encapsw)/sizeof(encapsw[0])], 0, + rn_inithead, 16, sizeof(struct sockaddr_encap)}; + + +void +encap_init() +{ + struct xformsw *xsp; + + for (xsp = xformsw; xsp < xformswNXFORMSW; xsp++) + { + printf("encap_init: attaching <%s>\n", xsp->xf_name); + (*(xsp->xf_attach))(); + } +} + +/*ARGSUSED*/ +int +encap_usrreq(register struct socket *so, int req, struct mbuf *m, struct mbuf *nam, struct mbuf *control) +{ + register int error = 0; + register struct rawcb *rp = sotorawcb(so); + int s; + + if (req == PRU_ATTACH) + { + MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK); + if ((so->so_pcb = (caddr_t)rp)) + bzero(so->so_pcb, sizeof(*rp)); + + } + s = splnet(); + error = raw_usrreq(so, req, m, nam, control); + rp = sotorawcb(so); + if ((req == PRU_ATTACH) && rp) + { + /* int af = rp->rcb_proto.sp_protocol; */ + + if (error) + { + free((caddr_t)rp, M_PCB); + splx(s); + return error; + } + rp->rcb_faddr = &encap_src; + soisconnected(so); + so->so_options |= SO_USELOOPBACK; + } + splx(s); + return error; +} + +int +#ifdef __STDC__ +encap_output(struct mbuf *m, ...) +#else +encap_output(m, va_alist) + register struct mbuf *m; + va_dcl +#endif +{ +#define SENDERR(e) do { error = e; goto flush;} while (0) + struct socket *so; + int len, emlen, error = 0, nspis, i; + struct encap_msghdr *emp; + struct ifnet *ifp; + struct ifaddr *ifa; + struct sockaddr_encap *sen, *sen2; + struct sockaddr_in *sin; + struct tdb *tdbp, *tprev; + va_list ap; + + va_start(ap, m); + so = va_arg(ap, struct socket *); + va_end(ap); + + if ((m == 0) || ((m->m_len < sizeof(long)) && + (m = m_pullup(m, sizeof(long))) == 0)) + return ENOBUFS; + + if ((m->m_flags & M_PKTHDR) == 0) + panic("encap_output"); + + len = m->m_pkthdr.len; + emp = mtod(m, struct encap_msghdr *); + emlen = emp->em_msglen; + if ((len < emlen)) + SENDERR(EINVAL); + + if (m->m_len < emlen) + { + m = m_pullup(m, emlen); + if (m == NULL) + SENDERR(ENOBUFS); + + emp = mtod(m, struct encap_msghdr *); + } + + switch (emp->em_type) + { + case EMT_IFADDR: + /* + * Set the default source address for an encap interface + */ + + ifp = &(enc_softc[emp->em_ifn].enc_if); + + if ((ifp->if_addrlist.tqh_first == NULL) || + (ifp->if_addrlist.tqh_first->ifa_addr == NULL) || + (ifp->if_addrlist.tqh_first->ifa_addr->sa_family != AF_ENCAP)) + { + MALLOC(ifa, struct ifaddr *, sizeof (struct ifaddr) + 2*SENT_DEFIF_LEN, M_IFADDR, M_WAITOK); + if (ifa == NULL) + SENDERR(ENOBUFS); + bzero((caddr_t)ifa, sizeof (struct ifaddr) + 2*SENT_DEFIF_LEN); + sen = (struct sockaddr_encap *)(ifa + 1); + sen2 = (struct sockaddr_encap *)((caddr_t)sen + SENT_DEFIF_LEN); + ifa->ifa_addr = (struct sockaddr *)sen; + ifa->ifa_dstaddr = (struct sockaddr *)sen2; + ifa->ifa_ifp = ifp; + TAILQ_INSERT_HEAD(&(ifp->if_addrlist), ifa, ifa_list); + } + else + { + sen = (struct sockaddr_encap *)((&(ifp->if_addrlist))->tqh_first->ifa_addr); + sen2 = (struct sockaddr_encap *)((&(ifp->if_addrlist))->tqh_first->ifa_dstaddr); + } + + sen->sen_family = AF_ENCAP; + sen->sen_len = SENT_DEFIF_LEN; + sen->sen_type = SENT_DEFIF; + sin = (struct sockaddr_in *) &(sen->sen_dfl); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr = emp->em_ifa; + + *sen2 = *sen; + + break; + + case EMT_SETSPI: + if (emp->em_if >= nencap) + SENDERR(ENODEV); + + tdbp = gettdb(emp->em_spi, emp->em_dst); + if (tdbp == NULL) + { + MALLOC(tdbp, struct tdb *, sizeof (*tdbp), M_TDB, M_WAITOK); + if (tdbp == NULL) + SENDERR(ENOBUFS); + + bzero((caddr_t)tdbp, sizeof(*tdbp)); + + tdbp->tdb_spi = emp->em_spi; + tdbp->tdb_dst = emp->em_dst; + tdbp->tdb_rcvif = &(enc_softc[emp->em_if].enc_if); + puttdb(tdbp); + } + else + (*tdbp->tdb_xform->xf_zeroize)(tdbp); + + error = tdb_init(tdbp, m); + ipspkernfs_dirty = 1; + break; + + case EMT_DELSPI: + if (emp->em_if >= nencap) + SENDERR(ENODEV); + + tdbp = gettdb(emp->em_spi, emp->em_dst); + if (tdbp == NULL) + { + error = EINVAL; + break; + } + + if (emp->em_alg != tdbp->tdb_xform->xf_type) + { + error = EINVAL; + break; + } + + error = tdb_delete(tdbp, 0); + break; + + case EMT_DELSPICHAIN: + if (emp->em_if >= nencap) + SENDERR(ENODEV); + + tdbp = gettdb(emp->em_spi, emp->em_dst); + if (tdbp == NULL) + { + error = EINVAL; + break; + } + + if (emp->em_alg != tdbp->tdb_xform->xf_type) + { + error = EINVAL; + break; + } + + error = tdb_delete(tdbp, 1); + break; + + case EMT_GRPSPIS: + nspis = (emlen - 4) / 12; + if (nspis * 12 + 4 != emlen) + { + SENDERR(EINVAL); + break; + } + + for (i = 0; i < nspis; i++) + if ((tdbp = gettdb(emp->em_rel[i].emr_spi, emp->em_rel[i].emr_dst)) == NULL) + SENDERR(ENOENT); + else + emp->em_rel[i].emr_tdb = tdbp; + tprev = emp->em_rel[0].emr_tdb; + tprev->tdb_inext = NULL; + for (i = 1; i < nspis; i++) + { + tdbp = emp->em_rel[i].emr_tdb; + tprev->tdb_onext = tdbp; + tdbp->tdb_inext = tprev; + tprev = tdbp; + } + tprev->tdb_onext = NULL; + ipspkernfs_dirty = 1; + error = 0; + break; + + default: + SENDERR(EINVAL); + } + + return error; + + flush: + if (m) + m_freem(m); + return error; +} + +struct ifaddr * +encap_findgwifa(struct sockaddr *gw) +{ + struct sockaddr_encap *egw = (struct sockaddr_encap *)gw; + u_char *op = (u_char *)gw; + int i, j; + struct ifaddr *retval = loif.if_addrlist.tqh_first; + union + { + struct in_addr ia; + u_char io[4]; + } iao; + + switch (egw->sen_type) + { + case SENT_IPSP: + return enc_softc[egw->sen_ipsp_ifn].enc_if.if_addrlist.tqh_first; + break; + + case SENT_IP4: + /* + * Pretty-much standard options walking code. + * Repeated elsewhere as necessary + */ + + for (i = SENT_IP4_LEN; i < egw->sen_len;) + switch (op[i]) + { + case SENO_EOL: + goto opt_done; + + case SENO_NOP: + i++; + continue; + + case SENO_IFN: + if (op[i+1] != 3) + { + return NULL; + } + retval = enc_softc[op[i+2]].enc_if.if_addrlist.tqh_first; + goto opt_done; + + case SENO_IFIP4A: + if (op[i+1] != 6) /* XXX -- IPv4 address */ + { + return NULL; + } + iao.io[0] = op[i+2]; + iao.io[1] = op[i+3]; + iao.io[2] = op[i+4]; + iao.io[3] = op[i+5]; + + for (j = 0; j < nencap; j++) + { + struct ifaddr *ia = (struct ifaddr *)enc_softc[j].enc_if.if_addrlist.tqh_first; + + struct sockaddr_in *si = (struct sockaddr_in *)ia->ifa_addr; + + if ((si->sin_family == AF_INET) && (si->sin_addr.s_addr == iao.ia.s_addr)) + { + retval = ia; + goto opt_done; + } + } + i += 6; + break; + + default: + if (op[i+1] == 0) + return NULL; + i += op[i+i]; + } + opt_done: + break; + } + return retval; +} diff --git a/sys/net/encap.h b/sys/net/encap.h new file mode 100644 index 00000000000..4d105db0cd7 --- /dev/null +++ b/sys/net/encap.h @@ -0,0 +1,230 @@ +/* + * 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. + */ + +/* + * encap.h + * + * Declarations useful in the encapsulation code. + */ + +/* + * Definitions for encapsulation-related phenomena. + * + * A lot of encapsulation protocols (ipip, swipe, ip_encap, ipsp, etc.) + * select their tunnel based on the destination (and sometimes the source) + * of the packet. The encap address/protocol family provides a generic + * mechanism for specifying tunnels. + */ + +/* + * A tunnel is characterized by which source/destination address pairs + * (with netmasks) it is valid for (the "destination" as far as the + * routing code is concerned), and what the source (local) and destination + * (remote) endpoints of the tunnel, and the SPI, should be (the "gateway" + * as far as the routing code is concerned. + */ + + +struct sockaddr_encap +{ + u_int8_t sen_len; /* length */ + u_int8_t sen_family; /* AF_ENCAP */ + u_int16_t sen_type; /* see SENT_* */ + union + { + u_int8_t Data[16]; /* other stuff mapped here */ + struct sockaddr Dfl; /* SENT_DEFIF */ + struct /* SENT_SA */ + { + struct sockaddr Src; + struct sockaddr Dst; + } Sa; +#ifdef INET + struct /* SENT_SAIN */ + { + struct sockaddr_in Src; + struct sockaddr_in Dst; + } Sin; + struct /* SENT_IP4 */ + { + struct in_addr Src; + struct in_addr Dst; + u_int16_t Sport; + u_int16_t Dport; + u_int8_t Proto; + u_int8_t Filler[3]; + } Sip4; + struct /* SENT_IPSP */ + { + struct in_addr Src; + struct in_addr Dst; + u_int32_t Spi; + u_int8_t Ifn; + u_int8_t Filler[3]; + } Sipsp; +#endif + } Sen; +}; + +#define sen_data Sen.Data +#define sen_dfl Sen.Dfl +#define sen_sa_src Sen.Sa.Src +#define sen_sa_dst Sen.Sa.Dst +#ifdef INET +#define sen_sin_src Sen.Sin.Src +#define sen_sin_dst Sen.Sin.Dst +#define sen_ip_src Sen.Sip4.Src +#define sen_ip_dst Sen.Sip4.Dst +#define sen_ipsp_src Sen.Sipsp.Src +#define sen_ipsp_dst Sen.Sipsp.Dst +#define sen_ipsp_spi Sen.Sipsp.Spi +#define sen_ipsp_ifn Sen.Sipsp.Ifn +#define sen_proto Sen.Sip4.Proto +#define sen_sport Sen.Sip4.Sport +#define sen_dport Sen.Sip4.Dport +#endif + +/* + * The "type" is really part of the address as far as the routing + * system is concerned. By using only one bit in the type field + * for each type, we sort-of make sure that different types of + * encapsulation addresses won't be matched against the wrong type. + * + */ + +#define SENT_DEFIF 0x0001 /* data is a default sockaddr for if */ +#define SENT_SA 0x0002 /* data is two struct sockaddr */ +#define SENT_SAIN 0x0004 /* data is two struct sockaddr_in */ +#define SENT_IP4 0x0008 /* data is two struct in_addr */ +#define SENT_IPSP 0x0010 /* data as in IP4 plus SPI and if# */ + +/* + * SENT_HDRLEN is the length of the "header" + * SENT_*_LEN are the lengths of various forms of sen_data + * SENT_*_OFF are the offsets in the sen_data array of various fields + */ + +#define SENT_HDRLEN (2*sizeof(u_int8_t)+sizeof(u_int16_t)) + +#define SENT_DEFIF_LEN (SENT_HDRLEN + sizeof (struct sockaddr_in)) + +#define SENT_IP4_SRCOFF (0) +#define SENT_IP4_DSTOFF (sizeof (struct in_addr)) +#define SENT_IP4_OPTOFF (2*sizeof(struct in_addr)+2*sizeof(u_int16_t)+sizeof(u_int8_t)+3*sizeof(u_int8_t)) + +#define SENT_IP4_LEN (SENT_HDRLEN + SENT_IP4_OPTOFF) + +#define SENT_IPSP_LEN (SENT_HDRLEN + 2 * sizeof (struct in_addr) + sizeof (u_int32_t) + 4) + +/* + * Options 0x00 and 01 are 1-byte options (no arguments). + * The rest of the options are T-L-V fields, where the L includes + * the T and L bytes; thus, the minimum length for an option with + * no arguments is 2. An option of length less than 2 causes en EINVAL + */ + + +#define SENO_EOL 0x00 /* End of Options, or placeholder */ +#define SENO_NOP 0x01 /* No Operation. Skip */ +#define SENO_NAME 0x02 /* tunnel name, NUL-terminated */ +#define SENO_SPI 0x03 /* Security Parameters Index */ +#define SENO_IFN 0x04 /* Encap interface number */ +#define SENO_IFIP4A 0x05 /* Encap interface IPv4 address */ +#define SENO_IPSA 0x06 /* Encap interface generic sockaddr */ + +struct enc_softc +{ + struct ifnet enc_if; +}; + +/* + * Tunnel descriptors are setup and torn down using a socket of the + * AF_ENCAP domain. The following defines the messages that can + * be sent down that socket. + */ + +#define EM_MAXRELSPIS 4 /* at most five chained xforms */ + + +struct encap_msghdr +{ + u_int16_t em_msglen; /* message length */ + u_int8_t em_version; /* for future expansion */ + u_int8_t em_type; /* message type */ + union + { + struct + { + struct in_addr Ia; + u_int8_t Ifn; + u_int8_t xxx[3]; /* makes life a lot easier */ + } Ifa; + + struct + { + u_int32_t Spi; /* SPI */ + struct in_addr Dst; /* Destination address */ + int32_t If; /* enc i/f for input */ + int32_t Alg; /* Algorithm to use */ + u_int8_t Dat[1]; /* Data */ + } Xfm; + + struct + { + u_int32_t emr_spi; /* SPI */ + struct in_addr emr_dst; /* Dest */ + struct tdb * emr_tdb; /* used internally! */ + + } Rel[EM_MAXRELSPIS]; + } Eu; +}; + +#define em_ifa Eu.Ifa.Ia +#define em_ifn Eu.Ifa.Ifn + +#define em_spi Eu.Xfm.Spi +#define em_dst Eu.Xfm.Dst +#define em_if Eu.Xfm.If +#define em_alg Eu.Xfm.Alg +#define em_dat Eu.Xfm.Dat + +#define em_rel Eu.Rel + +#define EMT_IFADDR 1 /* set enc if addr */ +#define EMT_SETSPI 2 /* Set SPI properties */ +#define EMT_GRPSPIS 3 /* Group SPIs (output order) */ +#define EMT_DELSPI 4 /* delete an SPI */ +#define EMT_DELSPICHAIN 5 /* delete an SPI chain starting from */ + +#define EM_MINLEN 8 /* count!!! */ +#define EMT_IFADDR_LEN 12 +#define EMT_SETSPI_FLEN 20 +#define EMT_GRPSPIS_FLEN 4 +#define EMT_DELSPI_FLEN 20 +#define EMT_DELSPICHAIN_FLEN 20 + +#ifdef _KERNEL +extern struct ifaddr *encap_findgwifa(struct sockaddr *); +extern struct enc_softc *enc_softc; +extern int32_t nencap; +#endif diff --git a/sys/net/if_enc.c b/sys/net/if_enc.c new file mode 100644 index 00000000000..5cecd2dfaaf --- /dev/null +++ b/sys/net/if_enc.c @@ -0,0 +1,247 @@ +/* + * 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. + */ + +/* + * Encapsulation interface driver. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <machine/cpu.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/netisr.h> +#include <net/route.h> +#include <net/bpf.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#endif + +#ifdef ISO +extern struct ifqueue clnlintrq; +#endif + +#ifdef NS +extern struct ifqueue nsintrq; +#endif + +#include "bpfilter.h" + +#define ENCMTU (1024+512) + +/* + * Called from boot code to establish enc interfaces. + */ + +struct enc_softc +{ + struct ifnet enc_if; +} ; + +struct enc_softc *enc_softc; + +int nencap; + +int encoutput(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); +int encioctl(struct ifnet *, u_long, caddr_t); +void encrtrequest(int, struct rtentry *, struct sockaddr *); + +void +encattach(int nenc) +{ + register struct enc_softc *enc; + register int i = 0; + + nencap = nenc; + + enc_softc = malloc(nenc * sizeof (*enc_softc), M_DEVBUF, M_WAIT); + bzero(enc_softc, nenc * sizeof (*enc_softc)); + for (enc = enc_softc; i < nenc; enc++) + { + enc->enc_if.if_index = i; + sprintf(enc->enc_if.if_xname, "enc%d", i++); + enc->enc_if.if_list.tqe_next = NULL; + enc->enc_if.if_mtu = ENCMTU; + enc->enc_if.if_flags = IFF_LOOPBACK; + enc->enc_if.if_type = IFT_ENC; + enc->enc_if.if_ioctl = encioctl; + enc->enc_if.if_output = encoutput; + enc->enc_if.if_hdrlen = 0; + enc->enc_if.if_addrlen = 0; + if_attach(&enc->enc_if); + bpfattach(&enc->enc_if.if_bpf, &enc->enc_if, DLT_NULL, sizeof(u_int)); + } +} + +/* + * Shamelessly stolen from looutput() + */ +int +encoutput(ifp, m, dst, rt) +struct ifnet *ifp; +register struct mbuf *m; +struct sockaddr *dst; +register struct rtentry *rt; +{ + int s, isr; + register struct ifqueue *ifq = 0; + + /* register struct enc_softc *ec = &enc_softc[ifp->if_index]; */ + + if ((m->m_flags & M_PKTHDR) == 0) + panic("encoutput no HDR"); + ifp->if_lastchange = time; + if (ifp->if_bpf) { + /* + * We need to prepend the address family as + * a four byte field. Cons up a dummy header + * to pacify bpf. This is safe because bpf + * will only read from the mbuf (i.e., it won't + * try to free it or keep a pointer a to it). + */ + struct mbuf m0; + u_int af = dst->sa_family; + + m0.m_next = m; + m0.m_len = 4; + m0.m_data = (char *)⁡ + + bpf_mtap(ifp->if_bpf, &m0); + } + m->m_pkthdr.rcvif = ifp; + + if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { + m_freem(m); + return (rt->rt_flags & RTF_BLACKHOLE ? 0 : + rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); + } + ifp->if_opackets++; + ifp->if_obytes += m->m_pkthdr.len; + switch (dst->sa_family) { + +#ifdef INET + case AF_INET: + ifq = &ipintrq; + isr = NETISR_IP; + break; +#endif +#ifdef NS + case AF_NS: + ifq = &nsintrq; + isr = NETISR_NS; + break; +#endif +#ifdef ISO + case AF_ISO: + ifq = &clnlintrq; + isr = NETISR_ISO; + break; +#endif + default: + m_freem(m); + return (EAFNOSUPPORT); + } + s = splimp(); + if (IF_QFULL(ifq)) { + IF_DROP(ifq); + m_freem(m); + splx(s); + return (ENOBUFS); + } + IF_ENQUEUE(ifq, m); + schednetisr(isr); + ifp->if_ipackets++; + ifp->if_ibytes += m->m_pkthdr.len; + splx(s); + return (0); +} + +/* ARGSUSED */ +void +encrtrequest(cmd, rt, sa) + int cmd; + struct rtentry *rt; + struct sockaddr *sa; +{ + + if (rt) + rt->rt_rmx.rmx_mtu = ENCMTU; +} + +/* + * Process an ioctl request. + * Also shamelessly stolen from loioctl() + */ + +/* ARGSUSED */ +int +encioctl(ifp, cmd, data) + register struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + register struct ifaddr *ifa; + register struct ifreq *ifr; + register int error = 0; + + switch (cmd) + { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + ifa = (struct ifaddr *)data; + /* + * Everything else is done at a higher level. + */ + break; + + switch (ifr->ifr_addr.sa_family) { + +#ifdef INET + case AF_INET: + break; +#endif + case AF_ENCAP: + break; + + default: + error = EAFNOSUPPORT; + break; + } + break; + + default: + error = EINVAL; + } + return error; +} diff --git a/sys/net/if_types.h b/sys/net/if_types.h index fc3e749edac..4b8fe650a59 100644 --- a/sys/net/if_types.h +++ b/sys/net/if_types.h @@ -95,3 +95,4 @@ #define IFT_SMDSICIP 0x34 /* SMDS InterCarrier Interface */ #define IFT_PROPVIRTUAL 0x35 /* Proprietary Virtual/internal */ #define IFT_PROPMUX 0x36 /* Proprietary Multiplexing */ +#define IFT_ENC 0x37 /* Encapsulation */ diff --git a/sys/net/route.c b/sys/net/route.c index 59f83f1d4a8..05ef2994fa7 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -1,4 +1,4 @@ -/* $OpenBSD: route.c,v 1.2 1996/03/03 21:07:19 niklas Exp $ */ +/* $OpenBSD: route.c,v 1.3 1997/02/20 01:07:43 deraadt Exp $ */ /* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */ /* @@ -57,6 +57,10 @@ #include <netns/ns.h> #endif +#ifdef IPSEC +#include <net/encap.h> +#endif + #define SA(p) ((struct sockaddr *)(p)) int rttrash; /* routes not in table but not freed */ @@ -283,6 +287,18 @@ ifa_ifwithroute(flags, dst, gateway) struct sockaddr *dst, *gateway; { register struct ifaddr *ifa; + +#ifdef IPSEC + /* + * If the destination is a AF_ENCAP address, we'll look + * for the existence of a encap interface number or address + * in the options list of the gateway. By default, we'll return + * encap0. + */ + if (dst && (dst->sa_family == AF_ENCAP)) + return encap_findgwifa(gateway); +#endif + if ((flags & RTF_GATEWAY) == 0) { /* * If we are adding a route to an interface, diff --git a/sys/netinet/in.h b/sys/netinet/in.h index 7e9c0fa0dc4..d69dbcef7c9 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in.h,v 1.6 1996/07/29 02:34:29 downsj Exp $ */ +/* $OpenBSD: in.h,v 1.7 1997/02/20 01:07:45 deraadt Exp $ */ /* $NetBSD: in.h,v 1.20 1996/02/13 23:41:47 christos Exp $ */ /* @@ -58,6 +58,8 @@ #define IPPROTO_UDP 17 /* user datagram protocol */ #define IPPROTO_IDP 22 /* xns idp */ #define IPPROTO_TP 29 /* tp-4 w/ class negotiation */ +#define IPPROTO_ESP 50 /* Encap. Security Payload */ +#define IPPROTO_AH 51 /* Authentication header */ #define IPPROTO_EON 80 /* ISO cnlp */ #define IPPROTO_ENCAP 98 /* encapsulation header */ diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c index 295ca096960..66c5ddd6047 100644 --- a/sys/netinet/in_proto.c +++ b/sys/netinet/in_proto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in_proto.c,v 1.6 1996/10/08 07:33:25 niklas Exp $ */ +/* $OpenBSD: in_proto.c,v 1.7 1997/02/20 01:07:46 deraadt Exp $ */ /* $NetBSD: in_proto.c,v 1.14 1996/02/18 18:58:32 christos Exp $ */ /* @@ -94,6 +94,18 @@ void iplinit __P((void)); #define ip_init iplinit #endif +#ifdef IPSEC +#include <net/encap.h> +#include <netinet/ip_ipsp.h> + +extern void ah_input __P((struct mbuf *, ...)); +extern void esp_input __P((struct mbuf *, ...)); +extern int ah_output __P((struct mbuf *, struct sockaddr_encap *, + struct tdb *, struct mbuf **)); +extern int esp_output __P((struct mbuf *, struct sockaddr_encap *, + struct tdb *, struct mbuf **)); +#endif + extern struct domain inetdomain; struct protosw inetsw[] = { @@ -128,7 +140,13 @@ struct protosw inetsw[] = { rip_usrreq, /* XXX */ 0, 0, 0, 0, }, -#endif /* MROUTING */ +#elif defined(IPSEC) +{ SOCK_RAW, &inetdomain, IPPROTO_IPIP, PR_ATOMIC|PR_ADDR, + ipe4_input, rip_output, 0, rip_ctloutput, + rip_usrreq, /* XXX */ + 0, 0, 0, 0, +}, +#endif /* MROUTING/IPSEC */ { SOCK_RAW, &inetdomain, IPPROTO_IGMP, PR_ATOMIC|PR_ADDR, igmp_input, rip_output, 0, rip_ctloutput, rip_usrreq, @@ -163,6 +181,18 @@ struct protosw inetsw[] = { 0, 0, 0, 0, }, #endif /* NSIP */ +#ifdef IPSEC +{ SOCK_RAW, &inetdomain, IPPROTO_AH, PR_ATOMIC|PR_ADDR, + ah_input, rip_output, 0, rip_ctloutput, + rip_usrreq, + 0, 0, 0, 0, +}, +{ SOCK_RAW, &inetdomain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR, + esp_input, rip_output, 0, rip_ctloutput, + rip_usrreq, + 0, 0, 0, 0, +}, +#endif /* raw wildcard */ { SOCK_RAW, &inetdomain, 0, PR_ATOMIC|PR_ADDR, rip_input, rip_output, 0, rip_ctloutput, diff --git a/sys/netinet/ip_ah.c b/sys/netinet/ip_ah.c new file mode 100644 index 00000000000..e08f4c22fdb --- /dev/null +++ b/sys/netinet/ip_ah.c @@ -0,0 +1,160 @@ +/* + * 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 RFC1826 (Atkinson, 1995) + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/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> + + +/* + * ah_input gets called when we receive an packet with an AH. + */ + +void +ah_input(register struct mbuf *m, int iphlen) +{ + struct ip *ipo; + struct ah *ahp; + struct tdb *tdbp; + struct ifqueue *ifq = NULL; + int s; + + /* + * 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 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_pullup(m, iphlen + AH_FLENGTH)) == 0) + { + ahstat.ahs_hdrops++; + return; + } + ipo = mtod(m, struct ip *); + } + ahp = (struct ah *)((caddr_t)ipo + iphlen); + + /* + * Find tunnel control block and (indirectly) call the appropriate + * tranform routine. The resulting mbuf chain is a valid + * IP packet ready to go through input processing. + */ + + tdbp = gettdb(ahp->ah_spi, ipo->ip_dst); + if (tdbp == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_input: no tdb for spi=%x\n", ahp->ah_spi); +#endif ENCDEBUG + m_freem(m); + ahstat.ahs_notdb++; + return; + } + + if (tdbp->tdb_xform == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ah_input: no xform for spi=%x\n", ahp->ah_spi); +#endif ENCDEBUG + m_freem(m); + ahstat.ahs_noxform++; + return; + } + + m->m_pkthdr.rcvif = tdbp->tdb_rcvif; + + m = (*(tdbp->tdb_xform->xf_input))(m, tdbp); + + if (m == NULL) + { + ahstat.ahs_badkcr++; + return; + } + + /* + * Interface pointer is already in first mbuf; chop off the + * `outer' header and reschedule. + */ + + ifq = &ipintrq; + + s = splimp(); /* isn't it already? */ + if (IF_QFULL(ifq)) + { + IF_DROP(ifq); + m_freem(m); + ahstat.ahs_qfull++; + splx(s); + return; + } + IF_ENQUEUE(ifq, m); + schednetisr(NETISR_IP); + splx(s); + return; +} diff --git a/sys/netinet/ip_ah.h b/sys/netinet/ip_ah.h new file mode 100644 index 00000000000..4ac4334dd36 --- /dev/null +++ b/sys/netinet/ip_ah.h @@ -0,0 +1,162 @@ +/* + * 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 RFC1826 (Atkinson, 1995) + */ + +#include <netinet/ip_md5.h> +#include <netinet/ip_sha1.h> + +struct ah +{ + 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*/ +}; + +#define AH_FLENGTH 8 /* size of fixed part */ + +struct ahstat +{ + u_int32_t ahs_hdrops; /* packet shorter than header shows */ + u_int32_t ahs_notdb; + u_int32_t ahs_badkcr; + u_int32_t ahs_badauth; + u_int32_t ahs_noxform; + u_int32_t ahs_qfull; + u_int32_t ahs_wrap; + u_int32_t ahs_replay; + u_int32_t ahs_badauthl; /* bad authenticator length */ +}; + +#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 HMACMD5_HASHLEN 16 +#define HMACMD5_RPLENGTH 8 + +#define HMACMD5_IPAD_VAL 0x36 +#define HMACMD5_OPAD_VAL 0x5C + +struct ahhmacmd5 +{ + 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 */ +}; + +struct ahhmacmd5_xencap +{ + u_int16_t amx_alen; + u_int16_t amx_rpl; + int32_t amx_wnd; + u_int8_t amx_key[AHHMACMD5_KMAX]; +}; + +struct ahhmacmd5_xdata +{ + u_int32_t amx_alen; /* authenticator length */ + 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 */ +}; + +#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 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 +{ + u_int32_t amx_alen; + int32_t amx_wnd; + u_int8_t amx_key[AHHMACSHA1_KMAX]; +}; + +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 */ + +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 ahmd5_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 */ +}; + +#ifdef _KERNEL +struct ahstat ahstat; +#endif 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; +} diff --git a/sys/netinet/ip_ahhmacsha1.c b/sys/netinet/ip_ahhmacsha1.c new file mode 100644 index 00000000000..af306827e7c --- /dev/null +++ b/sys/netinet/ip_ahhmacsha1.c @@ -0,0 +1,463 @@ +/* + * 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-sha-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> + +/* + * ahhmacsha1_attach() is called from the transformation initialization code. + * It just returns. + */ + +int +ahhmacsha1_attach() +{ + return 0; +} + +/* + * ahhmacsha1_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 +ahhmacsha1_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) +{ + struct ahhmacsha1_xdata *xd; + struct ahhmacsha1_xencap txd; + struct encap_msghdr *em; + int len; + + tdbp->tdb_xform = xsp; + + MALLOC(tdbp->tdb_xdata, caddr_t, sizeof (struct ahhmacsha1_xdata), + M_XDATA, M_WAITOK); + if (tdbp->tdb_xdata == NULL) + return ENOBUFS; + bzero(tdbp->tdb_xdata, sizeof (struct ahhmacsha1_xdata)); + bzero(&txd, sizeof(struct ahhmacsha1_xencap)); + xd = (struct ahhmacsha1_xdata *)tdbp->tdb_xdata; + + em = mtod(m, struct encap_msghdr *); + if (em->em_msglen - EMT_SETSPI_FLEN > sizeof (struct ahhmacsha1_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; + + SHA1Init(&(xd->amx_ictx)); + SHA1Init(&(xd->amx_octx)); + + for (len = 0; len < AHHMACSHA1_KMAX; len++) + txd.amx_key[len] ^= HMACSHA1_IPAD_VAL; + + SHA1Update(&(xd->amx_ictx), txd.amx_key, AHHMACSHA1_KMAX); + + for (len = 0; len < AHHMACSHA1_KMAX; len++) + txd.amx_key[len] ^= (HMACSHA1_IPAD_VAL ^ HMACSHA1_OPAD_VAL); + + SHA1Update(&(xd->amx_octx), txd.amx_key, AHHMACSHA1_KMAX); + bzero(&txd, sizeof(struct ahhmacsha1_xencap)); + bzero(ipseczeroes, IPSEC_ZEROES_SIZE); /* paranoid */ + + return 0; +} + +/* + * Free memory + */ + +int +ahhmacsha1_zeroize(struct tdb *tdbp) +{ + FREE(tdbp->tdb_xdata, M_XDATA); + return 0; +} + +/* + * ahhmacsha1_input() gets called to verify that an input packet + * passes authentication. + */ + +extern struct ifnet loif; + +struct mbuf * +ahhmacsha1_input(struct mbuf *m, struct tdb *tdb) +{ + struct ahhmacsha1_xdata *xd; + struct ip *ip, ipo; + struct ah *ah; + struct ahhmacsha1 aho, *ahp; + struct ifnet *rcvif; + int ohlen, len, count, off, ado, errc; + u_int64_t btsx; + struct mbuf *m0; + SHA1_CTX ctx; + + xd = (struct ahhmacsha1_xdata *)tdb->tdb_xdata; + ohlen = sizeof (struct ip) + AH_FLENGTH + xd->amx_alen; + if (xd->amx_wnd >= 0) + ohlen += HMACSHA1_RPLENGTH; + + rcvif = m->m_pkthdr.rcvif; + if (rcvif == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ahhmacsha1_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 ahhmacsha1 *)ah; + + if (xd->amx_wnd >= 0) + ado = HMACSHA1_RPLENGTH; + else + ado = 0; + + if (ah->ah_hl != xd->amx_alen + ado) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ahhmacsha1_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; + SHA1Update(&ctx, (unsigned char *)&ipo, sizeof (struct ip)); + if (xd->amx_wnd >= 0) + SHA1Update(&ctx, (unsigned char *)ahp, + AH_FLENGTH + HMACSHA1_RPLENGTH); + else + SHA1Update(&ctx, (unsigned char *)ahp, AH_FLENGTH); + SHA1Update(&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("ahhmacsha1_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("ahhmacsha1_input: m_copydata (copy)"); + count = min(m0->m_len - off, len); + SHA1Update(&ctx, mtod(m0, unsigned char *) + off, count); + len -= count; + off = 0; + m0 = m0->m_next; + } + + SHA1Final((unsigned char *)(&(aho.ah_data[0])), &ctx); + ctx = xd->amx_octx; + SHA1Update(&ctx, (unsigned char *)(&(aho.ah_data[0])), + HMACSHA1_HASHLEN); + SHA1Final((unsigned char *)(&(aho.ah_data[0])), &ctx); + + if (bcmp(aho.ah_data, ah->ah_data + ado, xd->amx_alen)) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ahhmacsha1_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("ahhmacsha1_input: replay counter wrapped\n"); +#endif + ahstat.ahs_wrap++; + break; + case 2: +#ifdef ENCDEBUG + printf("ahhmacsha1_input: received old packet\n"); +#endif + ahstat.ahs_replay++; + break; + case 3: +#ifdef ENCDEBUG + printf("ahhmacsha1_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 +ahhmacsha1_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, struct mbuf **mp) +{ + struct ahhmacsha1_xdata *xd; + struct ip *ip, ipo; + struct ah *ah; + struct ahhmacsha1 *ahp, aho; + register int len, off, count; + register struct mbuf *m0; + SHA1_CTX ctx; + int ilen, ohlen; + + + m = m_pullup(m, sizeof (struct ip)); + if (m == NULL) + return ENOBUFS; + + ip = mtod(m, struct ip *); + + xd = (struct ahhmacsha1_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 += HMACSHA1_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 += (HMACSHA1_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("ahhmacsha1_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; + SHA1Update(&ctx, (unsigned char *)&ipo, sizeof (struct ip)); + if (xd->amx_wnd >= 0) + SHA1Update(&ctx, (unsigned char *)&aho, + AH_FLENGTH + HMACSHA1_RPLENGTH); + else + SHA1Update(&ctx, (unsigned char *)&aho, AH_FLENGTH); + SHA1Update(&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("ahhmacsha1_output: m_copydata"); + count = min(m0->m_len - off, len); + SHA1Update(&ctx, mtod(m0, unsigned char *) + off, count); + + len -= count; + off = 0; + m0 = m0->m_next; + } + + SHA1Final((unsigned char *)(&(aho.ah_data[0])), &ctx); + ctx = xd->amx_octx; + SHA1Update(&ctx, (unsigned char *)(&(aho.ah_data[0])), + HMACSHA1_HASHLEN); + SHA1Final((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 ahhmacsha1 *)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; +} diff --git a/sys/netinet/ip_ahmd5.c b/sys/netinet/ip_ahmd5.c new file mode 100644 index 00000000000..27429cb5fa1 --- /dev/null +++ b/sys/netinet/ip_ahmd5.c @@ -0,0 +1,353 @@ +/* + * 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 RFC1828 (Metzger & Simpson, 1995) + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/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> + +/* + * ahmd5_attach() is called from the transformation initialization code. + * It just returns. + */ + +int +ahmd5_attach() +{ + return 0; +} + +/* + * ahmd5_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 +ahmd5_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) +{ + struct ahmd5_xdata *xd; + struct encap_msghdr *em; + + tdbp->tdb_xform = xsp; + + MALLOC(tdbp->tdb_xdata, caddr_t, sizeof (struct ahmd5_xdata), + M_XDATA, M_WAITOK); + if (tdbp->tdb_xdata == NULL) + return ENOBUFS; + bzero(tdbp->tdb_xdata, sizeof (struct ahmd5_xdata)); + xd = (struct ahmd5_xdata *)tdbp->tdb_xdata; + + em = mtod(m, struct encap_msghdr *); + if (em->em_msglen - EMT_SETSPI_FLEN > sizeof (struct ahmd5_xdata)) + { + 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)xd); + bzero(ipseczeroes, IPSEC_ZEROES_SIZE); /* paranoid */ + return 0; +} + +/* + * Free memory + */ + +int +ahmd5_zeroize(struct tdb *tdbp) +{ + FREE(tdbp->tdb_xdata, M_XDATA); + return 0; +} + +/* + * ahmd5_input() gets called to verify that an input packet + * passes authentication. + */ + +extern struct ifnet loif; + +struct mbuf * +ahmd5_input(struct mbuf *m, struct tdb *tdb) +{ + struct ahmd5_xdata *xd; + struct ip *ip, ipo; + struct ah *ah; + struct ahmd5 aho; + struct ifnet *rcvif; + int ohlen, len, count, off; + struct mbuf *m0; + MD5_CTX ctx; + + xd = (struct ahmd5_xdata *)tdb->tdb_xdata; + ohlen = sizeof (struct ip) + AH_FLENGTH + xd->amx_alen; + + rcvif = m->m_pkthdr.rcvif; + if (rcvif == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ahmd5_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); + + 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; + + realMD5Init(&ctx); + MD5Update(&ctx, (unsigned char *)xd->amx_key, xd->amx_klen); + MD5Final(NULL, &ctx); /* non-std usage of MD5Final! */ + MD5Update(&ctx, (unsigned char *)&ipo, sizeof (struct ip)); + MD5Update(&ctx, (unsigned char *)ah, 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("ahmd5_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("ahmd5_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; + } + + + MD5Update(&ctx, (unsigned char *)xd->amx_key, xd->amx_klen); + MD5Final((unsigned char *)(&(aho.ah_data[0])), &ctx); + + if (bcmp(aho.ah_data, ah->ah_data, xd->amx_alen)) + { +#ifdef ENCDEBUG + if (encdebug) + printf("ahmd5_input: bad auth\n"); /* XXX */ +#endif + ahstat.ahs_badauth++; + 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 +ahmd5_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, struct mbuf **mp) +{ + struct ahmd5_xdata *xd; + struct ip *ip, ipo; + struct ah *ah, 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 ahmd5_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 + + 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; + aho.ah_rv = 0; + aho.ah_spi = tdb->tdb_spi; + + realMD5Init(&ctx); + + MD5Update(&ctx, (unsigned char *)xd->amx_key, xd->amx_klen); + MD5Final(NULL, &ctx); + MD5Update(&ctx, (unsigned char *)&ipo, sizeof (struct ip)); + 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("ahmd5_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; + } + + MD5Update(&ctx, (unsigned char *)xd->amx_key, xd->amx_klen); + + 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); + *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; + + + MD5Final(&(ah->ah_data[0]), &ctx); + + *mp = m; + + return 0; +} diff --git a/sys/netinet/ip_esp.c b/sys/netinet/ip_esp.c new file mode 100644 index 00000000000..13541d3b59e --- /dev/null +++ b/sys/netinet/ip_esp.c @@ -0,0 +1,158 @@ +/* + * 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. + */ + +/* + * Encapsulation Security Payload Processing + * Per RFC1827 (Atkinson, 1995) + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/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> + +/* + * esp_input gets called when we receive an packet with an ESP. + */ + +void +esp_input(register struct mbuf *m, int iphlen) +{ + struct ip *ipo; + struct ifqueue *ifq = NULL; + int s; + u_long spi; + struct tdb *tdbp; + + /* + * 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_pullup(m, iphlen + ESP_FLENGTH)) == 0) + { + espstat.esps_hdrops++; + return; + } + ipo = mtod(m, struct ip *); + } + spi = *((u_long *)((caddr_t)ipo + iphlen)); + + /* + * Find tunnel control block and (indirectly) call the appropriate + * kernel crypto routine. The resulting mbuf chain is a valid + * IP packet ready to go through input processing. + */ + + tdbp = gettdb(spi, ipo->ip_dst); + if (tdbp == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_input: no tdb for spi=%x\n", spi); +#endif ENCDEBUG + m_freem(m); + espstat.esps_notdb++; + return; + } + + if (tdbp->tdb_xform == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp_input: no xform for spi=%x\n", spi); +#endif ENCDEBUG + m_freem(m); + espstat.esps_noxform++; + return; + } + + m->m_pkthdr.rcvif = tdbp->tdb_rcvif; + + m = (*(tdbp->tdb_xform->xf_input))(m, tdbp); + + if (m == NULL) + { + espstat.esps_badkcr++; + return; + } + + /* + * Interface pointer is already in first mbuf; chop off the + * `outer' header and reschedule. + */ + + ifq = &ipintrq; + + s = splimp(); /* isn't it already? */ + if (IF_QFULL(ifq)) + { + IF_DROP(ifq); + m_freem(m); + espstat.esps_qfull++; + splx(s); + return; + } + IF_ENQUEUE(ifq, m); + schednetisr(NETISR_IP); + splx(s); + return; +} diff --git a/sys/netinet/ip_esp.h b/sys/netinet/ip_esp.h new file mode 100644 index 00000000000..abd2abf1f62 --- /dev/null +++ b/sys/netinet/ip_esp.h @@ -0,0 +1,179 @@ +/* + * 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. + */ + +/* + * Encapsulation Security Payload Processing + * Per RFC1827 (Atkinson, 1995) + */ + +#ifndef _MD5_H_ +#include <netinet/ip_md5.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 +{ + u_int32_t esp_spi; /* Security Parameters Index */ + u_int8_t esp_iv[8]; /* iv[4] may actually be data! */ +}; + +struct espstat +{ + u_long esps_hdrops; /* packet shorter than header shows */ + u_long esps_notdb; + u_long esps_badkcr; + u_long esps_qfull; + u_long esps_noxform; + u_long esps_badilen; + u_long esps_wrap; /* Replay counter wrapped around */ + u_long esps_badauth; /* Only valid for transforms with auth */ + u_long esps_replay; /* Possible packet replay detected */ +}; + +struct espdes_xdata +{ + int32_t edx_ivlen; /* 4 or 8 */ + union + { + u_int8_t Iv[8]; /* that's enough space */ + u_int32_t Ivl; /* XXX make sure this is 4 bytes */ + u_int64_t Ivq; /* XXX 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 esp3desmd5_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]; +}; + +struct espdesmd5_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[ESPDESMD5_IVS]; + u_int8_t edx_key[ESPDESMD5_KEYSZ]; +}; + +#define ESPDESMD5_ULENGTH 8+ESPDESMD5_IVS+ESPDESMD5_KEYSZ +#define ESP3DESMD5_ULENGTH 8+ESP3DESMD5_IVS+ESP3DESMD5_KEYSZ + +struct espdesmd5_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_bitmap; + u_int32_t edx_initial; /* initial replay value */ + union + { + u_int8_t Iv[8]; /* that's enough space */ + u_int32_t Ivl; /* XXX make sure this is 4 bytes */ + u_int64_t Ivq; /* XXX 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_int32_t Ivl; /* XXX make sure this is 4 bytes */ + u_int64_t Ivq; /* XXX make sure this is 8 bytes! */ + }Iu; + union + { + u_int8_t Rk[3][8]; + u_int32_t Eks[3][16][2]; + }Xu; + MD5_CTX edx_ictx; + MD5_CTX edx_octx; +}; + +#define ESP_FLENGTH 12 +#define ESP_ULENGTH 20 /* coming from user mode */ + +#ifdef _KERNEL + +struct espstat espstat; + +#endif diff --git a/sys/netinet/ip_esp3desmd5.c b/sys/netinet/ip_esp3desmd5.c new file mode 100644 index 00000000000..d0d5b204ac3 --- /dev/null +++ b/sys/netinet/ip_esp3desmd5.c @@ -0,0 +1,749 @@ +/* + * 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-3des-md5-00.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> + +extern struct ifnet loif; + +extern void des_ecb_encrypt(caddr_t, caddr_t, caddr_t, int); +extern void des_set_key(caddr_t, caddr_t); + +int +esp3desmd5_attach() +{ + return 0; +} + +/* + * esp3desmd5_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 +esp3desmd5_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) +{ + struct esp3desmd5_xdata *xd; + struct encap_msghdr *em; + struct esp3desmd5_xencap txd; + u_char buf[ESP3DESMD5_KEYSZ]; + int len; + MD5_CTX ctx; + + tdbp->tdb_xform = xsp; + + m = m_pullup(m, ESP3DESMD5_ULENGTH); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_init: can't pull up %d bytes\n", ESP_ULENGTH); +#endif ENCDEBUG + return ENOBUFS; + } + + MALLOC(tdbp->tdb_xdata, caddr_t, sizeof (struct esp3desmd5_xdata), + M_XDATA, M_WAITOK); + if (tdbp->tdb_xdata == NULL) + return ENOBUFS; + bzero(tdbp->tdb_xdata, sizeof (struct esp3desmd5_xdata)); + xd = (struct esp3desmd5_xdata *)tdbp->tdb_xdata; + + em = mtod(m, struct encap_msghdr *); + if (em->em_msglen - EMT_SETSPI_FLEN != ESP3DESMD5_ULENGTH) + { + 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); + + if ((txd.edx_ivlen != 0) && (txd.edx_ivlen != 8)) + { + free((caddr_t)tdbp->tdb_xdata, M_XDATA); + tdbp->tdb_xdata = NULL; + return EINVAL; + } + + bzero(ipseczeroes, IPSEC_ZEROES_SIZE); /* paranoid */ + + xd->edx_ivlen = txd.edx_ivlen; + xd->edx_bitmap = 0; + xd->edx_wnd = txd.edx_wnd; + + /* Fix the IV */ + +#ifdef ENCDEBUG + if (encdebug) + { + if (txd.edx_initiator) + printf("INITIATOR\n"); + printf("IV length: %d\n", txd.edx_ivlen); + } +#endif + if (txd.edx_ivlen) + bcopy(txd.edx_ivv, xd->edx_iv, ESP3DESMD5_IVS); + else + { + for (len = 0; len < ESP3DESMD5_KEYSZ; len++) + buf[len] = txd.edx_initiator ? ESP3DESMD5_IPADI : + ESP3DESMD5_IPADR; + + realMD5Init(&ctx); + MD5Update(&ctx, buf, ESP3DESMD5_KEYSZ); + MD5Update(&ctx, txd.edx_key, txd.edx_keylen); + MD5Final(buf, &ctx); + bcopy(buf, xd->edx_iv, ESP3DESMD5_IVS); +#ifdef ENCDEBUG + printf("IV "); + if (encdebug) + for (len = 0; len < ESP3DESMD5_IVS; len++) + printf(" %02x", xd->edx_iv[len]); + printf("\n"); +#endif + } + + /* DES key */ + + for (len = 1; len < ESP3DESMD5_KEYSZ; len++) + buf[len] = txd.edx_initiator ? ESP3DESMD5_DPADI : ESP3DESMD5_DPADR; + + buf[0] = 0; + realMD5Init(&ctx); + MD5Update(&ctx, buf, ESP3DESMD5_KEYSZ); + MD5Update(&ctx, txd.edx_key, txd.edx_keylen); + MD5Final(buf, &ctx); + des_set_key((caddr_t)buf, (caddr_t)(xd->edx_eks[0])); + + buf[0] = 1; + realMD5Init(&ctx); + MD5Update(&ctx, buf, ESP3DESMD5_KEYSZ); + MD5Update(&ctx, txd.edx_key, txd.edx_keylen); + MD5Final(buf, &ctx); + des_set_key((caddr_t)buf, (caddr_t)(xd->edx_eks[1])); + + buf[0] = 2; + realMD5Init(&ctx); + MD5Update(&ctx, buf, ESP3DESMD5_KEYSZ); + MD5Update(&ctx, txd.edx_key, txd.edx_keylen); + MD5Final(buf, &ctx); + des_set_key((caddr_t)buf, (caddr_t)(xd->edx_eks[2])); + + /* HMAC contexts */ + + realMD5Init(&ctx); + for (len = 0; len < ESP3DESMD5_KEYSZ; len++) + buf[len] = txd.edx_initiator ? ESP3DESMD5_HPADI : ESP3DESMD5_HPADR; + + MD5Update(&ctx, buf, ESP3DESMD5_KEYSZ); + MD5Update(&ctx, txd.edx_key, txd.edx_keylen); + MD5Final(buf, &ctx); + + bzero(buf + ESP3DESMD5_ALEN, ESP3DESMD5_KEYSZ - ESP3DESMD5_ALEN); + + for (len = 0; len < ESP3DESMD5_KEYSZ; len++) + buf[len] ^= ESP3DESMD5_IPAD_VAL; + + realMD5Init(&ctx); + MD5Update(&ctx, buf, ESP3DESMD5_KEYSZ); + xd->edx_ictx = ctx; + + for (len = 0; len < ESP3DESMD5_KEYSZ; len++) + buf[len] ^= (ESP3DESMD5_IPAD_VAL ^ ESP3DESMD5_OPAD_VAL); + + realMD5Init(&ctx); + MD5Update(&ctx, buf, ESP3DESMD5_KEYSZ); + xd->edx_octx = ctx; + + /* Replay counter */ + + for (len = 0; len < ESP3DESMD5_KEYSZ; len++) + buf[len] = txd.edx_initiator ? ESP3DESMD5_RPADI : + ESP3DESMD5_RPADR; + + realMD5Init(&ctx); + MD5Update(&ctx, buf, ESP3DESMD5_KEYSZ); + MD5Update(&ctx, txd.edx_key, txd.edx_keylen); + MD5Final(buf, &ctx); + bcopy(buf, (unsigned char *)&(xd->edx_rpl), ESP3DESMD5_RPLENGTH); + xd->edx_initial = xd->edx_rpl - 1; + +#ifdef ENCDEBUG + if (encdebug) + printf("Initial replay counter: %x (%x)\n", xd->edx_rpl, + xd->edx_initial); +#endif + + bzero(&txd, sizeof(struct esp3desmd5_xencap)); + bzero(buf, ESP3DESMD5_KEYSZ); + bzero(&ctx, sizeof(MD5_CTX)); + + return 0; +} + +int +esp3desmd5_zeroize(struct tdb *tdbp) +{ + FREE(tdbp->tdb_xdata, M_XDATA); + return 0; +} + + +struct mbuf * +esp3desmd5_input(struct mbuf *m, struct tdb *tdb) +{ + struct esp3desmd5_xdata *xd; + struct ip *ip, ipo; + u_char iv[8], niv[8], blk[8], auth[ESP3DESMD5_ALEN]; + u_char iauth[ESP3DESMD5_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 esp3desmd5_xdata *)tdb->tdb_xdata; + + rcvif = m->m_pkthdr.rcvif; + if (rcvif == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_input: receive interface is NULL!!!\n"); +#endif ENCDEBUG + rcvif = &loif; + } + + ip = mtod(m, struct ip *); + ipo = *ip; + esp = (struct esp *)(ip + 1); + + plen = m->m_pkthdr.len - sizeof (struct ip) - sizeof (u_long) - xd->edx_ivlen; + if (plen & 07) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_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) - ESP3DESMD5_IVS - sizeof(u_long); + idat = mtod(m, unsigned char *) + sizeof (struct ip) + sizeof(u_long) + + ESP3DESMD5_IVS; + + if (xd->edx_ivlen == 0) /* KeyIV in use */ + { + bcopy(xd->edx_iv, iv, ESP3DESMD5_IVS); + ilen += ESP3DESMD5_IVS; + idat -= ESP3DESMD5_IVS; + } + else + bcopy(idat - ESP3DESMD5_IVS, iv, ESP3DESMD5_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, ESP3DESMD5_IVS); + +#ifdef ENCDEBUG + printf("IV "); + for (i = 0; i < ESP3DESMD5_IVS; i++) + printf(" %02x", iv[i]); + printf("\n"); + i = 0; +#endif + + /* + * 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("esp3desmd5_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[2]), + 0); + des_ecb_encrypt(blk, blk, (caddr_t)(xd->edx_eks[1]), + 0); + des_ecb_encrypt(blk, blk, (caddr_t)(xd->edx_eks[0]), + 0); + for (i=0; i<8; i++) + { + while (olen == 0) + { + mo = mo->m_next; + if (mo == NULL) + panic("esp3desmd5_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 < ESP3DESMD5_ALEN) + { + bcopy(blk, auth + authp, ESP3DESMD5_DESBLK); + authp += ESP3DESMD5_DESBLK; +#ifdef ENCDEBUG + if (encdebug) + printf("Copying authenticator from %d\n", + plen); +#endif + } + else + { + if (plen == ESP3DESMD5_ALEN + 1) + { + nextproto = blk[7]; + padsize = blk[6]; +#ifdef ENCDEBUG + if (encdebug) + printf("Next protocol: %d\nPadsize: %d\n", nextproto, padsize); +#endif + } + else + if (plen + 7 == oplen) + { +#ifdef ENCDEBUG + if (encdebug) + printf("SEQ %02x %02x %02x %02x\n", + blk[0], blk[1], blk[2], + blk[3]); +#endif + 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("esp3desmd5_input: replay counter wrapped\n"); +#endif + espstat.esps_wrap++; + break; + case 2: +#ifdef ENCDEBUG + printf("esp3desmd5_input: received old packet, seq = %08x\n", rplc); +#endif + espstat.esps_replay++; + break; + case 3: +#ifdef ENCDEBUG + printf("esp3desmd5_input: packet already received\n"); +#endif + espstat.esps_replay++; + break; + } + m_freem(m); + return NULL; + } + } + + MD5Update(&ctx, blk, ESP3DESMD5_DESBLK); + } + } + + plen--; + } + + /* + * Now, the entire chain has been decrypted. + */ + + MD5Final(iauth, &ctx); + ctx = xd->edx_octx; + MD5Update(&ctx, iauth, ESP3DESMD5_ALEN); + MD5Final(iauth, &ctx); + +#ifdef ENCDEBUG + printf("RECEIVED "); + for (rplc = 0; rplc < ESP3DESMD5_ALEN; rplc++) + printf(" %02x", auth[rplc]); + printf("\nSHOULD HAVE "); + for (rplc = 0; rplc < ESP3DESMD5_ALEN; rplc++) + printf(" %02x", iauth[rplc]); + printf("\n"); +#endif + + if (bcmp(auth, iauth, ESP3DESMD5_ALEN)) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_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 - ESP3DESMD5_ALEN); + m_adj(m, 4 + xd->edx_ivlen + ESP3DESMD5_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) - ESP3DESMD5_RPLENGTH - 4 - xd->edx_ivlen - padsize - 2 - ESP3DESMD5_ALEN; +#ifdef ENCDEBUG + if (encdebug) + printf("IP packet size %d\n", ipo.ip_len); +#endif + ipo.ip_len = htons(ipo.ip_len); + ipo.ip_sum = 0; + *ip = ipo; + ip->ip_sum = in_cksum(m, sizeof (struct ip)); + + return m; +} + +int +esp3desmd5_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, struct mbuf **mp) +{ + struct esp3desmd5_xdata *xd; + struct ip *ip, ipo; + int i, ilen, olen, ohlen, nh, rlen, plen, padding; + u_int32_t rplc; + u_long spi; + struct mbuf *mi, *mo, *ms; + u_char *pad, *idat, *odat; + u_char iv[ESP3DESMD5_IVS], blk[8], auth[ESP3DESMD5_ALEN]; + MD5_CTX ctx; + + 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; + + xd = (struct esp3desmd5_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("esp3desmd5_output: replay counter wrapped\n"); +#endif + espstat.esps_wrap++; + return EHOSTDOWN; /* XXX */ + } + + ipo = *ip; + nh = ipo.ip_p; + +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_output: next protocol is %d\n", nh); +#endif + + /* Raw payload length */ + rlen = ESP3DESMD5_RPLENGTH + ilen - sizeof (struct ip); + + 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; + +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_output: padding %d bytes\n", padding); +#endif + + plen = rlen + padding + ESP3DESMD5_ALEN; + + ctx = xd->edx_ictx; /* Get inner padding cached */ + + bcopy(xd->edx_iv, iv, ESP3DESMD5_IVS); + + MD5Update(&ctx, (u_char *)&spi, sizeof(u_long)); + MD5Update(&ctx, iv, ESP3DESMD5_IVS); + rplc = htonl(xd->edx_rpl); + MD5Update(&ctx, (unsigned char *)&rplc, ESP3DESMD5_RPLENGTH); + xd->edx_rpl++; + +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_output: using replay counter %x\n", + xd->edx_rpl - 1); +#endif + mi = m; + + /* MD5 the data */ + while (mi != NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_output: MD5'ing %d bytes\n", mi->m_len); +#endif + if (mi == m) + MD5Update(&ctx, (u_char *)mi->m_data + sizeof(struct ip), + mi->m_len - sizeof(struct ip)); + 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, ESP3DESMD5_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, sizeof(struct ip) + ESP3DESMD5_RPLENGTH + + sizeof(u_int32_t) + xd->edx_ivlen); + if (m == NULL) + return ENOBUFS; + + /* Copy data if necessary */ + if (m->m_len - sizeof(struct ip)) + { +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_output: pushing data\n"); +#endif + ms = m_copym(m, sizeof(struct ip), m->m_len - sizeof(struct ip), + M_DONTWAIT); + if (ms == NULL) + return ENOBUFS; + + ms->m_next = m->m_next; + m->m_next = ms; + m->m_len = sizeof(struct ip); + } + + /* Copy SPI, IV (or not) and replay counter */ + bcopy((caddr_t)&spi, mtod(m, caddr_t) + sizeof (struct ip), + sizeof (u_int32_t)); + bcopy((caddr_t)iv, mtod(m, caddr_t) + sizeof (struct ip) + + sizeof (u_int32_t), xd->edx_ivlen); + bcopy((caddr_t)&rplc, mtod(m, caddr_t) + sizeof(struct ip) + + sizeof(u_int32_t) + xd->edx_ivlen, ESP3DESMD5_RPLENGTH); + +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_output: replay counter (wire value) %x\n", rplc); +#endif + + /* Adjust the length accordingly */ + m->m_len += sizeof(u_int32_t) + ESP3DESMD5_RPLENGTH + xd->edx_ivlen; + m->m_pkthdr.len += sizeof(u_int32_t) + ESP3DESMD5_RPLENGTH + + xd->edx_ivlen; + +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_output: mbuf chain length %d\n", m->m_pkthdr.len); +#endif + + /* Let's append the authenticator too */ + MGET(ms, M_DONTWAIT, MT_DATA); + if (ms == NULL) + return ENOBUFS; + + bcopy(auth, mtod(ms, u_char *), ESP3DESMD5_ALEN); + ms->m_len = ESP3DESMD5_ALEN; + + m_cat(m, ms); + m->m_pkthdr.len += ESP3DESMD5_ALEN; /* Adjust length */ + +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_output: final mbuf chain length %d\n", + m->m_pkthdr.len); +#endif + + ilen = olen = m->m_len - sizeof (struct ip) - sizeof(u_int32_t) - + xd->edx_ivlen; + idat = odat = mtod(m, u_char *) + sizeof (struct ip) + + sizeof(u_int32_t) + xd->edx_ivlen; + i = 0; + mi = mo = m; + +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_output: starting encryption with ilen=%d, plen=%d\n", ilen, plen); +#endif + + while (plen > 0) /* while not done */ + { + while (ilen == 0) /* we exhausted previous mbuf */ + { + mi = mi->m_next; + if (mi == NULL) + panic("esp3desmd5_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[0]), + 1); + des_ecb_encrypt(blk, blk, (caddr_t)(xd->edx_eks[1]), + 1); + des_ecb_encrypt(blk, blk, (caddr_t)(xd->edx_eks[2]), + 1); + for (i=0; i<8; i++) + { + while (olen == 0) + { + mo = mo->m_next; + if (mo == NULL) + panic("esp3desmd5_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--; + } + +#ifdef ENCDEBUG + if (encdebug) + printf("esp3desmd5_output: almost done now\n"); +#endif + + if (xd->edx_ivlen != 0) + bcopy(iv, xd->edx_iv, ESP3DESMD5_IVS); /* New IV */ + + /* Fix the length and the next protocol, copy back and off we go */ + ipo.ip_len = htons(sizeof (struct ip) + ohlen + rlen + padding + + ESP3DESMD5_ALEN); + ipo.ip_p = IPPROTO_ESP; + bcopy((caddr_t)&ipo, mtod(m, caddr_t), sizeof(struct ip)); + + *mp = m; + return 0; +} diff --git a/sys/netinet/ip_espdes.c b/sys/netinet/ip_espdes.c new file mode 100644 index 00000000000..b034a3e78db --- /dev/null +++ b/sys/netinet/ip_espdes.c @@ -0,0 +1,516 @@ +/* + * 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 RFC1829 (Metzger & Simpson, 1995) + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/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> + +extern struct ifnet loif; + +extern void des_ecb_encrypt(caddr_t, caddr_t, caddr_t, int); +extern void des_set_key(caddr_t, caddr_t); + +extern MD5_CTX IP4_ctx; + +extern int ticks; +#ifdef EXT_CLOCK +extern int clock_count; +#endif + +int +espdes_attach() +{ + return 0; +} + +/* + * espdes_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 +espdes_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) +{ + struct espdes_xdata *xd; + struct encap_msghdr *em; + u_long rk[2]; + + tdbp->tdb_xform = xsp; + + m = m_pullup(m, ESP_ULENGTH); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("espdes_init: can't pull up %d bytes\n", ESP_ULENGTH); +#endif ENCDEBUG + return ENOBUFS; + } + + MALLOC(tdbp->tdb_xdata, caddr_t, sizeof (struct espdes_xdata), + M_XDATA, M_WAITOK); + if (tdbp->tdb_xdata == NULL) + return ENOBUFS; + bzero(tdbp->tdb_xdata, sizeof (struct espdes_xdata)); + xd = (struct espdes_xdata *)tdbp->tdb_xdata; + + em = mtod(m, struct encap_msghdr *); + if (em->em_msglen - EMT_SETSPI_FLEN != ESP_ULENGTH) + { + free((caddr_t)tdbp->tdb_xdata, M_XDATA); + tdbp->tdb_xdata = NULL; + return EINVAL; + } + + m_copydata(m, EMT_SETSPI_FLEN, ESP_ULENGTH, (caddr_t)xd); + + rk[0] = xd->edx_eks[0][0]; /* some overloading doesn't hurt */ + rk[1] = xd->edx_eks[0][1]; /* XXX -- raw-major order */ + + des_set_key((caddr_t)rk, (caddr_t)(xd->edx_eks)); + rk[0] = rk[1] = 0; /* zeroize! */ + + return 0; +} + +int +espdes_zeroize(struct tdb *tdbp) +{ + FREE(tdbp->tdb_xdata, M_XDATA); + return 0; +} + + +struct mbuf * +espdes_input(struct mbuf *m, struct tdb *tdb) +{ + struct espdes_xdata *xd; + struct ip *ip, ipo; + u_char iv[8], niv[8], blk[8]; + u_char *idat, *odat; + struct esp *esp; + struct ifnet *rcvif; + int ohlen, plen, ilen, olen, i; + struct mbuf *mi, *mo; + + xd = (struct espdes_xdata *)tdb->tdb_xdata; + ohlen = sizeof (struct ip) + ESP_FLENGTH; + + rcvif = m->m_pkthdr.rcvif; + if (rcvif == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("espdes_input: receive interface is NULL!!!\n"); +#endif ENCDEBUG + rcvif = &loif; + } + + ip = mtod(m, struct ip *); + ipo = *ip; + esp = (struct esp *)(ip + 1); + + plen = m->m_pkthdr.len - sizeof (struct ip) - sizeof (u_long) - xd->edx_ivlen; + if (plen & 07) + { +#ifdef ENCDEBUG + if (encdebug) + printf("espdes_input: payload not a multiple of 8 octets\n"); +#endif ENCDEBUG + espstat.esps_badilen++; + m_freem(m); + return NULL; + } + + ilen = m->m_len - sizeof (struct ip) - 8; + idat = mtod(m, unsigned char *) + sizeof (struct ip) + 8; + + 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) + { + 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]; + + 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("espdes_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("espdes_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--; + } + + /* + * 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 < sizeof (struct ip)) + { + m = m_pullup(m, sizeof (struct ip)); + if (m == NULL) + { + 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 += sizeof (struct ip) - 4 - xd->edx_ivlen - blk[6] - 2; + ipo.ip_len = htons(ipo.ip_len); + ipo.ip_sum = 0; + *ip = ipo; + ip->ip_sum = in_cksum(m, sizeof (struct ip)); + + return m; +} + +int +espdes_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, struct mbuf **mp) +{ + struct espdes_xdata *xd; + struct ip *ip, ipo; + int i, ilen, olen, ohlen, nh, rlen, plen, padding; + u_long spi; + struct mbuf *mi, *mo; + u_char *pad, *idat, *odat; + u_char iv[8], blk[8]; + + + m = m_pullup(m, sizeof (struct ip)); + if (m == NULL) + return ENOBUFS; + + ip = mtod(m, struct ip *); + spi = tdb->tdb_spi; + + xd = (struct espdes_xdata *)tdb->tdb_xdata; + ilen = ntohs(ip->ip_len); + ohlen = sizeof (u_long) + xd->edx_ivlen; + + ipo = *ip; + nh = ipo.ip_p; + + rlen = ilen - sizeof (struct ip); /* raw payload length */ + 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; + mi = mo = m; + ilen = olen = m->m_len - sizeof (struct ip); + idat = odat = mtod(m, u_char *) + sizeof (struct ip); + i = 0; + + /* + * We are now ready to encrypt the payload. + */ + + xd->edx_ivl++; + + 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) + { + 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("espdes_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) + { + 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("espdes_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) + return ENOBUFS; + + m = m_pullup(m, sizeof(struct ip) + xd->edx_ivlen + sizeof(u_long)); + if (m == NULL) + return ENOBUFS; + + ipo.ip_len = htons(sizeof (struct ip) + 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]; + } + + bcopy((caddr_t)&ipo, mtod(m, caddr_t), sizeof (struct ip)); + bcopy((caddr_t)&spi, mtod(m, caddr_t) + sizeof (struct ip), sizeof (u_long)); + bcopy((caddr_t)iv, mtod(m, caddr_t) + sizeof (struct ip) + sizeof (u_long), xd->edx_ivlen); + + *mp = m; + 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 int len, pad; + register struct mbuf *m0, *m1; + caddr_t retval; + u_char 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(dat)); + retval[len] = len + dat; + } + + return retval; +} diff --git a/sys/netinet/ip_espdesmd5.c b/sys/netinet/ip_espdesmd5.c new file mode 100644 index 00000000000..ed1b2c9845a --- /dev/null +++ b/sys/netinet/ip_espdesmd5.c @@ -0,0 +1,805 @@ +/* + * 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> + +extern struct ifnet loif; + +extern void des_ecb_encrypt(caddr_t, caddr_t, caddr_t, int); +extern void des_set_key(caddr_t, caddr_t); + +int +espdesmd5_attach() +{ + return 0; +} + +/* + * espdesmd5_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 +espdesmd5_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) +{ + struct espdesmd5_xdata *xd; + struct encap_msghdr *em; + struct espdesmd5_xencap txd; + u_char buf[ESPDESMD5_KEYSZ]; + int len; + MD5_CTX ctx; + + tdbp->tdb_xform = xsp; + + m = m_pullup(m, ESPDESMD5_ULENGTH); + if (m == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_init: can't pull up %d bytes\n", ESP_ULENGTH); +#endif ENCDEBUG + return ENOBUFS; + } + + MALLOC(tdbp->tdb_xdata, caddr_t, sizeof (struct espdesmd5_xdata), + M_XDATA, M_WAITOK); + if (tdbp->tdb_xdata == NULL) + return ENOBUFS; + bzero(tdbp->tdb_xdata, sizeof (struct espdesmd5_xdata)); + xd = (struct espdesmd5_xdata *)tdbp->tdb_xdata; + + em = mtod(m, struct encap_msghdr *); + if (em->em_msglen - EMT_SETSPI_FLEN != ESPDESMD5_ULENGTH) + { + 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); + + if ((txd.edx_ivlen != 0) && (txd.edx_ivlen != 8)) + { + free((caddr_t)tdbp->tdb_xdata, M_XDATA); + tdbp->tdb_xdata = NULL; + return EINVAL; + } + + bzero(ipseczeroes, IPSEC_ZEROES_SIZE); /* paranoid */ + + xd->edx_ivlen = txd.edx_ivlen; + xd->edx_bitmap = 0; + xd->edx_wnd = txd.edx_wnd; + + /* Fix the IV */ + +#ifdef ENCDEBUG + if (encdebug) + { + if (txd.edx_initiator) + printf("INITIATOR\n"); + printf("IV length: %d\n", txd.edx_ivlen); + } +#endif + 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; + + realMD5Init(&ctx); + MD5Update(&ctx, buf, ESPDESMD5_KEYSZ); + MD5Update(&ctx, txd.edx_key, txd.edx_keylen); + MD5Final(buf, &ctx); + bcopy(buf, xd->edx_iv, ESPDESMD5_IVS); +#ifdef ENCDEBUG + printf("IV "); + if (encdebug) + for (len = 0; len < ESPDESMD5_IVS; len++) + printf(" %02x", xd->edx_iv[len]); + printf("\n"); +#endif + } + + /* DES key */ + + realMD5Init(&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 */ + + realMD5Init(&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; + + realMD5Init(&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); + + realMD5Init(&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; + + realMD5Init(&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; + +#ifdef ENCDEBUG + if (encdebug) + printf("Initial replay counter: %x (%x)\n", xd->edx_rpl, + xd->edx_initial); +#endif + + bzero(&txd, sizeof(struct espdesmd5_xencap)); + bzero(buf, ESPDESMD5_KEYSZ); + bzero(&ctx, sizeof(MD5_CTX)); + + return 0; +} + +int +espdesmd5_zeroize(struct tdb *tdbp) +{ + FREE(tdbp->tdb_xdata, M_XDATA); + return 0; +} + + +struct mbuf * +espdesmd5_input(struct mbuf *m, struct tdb *tdb) +{ + struct espdesmd5_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 espdesmd5_xdata *)tdb->tdb_xdata; + + rcvif = m->m_pkthdr.rcvif; + if (rcvif == NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_input: receive interface is NULL!!!\n"); +#endif ENCDEBUG + rcvif = &loif; + } + + ip = mtod(m, struct ip *); + ipo = *ip; + esp = (struct esp *)(ip + 1); + + plen = m->m_pkthdr.len - sizeof (struct ip) - sizeof (u_long) - xd->edx_ivlen; + if (plen & 07) + { +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_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_long); + idat = mtod(m, unsigned char *) + sizeof (struct ip) + sizeof(u_long) + + 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); + +#ifdef ENCDEBUG + printf("IV "); + for (i = 0; i < ESPDESMD5_IVS; i++) + printf(" %02x", iv[i]); + printf("\n"); + i = 0; +#endif + + /* + * 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("espdesmd5_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("espdesmd5_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; +#ifdef ENCDEBUG + if (encdebug) + printf("Copying authenticator from %d\n", + plen); +#endif + } + else + { + if (plen == ESPDESMD5_ALEN + 1) + { + nextproto = blk[7]; + padsize = blk[6]; +#ifdef ENCDEBUG + if (encdebug) + printf("Next protocol: %d\nPadsize: %d\n", nextproto, padsize); +#endif + } + else + if (plen + 7 == oplen) + { +#ifdef ENCDEBUG + if (encdebug) + printf("SEQ %02x %02x %02x %02x\n", + blk[0], blk[1], blk[2], + blk[3]); +#endif + 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("espdesmd5_input: replay counter wrapped\n"); +#endif + espstat.esps_wrap++; + break; + case 2: +#ifdef ENCDEBUG + printf("espdesmd5_input: received old packet, seq = %08x\n", rplc); +#endif + espstat.esps_replay++; + break; + case 3: +#ifdef ENCDEBUG + printf("espdesmd5_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); + +#ifdef ENCDEBUG + printf("RECEIVED "); + for (rplc = 0; rplc < ESPDESMD5_ALEN; rplc++) + printf(" %02x", auth[rplc]); + printf("\nSHOULD HAVE "); + for (rplc = 0; rplc < ESPDESMD5_ALEN; rplc++) + printf(" %02x", iauth[rplc]); + printf("\n"); +#endif + + if (bcmp(auth, iauth, ESPDESMD5_ALEN)) + { +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_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 - ESPDESMD5_ALEN); + 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; +#ifdef ENCDEBUG + if (encdebug) + printf("IP packet size %d\n", ipo.ip_len); +#endif + ipo.ip_len = htons(ipo.ip_len); + ipo.ip_sum = 0; + *ip = ipo; + ip->ip_sum = in_cksum(m, sizeof (struct ip)); + + return m; +} + +int +espdesmd5_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, struct mbuf **mp) +{ + struct espdesmd5_xdata *xd; + struct ip *ip, ipo; + int i, ilen, olen, ohlen, nh, rlen, plen, padding; + u_int32_t rplc; + u_long spi; + struct mbuf *mi, *mo, *ms; + u_char *pad, *idat, *odat; + u_char iv[ESPDESMD5_IVS], blk[8], auth[ESPDESMD5_ALEN]; + MD5_CTX ctx; + + 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; + + xd = (struct espdesmd5_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("espdesmd5_output: replay counter wrapped\n"); +#endif + espstat.esps_wrap++; + return EHOSTDOWN; /* XXX */ + } + + ipo = *ip; + nh = ipo.ip_p; + +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_output: next protocol is %d\n", nh); +#endif + + /* Raw payload length */ + rlen = ESPDESMD5_RPLENGTH + ilen - sizeof (struct ip); + + 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; + +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_output: padding %d bytes\n", padding); +#endif + + 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_long)); + MD5Update(&ctx, iv, ESPDESMD5_IVS); + rplc = htonl(xd->edx_rpl); + MD5Update(&ctx, (unsigned char *)&rplc, ESPDESMD5_RPLENGTH); + xd->edx_rpl++; + +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_output: using replay counter %x\n", + xd->edx_rpl - 1); +#endif + mi = m; + + /* MD5 the data */ + while (mi != NULL) + { +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_output: MD5'ing %d bytes\n", mi->m_len); +#endif + if (mi == m) + MD5Update(&ctx, (u_char *)mi->m_data + sizeof(struct ip), + mi->m_len - sizeof(struct ip)); + 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, sizeof(struct ip) + ESPDESMD5_RPLENGTH + + sizeof(u_int32_t) + xd->edx_ivlen); + if (m == NULL) + return ENOBUFS; + + /* Copy data if necessary */ + if (m->m_len - sizeof(struct ip)) + { +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_output: pushing data\n"); +#endif + ms = m_copym(m, sizeof(struct ip), m->m_len - sizeof(struct ip), + M_DONTWAIT); + if (ms == NULL) + return ENOBUFS; + + ms->m_next = m->m_next; + m->m_next = ms; + m->m_len = sizeof(struct ip); + } + + /* Copy SPI, IV (or not) and replay counter */ + bcopy((caddr_t)&spi, mtod(m, caddr_t) + sizeof (struct ip), + sizeof (u_int32_t)); + bcopy((caddr_t)iv, mtod(m, caddr_t) + sizeof (struct ip) + + sizeof (u_int32_t), xd->edx_ivlen); + bcopy((caddr_t)&rplc, mtod(m, caddr_t) + sizeof(struct ip) + + sizeof(u_int32_t) + xd->edx_ivlen, ESPDESMD5_RPLENGTH); + +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_output: replay counter (wire value) %x\n", rplc); +#endif + + /* 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; + +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_output: mbuf chain length %d\n", m->m_pkthdr.len); +#endif + + /* 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 */ + +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_output: final mbuf chain length %d\n", + m->m_pkthdr.len); +#endif + + ilen = olen = m->m_len - sizeof (struct ip) - sizeof(u_int32_t) - + xd->edx_ivlen; + idat = odat = mtod(m, u_char *) + sizeof (struct ip) + + sizeof(u_int32_t) + xd->edx_ivlen; + i = 0; + mi = mo = m; + +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_output: starting encryption with ilen=%d, plen=%d\n", ilen, plen); +#endif + + while (plen > 0) /* while not done */ + { + while (ilen == 0) /* we exhausted previous mbuf */ + { + mi = mi->m_next; + if (mi == NULL) + panic("espdesmd5_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("espdesmd5_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--; + } + +#ifdef ENCDEBUG + if (encdebug) + printf("espdesmd5_output: almost done now\n"); +#endif + + 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(sizeof (struct ip) + ohlen + rlen + padding + + ESPDESMD5_ALEN); + ipo.ip_p = IPPROTO_ESP; + bcopy((caddr_t)&ipo, mtod(m, caddr_t), sizeof(struct ip)); + + *mp = m; + return 0; +} + + +/* + * both versions of the checkreplaywindow for 32 and 64 bit counters + * 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 +checkreplaywindow64(u_int64_t seq, u_int64_t *lastseq, u_int64_t window, u_int64_t *bitmap) +{ + u_int64_t diff; + + if (seq == 0) + return 1; + + if (seq > *lastseq) + { + diff = seq - *lastseq; + if (diff < window) + *bitmap = ((*bitmap) << diff) | 1; + else + *bitmap = 1; + *lastseq = seq; + return 0; + } + diff = *lastseq - seq; + if (diff >= window) + { + espstat.esps_wrap++; + return 2; + } + if ((*bitmap) & (((u_int64_t) 1) << diff)) + { + espstat.esps_replay++; + return 3; + } + *bitmap |= (((u_int64_t) 1) << diff); + return 0; +} + +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; + +#ifdef ENCDEBUG + if (encdebug) + printf("checkreplaywindow32: seq=%x lastseq=%x\n", seq, *lastseq); +#endif + + 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_ip4.c b/sys/netinet/ip_ip4.c new file mode 100644 index 00000000000..b16b0786d8e --- /dev/null +++ b/sys/netinet/ip_ip4.c @@ -0,0 +1,222 @@ +/* + * 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. + */ + +/* + * IP-inside-IP processing + */ + + +#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_ip4.h> +#include <dev/rndvar.h> + + + +/* + * ip4_input gets called when we receive an encapsulated packet, + * either because we got it at a real interface, or because AH or ESP + * were being used in tunnel mode (in which case the rcvif element will + * contain the address of the encapX interface associated with the tunnel. + */ + +void +ip4_input(register struct mbuf *m, int iphlen) +{ + struct ip *ipo, *ipi; + struct ifqueue *ifq = NULL; + int s; + /* + * Strip IP options, if any. + */ + + if (iphlen > sizeof (struct ip)) + { + ip_stripoptions(m, (struct mbuf *)0); + iphlen = sizeof (struct ip); + } + + /* + * Make sure next IP header is in the first mbuf. + * + * Careful here! we are receiving the packet from ipintr; + * this means that the ip_len field has been adjusted to + * not count the ip header, and is also in host order. + */ + + ipo = mtod(m, struct ip *); + + if (m->m_len < iphlen + sizeof (struct ip)) + { + if ((m = m_pullup(m, iphlen + sizeof (struct ip))) == 0) + { + ip4stat.ip4s_hdrops++; + return; + } + ipo = mtod(m, struct ip *); + } + ipi = (struct ip *)((caddr_t)ipo + iphlen); + + /* + * XXX - Should we do anything to the inner packet? + * Does arriving at the far end of the tunnel count as one hop + * (thus requiring ipi->ip_ttl to be decremented)? + */ + + if (ipi->ip_v != IPVERSION) + { + ip4stat.ip4s_notip4++; + return; + } + + /* + * Interface pointer is already in first mbuf; chop off the + * `outer' header and reschedule. + */ + + m->m_len -= iphlen; + m->m_pkthdr.len -= iphlen; + m->m_data += iphlen; + + /* XXX -- interface pointer stays the same (which is probably + * the way it should be. + */ + + ifq = &ipintrq; + + s = splimp(); /* isn't it already? */ + if (IF_QFULL(ifq)) + { + IF_DROP(ifq); + m_freem(m); + ip4stat.ip4s_qfull++; + splx(s); + return; + } + IF_ENQUEUE(ifq, m); + schednetisr(NETISR_IP); + splx(s); + + return; +} + +int +ipe4_output(struct mbuf *m, struct sockaddr_encap *gw, struct tdb *tdb, struct mbuf **mp) +{ + struct ip *ipo, *ipi; + ushort ilen; + + ipi = mtod(m, struct ip *); + ilen = ntohs(ipi->ip_len); + + M_PREPEND(m, sizeof (struct ip), M_DONTWAIT); + if (m == 0) + 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_off = ipi->ip_off & ~(IP_MF | IP_OFFMASK); /* keep C and DF */ + ipo->ip_ttl = ipi->ip_ttl; /* already decremented if fwding */ + ipo->ip_p = IPPROTO_IPIP; + ipo->ip_sum = 0; + ipo->ip_src = gw->sen_ipsp_src; + ipo->ip_dst = gw->sen_ipsp_dst; + +/* printf("ip4_output: [%x->%x](l=%d, p=%d)", + ntohl(ipi->ip_src.s_addr), ntohl(ipi->ip_dst.s_addr), + ilen, ipi->ip_p); + printf(" through [%x->%x](l=%d, p=%d)\n", + ntohl(ipo->ip_src.s_addr), ntohl(ipo->ip_dst.s_addr), + ipo->ip_len, ipo->ip_p);*/ + + *mp = m; + return 0; + +/* return ip_output(m, NULL, NULL, IP_ENCAPSULATED, NULL);*/ +} + +int +ipe4_attach() +{ + return 0; +} + +int +ipe4_init(struct tdb *tdbp, struct xformsw *xsp, struct mbuf *m) +{ + printf("ipe4_init: setting up\n"); + tdbp->tdb_xform = xsp; + if (m) + m_freem(m); + return 0; +} + +int +ipe4_zeroize(struct tdb *tdbp) +{ + /* Nothing much really - we don't need any state */ + return 0; +} + + + +void +ipe4_input(struct mbuf *m, ...) +{ + printf("ipe4_input: should never be called\n"); + if (m) + m_freem(m); +} diff --git a/sys/netinet/ip_ip4.h b/sys/netinet/ip_ip4.h new file mode 100644 index 00000000000..4e580fe7524 --- /dev/null +++ b/sys/netinet/ip_ip4.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +/* + * IP-inside-IP processing. + * Not quite all the functionality of RFC-1853, but the main idea is there. + */ + +struct ip4stat +{ + u_long ip4s_ipackets; /* total input packets */ + u_long ip4s_hdrops; /* packet shorter than header shows */ + u_long ip4s_badlen; + u_long ip4s_notip4; + u_long ip4s_qfull; +}; + +#ifdef _KERNEL +struct ip4stat ip4stat; +#endif diff --git a/sys/netinet/ip_ipsp.c b/sys/netinet/ip_ipsp.c new file mode 100644 index 00000000000..390745def98 --- /dev/null +++ b/sys/netinet/ip_ipsp.c @@ -0,0 +1,259 @@ +/* + * 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. + */ + +/* + * IPSP Processing + */ + +#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/socketvar.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> +#include <net/route.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/raw_cb.h> +#include <net/encap.h> + +#define IPSEC_IPSP_C +#include <netinet/ip_ipsp.h> +#undef IPSEC_IPSP_C +#include <netinet/ip_ah.h> +#include <netinet/ip_esp.h> + +#ifdef ENCDEBUG +int encdebug = 1; +#endif + +/* + * This is the proper place to define the various encapsulation transforms. + * CAUTION: the human-readable string should be LESS than 200 bytes if the + * kernfs is to work properly. + */ + +struct xformsw xformsw[] = { +{ XF_IP4, 0, "IPv4 Simple Encapsulation", + 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_ESPDES, XFT_CONF, "DES-CBC Encryption", + espdes_attach, espdes_init, espdes_zeroize, + espdes_input, espdes_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, "DES-CBC Encryption + MD5 Authentication", + espdesmd5_attach, espdesmd5_init, espdesmd5_zeroize, + espdesmd5_input, espdesmd5_output, }, +{ XF_ESP3DESMD5, XFT_CONF, "3DES-CBC Encryption + MD5 Authentication", + esp3desmd5_attach, esp3desmd5_init, esp3desmd5_zeroize, + esp3desmd5_input, esp3desmd5_output, }, +}; + +struct xformsw *xformswNXFORMSW = &xformsw[sizeof(xformsw)/sizeof(xformsw[0])]; + +unsigned char ipseczeroes[IPSEC_ZEROES_SIZE]; /* zeroes! */ + +static char *ipspkernfs = NULL; +static int ipspkernfs_len = 0; +int ipspkernfs_dirty = 1; + +/* + * 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! + */ + +struct tdb * +gettdb(u_long spi, struct in_addr dst) +{ + int hashval; + struct tdb *tdbp; + + hashval = (spi+dst.s_addr) % TDB_HASHMOD; + + for (tdbp = tdbh[hashval]; tdbp; tdbp = tdbp->tdb_hnext) + if ((tdbp->tdb_spi == spi) && (tdbp->tdb_dst.s_addr == dst.s_addr)) + break; + + return tdbp; +} + +void +puttdb(struct tdb *tdbp) +{ + int hashval; + hashval = ((tdbp->tdb_spi + tdbp->tdb_dst.s_addr) % TDB_HASHMOD); + tdbp->tdb_hnext = tdbh[hashval]; + tdbh[hashval] = tdbp; + ipspkernfs_dirty = 1; +} + +int +tdb_delete(struct tdb *tdbp, int delchain) +{ + struct tdb *tdbpp; + int hashval; + + hashval = ((tdbp->tdb_spi + tdbp->tdb_dst.s_addr) % TDB_HASHMOD); + + if (tdbh[hashval] == tdbp) + { + tdbpp = tdbp; + tdbh[hashval] = tdbp->tdb_hnext; + } + else + for (tdbpp = tdbh[hashval]; tdbpp != NULL; tdbpp = tdbpp->tdb_hnext) + if (tdbpp->tdb_hnext == tdbp) + { + tdbpp->tdb_hnext = tdbp->tdb_hnext; + tdbpp = tdbp; + } + + if (tdbp != tdbpp) + return EINVAL; /* Should never happen */ + + ipspkernfs_dirty = 1; + tdbpp = tdbp->tdb_onext; + (*(tdbp->tdb_xform->xf_zeroize))(tdbp); + FREE(tdbp, M_TDB); + if (delchain && tdbpp) + return tdb_delete(tdbpp, delchain); + else + return 0; +} + +int +tdb_init(struct tdb *tdbp, struct mbuf *m) +{ + int alg; + struct encap_msghdr *em; + struct xformsw *xsp; + + em = mtod(m, struct encap_msghdr *); + alg = em->em_alg; + + for (xsp = xformsw; xsp < xformswNXFORMSW; xsp++) + if (xsp->xf_type == alg) + return (*(xsp->xf_init))(tdbp, xsp, m); + + printf("tdbinit: no alg %d for spi %x, addr %x\n", alg, tdbp->tdb_spi, ntohl(tdbp->tdb_dst.s_addr)); + + m_freem(m); + return EINVAL; +} + + +int +ipsp_kern(int off, char **bufp, int len) +{ + struct tdb *tdbp; + int i, k; + char *b, buf[512]; + + if (off != 0) + return 0; + + if ((!ipspkernfs_dirty) && (ipspkernfs)) + { + *bufp = ipspkernfs; + return ipspkernfs_len; + } + 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 (strlen(tdbp->tdb_xform->xf_name) >= 200) + return 0; + + b = (char *)&(tdbp->tdb_dst.s_addr); + k += sprintf(buf, + "SPI=%x, destination=%d.%d.%d.%d, interface=%s\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_rcvif ? tdbp->tdb_rcvif->if_xname : "none"), + 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)); + } + + if (k == 0) + return 0; + + MALLOC(ipspkernfs, char *, k + 1, M_XDATA, M_DONTWAIT); + if (!ipspkernfs) + return 0; + + ipspkernfs_len = k + 1; + + 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); + k += sprintf(ipspkernfs + k, + "SPI=%x, destination=%d.%d.%d.%d, interface=%s\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_rcvif ? tdbp->tdb_rcvif->if_xname : "none"), + 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 ipspkernfs_len; +} diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h new file mode 100644 index 00000000000..85e122a752e --- /dev/null +++ b/sys/netinet/ip_ipsp.h @@ -0,0 +1,139 @@ +/* + * 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. + */ + +/* + * IPSP global definitions. + */ + +struct tdb /* tunnel descriptor block */ +{ + struct tdb *tdb_hnext; /* next in hash chain */ + struct tdb *tdb_onext; /* next in output */ + struct tdb *tdb_inext; /* next in input (prev!) */ + u_long tdb_spi; /* SPI to use */ + struct in_addr tdb_dst; /* dest address for this SPI */ + struct ifnet *tdb_rcvif; /* related rcv encap interface */ + struct xformsw *tdb_xform; /* transformation to use */ + caddr_t tdb_xdata; /* transformation data (opaque) */ +}; + +#define TDB_HASHMOD 257 + +struct xformsw +{ + u_short xf_type; /* Unique ID of xform */ + u_short xf_flags; /* flags (see below) */ + char *xf_name; /* human-readable name */ + int (*xf_attach)(void); /* called at config time */ + int (*xf_init)(struct tdb *, struct xformsw *, struct mbuf *); /* xform initialization */ + int (*xf_zeroize)(struct tdb *); /* termination */ + struct mbuf *(*xf_input)(struct mbuf *, struct tdb *); /* called when packet received */ + int (*xf_output)(struct mbuf *, struct sockaddr_encap *, struct tdb *, struct mbuf **); /* called when packet sent */ +}; + +#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 XFT_AUTH 0x0001 +#define XFT_CONF 0x0100 + +#define IPSEC_ZEROES_SIZE 64 + +#ifdef IPSEC_IPSP_C +#if BYTE_ORDER == LITTLE_ENDIAN +inline u_int64_t +htonq(u_int64_t q) +{ + register u_int32_t u, l; + u = q >> 32; + l = (u_int32_t) q; + + return htonl(u) | ((u_int64_t)htonl(l) << 32); +} + +#define ntohq(_x) htonq(_x) + +#elif BYTE_ORDER == BIG_ENDIAN + +#define htonq(_x) (_x) +#define ntohq(_x) htonq(_x) + +#else +#error "Please fix <machine/endian.h>" +#endif +#else +u_int64_t htonq(u_int64_t); +#define ntohq(_x) htonq(_x) +extern unsigned char ipseczeroes[IPSEC_ZEROES_SIZE]; +#endif + +#ifdef _KERNEL +#undef ENCDEBUG +extern int encdebug; + +struct tdb *tdbh[TDB_HASHMOD]; +extern struct xformsw xformsw[], *xformswNXFORMSW; + +extern struct tdb *gettdb(u_long, struct in_addr); +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 **); +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 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 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 *); + +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 *); +#endif diff --git a/sys/netinet/ip_md5.h b/sys/netinet/ip_md5.h new file mode 100644 index 00000000000..2e102c8fcdc --- /dev/null +++ b/sys/netinet/ip_md5.h @@ -0,0 +1,71 @@ +/* GLOBAL.H - RSAREF types and constants + */ + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. + The following makes PROTOTYPES default to 0 if it has not already + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 1 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. + If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ + +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + + +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void realMD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST + ((MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); + +#define _MD5_H_ diff --git a/sys/netinet/ip_md5c.c b/sys/netinet/ip_md5c.c new file mode 100644 index 00000000000..80eae87b3c7 --- /dev/null +++ b/sys/netinet/ip_md5c.c @@ -0,0 +1,383 @@ +/* + * The rest of the code is derived from MD5C.C by RSADSI. Minor cosmetic + * changes to accomodate it in the kernel by ji. + */ + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* + * Additions by JI + * + * HAVEMEMCOPY is defined if mem* routines are available + * + * HAVEHTON is defined if htons() and htonl() can be used + * for big/little endian conversions + * + */ + +#include <sys/param.h> +#include <netinet/ip_md5.h> +#include <string.h> + +#define HAVEBCOPY + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); + +#if BYTE_ORDER == LITTLE_ENDIAN +#define Encode MD5_memcpy +#define Decode MD5_memcpy +#else +static void Encode PROTO_LIST + ((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST + ((UINT4 *, unsigned char *, unsigned int)); +#endif + +#ifdef HAVEMEMCOPY +#include <memory.h> +#define MD5_memcpy memcpy +#define MD5_memset memset +#else +#ifdef HAVEBCOPY +#define MD5_memcpy(_a,_b,_c) bcopy((_b),(_a),(_c)) +#define MD5_memset(_a,_b,_c) bzero((_a),(_c)) +#else +static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); +#endif +#endif +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void realMD5Init (context) +MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void MD5Update (context, input, inputLen) +MD5_CTX *context; /* context */ +unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. +*/ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. +*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + if (digest != NULL) /* Bill Simpson's padding */ + { + /* store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); + } +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +#if BYTE_ORDER != LITTLE_ENDIAN + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +#endif + +#ifndef HAVEMEMCOPY +#ifndef HAVEBCOPY +/* Note: Replace "for loop" with standard memcpy if possible. + */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} +#endif +#endif + diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c index 7eeb38ee6ad..ddfe57f6d3a 100644 --- a/sys/netinet/ip_mroute.c +++ b/sys/netinet/ip_mroute.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_mroute.c,v 1.4 1996/05/10 12:31:19 deraadt Exp $ */ +/* $OpenBSD: ip_mroute.c,v 1.5 1997/02/20 01:08:04 deraadt Exp $ */ /* $NetBSD: ip_mroute.c,v 1.27 1996/05/07 02:40:50 thorpej Exp $ */ /* @@ -42,6 +42,10 @@ #include <machine/stdarg.h> +#ifdef IPSEC +#include <dev/rndvar.h> +#endif + #define IP_MULTICASTOPTS 0 #define M_PULLUP(m, len) \ do { \ @@ -1393,7 +1397,11 @@ encap_send(ip, vifp, m) */ ip_copy = mtod(mb_copy, struct ip *); *ip_copy = multicast_encap_iphdr; +#ifdef IPSEC + get_random_bytes((void *)&(ip_copy->ip_id), sizeof(ip_copy->ip_id)); +#else ip_copy->ip_id = htons(ip_id++); +#endif ip_copy->ip_len = len; ip_copy->ip_src = vifp->v_lcl_addr; ip_copy->ip_dst = vifp->v_rmt_addr; @@ -1439,16 +1447,19 @@ ipip_input(m, va_alist) register int s; register struct ifqueue *ifq; register struct vif *vifp; + int isencaped = 0; va_list ap; va_start(ap, m); hlen = va_arg(ap, int); va_end(ap); +#ifndef IPSEC if (!have_encap_tunnel) { rip_input(m); return; } +#endif /* * dump the packet if it's not to a multicast destination or if @@ -1458,6 +1469,10 @@ ipip_input(m, va_alist) * at most one tunnel with the remote site). */ if (!IN_MULTICAST(((struct ip *)((char *)ip + hlen))->ip_dst.s_addr)) { +#ifdef IPSEC + isencaped = 1; + goto acceptedhere; +#endif ++mrtstat.mrts_bad_tunnel; m_freem(m); return; @@ -1485,10 +1500,16 @@ ipip_input(m, va_alist) } else vifp = last_encap_vif; +acceptedhere: m->m_data += hlen; m->m_len -= hlen; m->m_pkthdr.len -= hlen; +#ifdef IPSEC + if (isencaped == 0) + m->m_pkthdr.rcvif = vifp->v_ifp; +#else m->m_pkthdr.rcvif = vifp->v_ifp; +#endif ifq = &ipintrq; s = splimp(); if (IF_QFULL(ifq)) { diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 2b31d8dd9f2..0bfe9cce3f7 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_output.c,v 1.6 1996/07/29 02:34:31 downsj Exp $ */ +/* $OpenBSD: ip_output.c,v 1.7 1997/02/20 01:08:06 deraadt Exp $ */ /* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */ /* @@ -61,6 +61,13 @@ #include <machine/stdarg.h> +#ifdef IPSEC +#include <net/encap.h> +#include <netinet/ip_ipsp.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> +#endif + static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *)); static void ip_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in *)); @@ -96,6 +103,11 @@ ip_output(m0, va_alist) int flags; struct ip_moptions *imo; va_list ap; +#ifdef IPSEC + struct mbuf *mp; + struct udphdr *udp; + struct tcphdr *tcp; +#endif va_start(ap, m0); opt = va_arg(ap, struct mbuf *); @@ -127,6 +139,174 @@ ip_output(m0, va_alist) } else { hlen = ip->ip_hl << 2; } + +#ifdef IPSEC + /* + * Check if the packet needs encapsulation + */ + if (!(flags & IP_ENCAPSULATED)) { + struct route_enc { + struct rtentry *re_rt; + struct sockaddr_encap re_dst; + } re0, *re = &re0; + struct sockaddr_encap *dst, *gw; + struct tdb *tdb; + + 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; + dst->sen_ip_src = ip->ip_src; + dst->sen_ip_dst = ip->ip_dst; + dst->sen_proto = ip->ip_p; + + if ((m->m_len < hlen + 2*sizeof(u_int16_t)) && + ((m = m_pullup(m, hlen + 2*sizeof(u_int16_t))) == 0)) + goto bad; + + switch (ip->ip_p) { + case IPPROTO_TCP: + udp = (struct udphdr *) (mtod(m, u_char *) + hlen); + dst->sen_sport = ntohs(udp->uh_sport); + dst->sen_dport = ntohs(udp->uh_dport); + break; + case IPPROTO_UDP: + 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); + if (re->re_rt == NULL) + goto no_encap; + + 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"); +#endif ENCDEBUG + m_freem(m); + RTFREE(re->re_rt); + return EHOSTUNREACH; + } + + ifp = re->re_rt->rt_ifp; + + if (ip->ip_src.s_addr == INADDR_ANY) { + struct sockaddr_encap *sen; + struct sockaddr_in *sinp; + + if (ifp->if_addrlist.tqh_first) + sen = (struct sockaddr_encap *) + ifp->if_addrlist.tqh_first->ifa_addr; + else { +#ifdef ENCDEBUG + if (encdebug) + printf("ip_output: interface %s has no default address\n", + ifp->if_xname); +#endif ENCDEBUG + return ENXIO; + } + + if (sen->sen_family != AF_ENCAP) { +#ifdef ENCDEBUG + if (encdebug) + printf("ip_output: %s does not have AF_ENCAP address\n", + ifp->if_xname); +#endif ENCDEBUG + m_freem(m); + RTFREE(re->re_rt); + return EHOSTDOWN; + } + + if (sen->sen_type != SENT_DEFIF) { +#ifdef ENCDEBUG + if (encdebug) + printf("ip_output: %s does not have SENT_DEFIF address\n", + ifp->if_xname); +#endif ENCDEBUG + m_freem(m); + RTFREE(re->re_rt); + return EHOSTDOWN; + } + sinp = (struct sockaddr_in *)&(sen->sen_dfl); + ip->ip_src = sinp->sin_addr; + } + + if (hlen > sizeof (struct ip)) { /* XXX IPOPT */ + ip_stripoptions(m, (struct mbuf *)0); + hlen = sizeof (struct ip); + } + +#ifdef ENCDEBUG + if (encdebug) + printf("ip_output: encapsulating %x->%x through %x->%x\n", + ip->ip_src.s_addr, ip->ip_dst.s_addr, + gw->sen_ipsp_src, gw->sen_ipsp_dst); +#endif + ip->ip_len = htons((u_short)ip->ip_len); + ip->ip_off = htons((u_short)ip->ip_off); + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m, hlen); + + /* + * At this point we have an IPSP "gateway" (tunnel) spec. + * Use the destination of the tunnel and the SPI to + * look up the necessary Tunnel Control Block. Look it up, + * and then pass it, along with the packet and the gw, + * to the appropriate transformation. + */ + + tdb = (struct tdb *) gettdb(gw->sen_ipsp_spi, gw->sen_ipsp_dst); + +#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); +#endif ENCDEBUG + + while (tdb && tdb->tdb_xform) { + m0 = NULL; +#ifdef ENCDEBUG + if (encdebug) + printf("ip_output: calling %s\n", + tdb->tdb_xform->xf_name); +#endif ENCDEBUG + error = (*(tdb->tdb_xform->xf_output))(m, gw, tdb, &mp); + if (mp == NULL) + error = EFAULT; + if (error) { + RTFREE(re->re_rt); + return error; + } + tdb = tdb->tdb_onext; + m = mp; + } + + /* + * At this point, mp is pointing to an mbuf chain with the + * processed packet. Call ourselves recursively, but + * bypass the encap code. + */ + + RTFREE(re->re_rt); + + ip = mtod(m, struct ip *); + NTOHS(ip->ip_len); + NTOHS(ip->ip_off); + + return ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, NULL); + +no_encap: + if (re->re_rt) + RTFREE(re->re_rt); + } +#endif IPSEC + /* * Route packet. */ diff --git a/sys/netinet/ip_sha1.c b/sys/netinet/ip_sha1.c new file mode 100644 index 00000000000..c90a61d0033 --- /dev/null +++ b/sys/netinet/ip_sha1.c @@ -0,0 +1,166 @@ +/* +SHA-1 in C +By Steve Reid <steve@edmweb.com> +100% Public Domain + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#define SHA1HANDSOFF + +#include <sys/param.h> +#include <netinet/ip_sha1.h> +#include <string.h> + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(unsigned long state[5], unsigned char buffer[64]) +{ +unsigned long a, b, c, d, e; +typedef union { + unsigned char c[64]; + unsigned long l[16]; +} CHAR64LONG16; +CHAR64LONG16* block; +#ifdef SHA1HANDSOFF +static unsigned char workspace[64]; + block = (CHAR64LONG16*)workspace; + bcopy(buffer, block, 64); +#else + block = (CHAR64LONG16*)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) +{ +unsigned int i; +unsigned long j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) context->count[1] += (len>>29)+1; + j = (j >> 3) & 63; + if ((j + len) > 63) { + bcopy(data, &context->buffer[j], (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + bcopy(&data[i], &context->buffer[j], len - i); +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ +unsigned long i, j; +unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1Update(context, (unsigned char *)"\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + i = j = 0; + bzero(context->buffer, 64); + bzero(context->state, 20); + bzero(context->count, 8); + bzero(&finalcount, 8); +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ + SHA1Transform(context->state, context->buffer); +#endif +} diff --git a/sys/netinet/ip_sha1.h b/sys/netinet/ip_sha1.h new file mode 100644 index 00000000000..e4fd065782c --- /dev/null +++ b/sys/netinet/ip_sha1.h @@ -0,0 +1,16 @@ +/* +SHA-1 in C +By Steve Reid <steve@edmweb.com> +100% Public Domain +*/ + +typedef struct { + unsigned long state[5]; + unsigned long count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Transform(unsigned long 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); diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index c79623bd0a8..743172b3cf9 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_var.h,v 1.4 1997/01/26 01:23:44 tholo Exp $ */ +/* $OpenBSD: ip_var.h,v 1.5 1997/02/20 01:08:09 deraadt Exp $ */ /* $NetBSD: ip_var.h,v 1.16 1996/02/13 23:43:20 christos Exp $ */ /* @@ -154,6 +154,10 @@ struct ipstat { #define IP_ROUTETOIF SO_DONTROUTE /* bypass routing tables */ #define IP_ALLOWBROADCAST SO_BROADCAST /* can send broadcast packets */ +#ifdef IPSEC +#define IP_ENCAPSULATED 0x0800 /* encapsulated already */ +#endif + struct ipstat ipstat; LIST_HEAD(ipqhead, ipq) ipq; /* ip reass. queue */ u_int16_t ip_id; /* ip packet ctr, for ids */ diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h index 02e4a1dc8e0..63743b5102d 100644 --- a/sys/sys/malloc.h +++ b/sys/sys/malloc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: malloc.h,v 1.8 1997/02/13 18:11:04 kstailey Exp $ */ +/* $OpenBSD: malloc.h,v 1.9 1997/02/20 01:07:34 deraadt Exp $ */ /* $NetBSD: malloc.h,v 1.23 1996/04/05 04:52:52 mhitch Exp $ */ /* @@ -123,7 +123,9 @@ #define M_AFS 70 /* Andrew File System */ #define M_ADOSFSBITMAP 71 /* adosfs bitmap */ #define M_EXT2FSNODE 72 /* EXT2FS vnode private part */ -#define M_PFIL 73 /* packer filter */ +#define M_PFIL 73 /* packer filter */ +#define M_TDB 75 /* Transforms database */ +#define M_XDATA 76 /* IPsec data */ #define M_TEMP 84 /* misc temporary data buffers */ #define M_LAST 85 /* Must be last type + 1 */ @@ -202,7 +204,10 @@ "adosfs bitmap", /* 71 M_ADOSFSBITMAP */ \ "EXT2FS node", /* 72 M_EXT2FSNODE */ \ "pfil", /* 73 M_PFIL */ \ - NULL, NULL, NULL, NULL, NULL, \ + NULL, \ + "tdb", /* 75 M_TDB */ \ + "xform_data", /* 76 M_XDATA */ \ + NULL, NULL, \ NULL, NULL, NULL, NULL, NULL, \ "temp", /* 84 M_TEMP */ \ } diff --git a/sys/sys/socket.h b/sys/sys/socket.h index 2069279f6c5..d9e2c6f390d 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -1,4 +1,4 @@ -/* $OpenBSD: socket.h,v 1.8 1996/12/12 13:50:53 mickey Exp $ */ +/* $OpenBSD: socket.h,v 1.9 1997/02/20 01:07:35 deraadt Exp $ */ /* $NetBSD: socket.h,v 1.14 1996/02/09 18:25:36 christos Exp $ */ /* @@ -125,8 +125,8 @@ struct linger { #define AF_ISDN 26 /* Integrated Services Digital Network*/ #define AF_E164 AF_ISDN /* CCITT E.164 recommendation */ #define AF_NATM 27 /* native ATM access */ - -#define AF_MAX 28 +#define AF_ENCAP 28 +#define AF_MAX 29 /* * Structure used by kernel to store most @@ -180,7 +180,7 @@ struct sockproto { #define PF_PIP pseudo_AF_PIP #define PF_ISDN AF_ISDN #define PF_NATM AF_NATM - +#define PF_ENCAP AF_ENCAP #define PF_MAX AF_MAX /* @@ -221,6 +221,7 @@ struct sockproto { { "sip", CTLTYPE_NODE }, \ { "pip", CTLTYPE_NODE }, \ { "natm", CTLTYPE_NODE }, \ + { "ipsec", CTLTYPE_NODE }, \ } /* |