summaryrefslogtreecommitdiff
path: root/sys/netinet
diff options
context:
space:
mode:
authorAngelos D. Keromytis <angelos@cvs.openbsd.org>1999-12-08 06:16:57 +0000
committerAngelos D. Keromytis <angelos@cvs.openbsd.org>1999-12-08 06:16:57 +0000
commitf914bf21936392dc7a7d16c162a5c31094c1466c (patch)
treee2e8a9087aa04182782a28d2c4ebb1b2c35109fb /sys/netinet
parentdcbd7832484eb063dee61ac00a9d3700ab1f5eb3 (diff)
IPv6 header handling, improve IPv4 option handling support.
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/ip_ah_new.c399
-rw-r--r--sys/netinet/ip_ipsp.h4
2 files changed, 396 insertions, 7 deletions
diff --git a/sys/netinet/ip_ah_new.c b/sys/netinet/ip_ah_new.c
index 9fdf14d5922..963bb18dc75 100644
--- a/sys/netinet/ip_ah_new.c
+++ b/sys/netinet/ip_ah_new.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ah_new.c,v 1.31 1999/12/08 02:26:18 angelos Exp $ */
+/* $OpenBSD: ip_ah_new.c,v 1.32 1999/12/08 06:16:56 angelos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
@@ -199,14 +199,20 @@ ah_new_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
unsigned char calcauth[AH_MAX_HASHLEN];
int len, count, off, roff;
struct mbuf *m0, *m1;
+ unsigned char *ptr;
union authctx ctx;
struct ah_new ah;
#ifdef INET
- unsigned char *ptr;
struct ip ipo;
#endif /* INET */
+#ifdef INET6
+ struct ip6_ext *ip6e;
+ struct ip6_hdr ip6;
+ int last;
+#endif /* INET6 */
+
/* Save the AH header, we use it throughout */
m_copydata(m, skip, sizeof(ah), (unsigned char *) &ah);
@@ -280,7 +286,7 @@ ah_new_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
#ifdef INET
case AF_INET:
/*
- * This is the most painless way of dealing with IPv4 header
+ * This is the least painful way of dealing with IPv4 header
* and option processing -- just make sure they're in
* contiguous memory.
*/
@@ -330,6 +336,15 @@ ah_new_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
case 0x94: /* Router alert */
case 0x95: /* RFC1770 */
ahx->Update(&ctx, ptr + off, ptr[off + 1]);
+ /* Sanity check for zero-length options */
+ if (ptr[off + 1] == 0)
+ {
+ DPRINTF(("ah_new_input(): illegal zero-length IPv4 option %d in SA %s/%08x\n", ptr[off], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return NULL;
+ }
+
off += ptr[off + 1];
break;
@@ -338,13 +353,165 @@ ah_new_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
off += ptr[off + 1];
break;
}
+
+ /* Sanity check */
+ if (off > skip)
+ {
+ DPRINTF(("ah_new_input(): malformed IPv4 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return NULL;
+ }
}
break;
#endif /* INET */
#ifdef INET6
- /* XXX */
+ case AF_INET6: /* Ugly... */
+ /* Copy and "cook" (later on) the IPv6 header */
+ m_copydata(m, 0, sizeof(ip6), (unsigned char *) &ip6);
+
+ /* We don't do IPv6 Jumbograms */
+ if (ip6.ip6_plen == 0)
+ {
+ DPRINTF(("ah_new_input(): unsupported IPv6 jumbogram in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return NULL;
+ }
+
+ ip6.ip6_flow = 0;
+ ip6.ip6_hlim = 0;
+ ip6.ip6_vfc = IPV6_VERSION; /* This resets some bitfields */
+
+ /* Include IPv6 header in authenticator computation */
+ ahx->Update(&ctx, (unsigned char *) &ip6, sizeof(ip6));
+
+ /* Let's deal with the remaining headers (if any) */
+ if (skip - sizeof(struct ip6_hdr) > 0)
+ {
+ if (m->m_len <= skip)
+ {
+ MALLOC(ptr, unsigned char *, skip - sizeof(struct ip6_hdr),
+ M_XDATA, M_WAITOK);
+
+ /* Copy all the protocol headers after the IPv6 header */
+ m_copydata(m, sizeof(struct ip6_hdr),
+ skip - sizeof(struct ip6_hdr), ptr);
+ }
+ else
+ ptr = mtod(m, unsigned char *) + sizeof(struct ip6_hdr);
+ }
+ else
+ break;
+
+ off = ip6.ip6_nxt & 0xff; /* Next header type */
+
+ for (len = 0; len < skip - sizeof(struct ip6_hdr);)
+ switch (off)
+ {
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ ip6e = (struct ip6_ext *) (ptr + len);
+
+ /*
+ * Process the mutable/immutable options -- borrows
+ * heavily from the KAME code.
+ */
+ for (last = len, count = len + sizeof(struct ip6_ext);
+ count < len + ((ip6e->ip6e_len + 1) << 3);)
+ {
+ if (ptr[count] == IP6OPT_PAD1)
+ {
+ count++;
+ continue;
+ }
+
+ /* Sanity check */
+ if (count + sizeof(struct ip6_ext) > len +
+ ((ip6e->ip6e_len + 1) << 3))
+ {
+ DPRINTF(("ah_new_input(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+
+ /* Free, if we allocated */
+ if (m->m_len < skip)
+ {
+ FREE(ptr, M_XDATA);
+ ptr = NULL;
+ }
+ return NULL;
+ }
+
+ /*
+ * If mutable option, calculate authenticator
+ * for all immutable fields so far, then include
+ * a zeroed-out version of this option.
+ */
+ if (ptr[count] & IP6OPT_MUTABLE)
+ {
+ /* Calculate immutables */
+ ahx->Update(&ctx, ptr + last,
+ count + sizeof(struct ip6e_ext) -
+ last);
+ last = count + ptr[count + 1] +
+ sizeof(struct ip6e_ext);
+
+ /* Calculate "zeroed-out" immutables */
+ ahx->Update(&ctx, ipseczeroes, ptr[count + 1] -
+ sizeof(struct ip6_ext));
+ }
+
+ count += ptr[count + 1] + sizeof(struct ip6_ext);
+
+ /* Sanity check */
+ if (count > skip - sizeof(struct ip6_hdr))
+ {
+ DPRINTF(("ah_new_input(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+
+ /* Free, if we allocated */
+ if (m->m_len < skip)
+ {
+ FREE(ptr, M_XDATA);
+ ptr = NULL;
+ }
+ return NULL;
+ }
+ }
+
+ /* Include any trailing immutable options */
+ ahx->Update(&ctx, ptr + last,
+ len + ((ip6e->ip6e_len + 1) << 3) - last);
+
+ len += ((ip6e->ip6e_len + 1) << 3); /* Advance */
+ off = ip6e->ip6e_nxt;
+ break;
+
+ case IPPROTO_ROUTING:
+ ip6e = (struct ip6_ext *) (ptr + len);
+ ahx->Update(&ctx, ptr + len, (ip6e->ip6e_len + 1) << 3);
+ len += ((ip6e->ip6e_len + 1) << 3); /* Advance */
+ off = ip6e->ip6e_nxt;
+ break;
+
+ default:
+ DPRINTF(("ah_new_input(): unexpected IPv6 header type %d in SA %s/%08x\n", off, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ len = skip - sizeof(struct ip6_hdr);
+ break;
+ }
+
+ /* Free, if we allocated */
+ if (m->m_len < skip)
+ {
+ FREE(ptr, M_XDATA);
+ ptr = NULL;
+ }
+
+ break;
#endif /* INET6 */
default:
@@ -496,15 +663,21 @@ ah_new_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
struct auth_hash *ahx = (struct auth_hash *) tdb->tdb_authalgxform;
unsigned char calcauth[AH_MAX_HASHLEN];
int len, off, count;
+ unsigned char *ptr;
struct ah_new *ah;
union authctx ctx;
struct mbuf *mo;
#ifdef INET
- unsigned char *ptr;
struct ip ipo;
#endif /* INET */
+#ifdef INET6
+ struct ip6_ext *ip6e;
+ struct ip6_hdr ip6;
+ int last;
+#endif /* INET6 */
+
ahstat.ahs_output++;
/* Check for replay counter wrap-around in automatic (not manual) keying */
@@ -620,6 +793,76 @@ ah_new_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
ipo.ip_p = IPPROTO_AH;
ipo.ip_len = htons(ntohs(ipo.ip_len) + AH_NEW_FLENGTH);
+ /*
+ * If we have a loose or strict routing option, we are
+ * supposed to use the last address in it as the
+ * destination address in the authenticated IPv4 header.
+ *
+ * Note that this is an issue only with the output routine;
+ * we will correctly process (in the AH input routine) incoming
+ * packets with these options without special consideration.
+ *
+ * We assume that the IP header contains the next hop's address,
+ * and that the last entry in the option is the final
+ * destination's address.
+ */
+ if (skip > sizeof(struct ip))
+ {
+ for (off = sizeof(struct ip); off < skip;)
+ {
+ /* First sanity check for zero-length options */
+ if ((ptr[off] != IPOPT_EOL) && (ptr[off] != IPOPT_NOP) &&
+ (ptr[off + 1] == 0))
+ {
+ DPRINTF(("ah_new_output(): illegal zero-length IPv4 option %d in SA %s/%08x\n", ptr[off], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return EMSGSIZE;
+ }
+
+ switch (ptr[off])
+ {
+ case IPOPT_LSRR:
+ case IPOPT_SSRR:
+ /* Sanity check for length */
+ if (ptr[off + 1] < 2 + sizeof(struct in_addr))
+ {
+ DPRINTF(("ah_new_output(): malformed LSRR or SSRR IPv4 option header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return EMSGSIZE;
+ }
+
+ bcopy(ptr + off + ptr[off + 1] -
+ sizeof(struct in_addr),
+ &(ipo.ip_dst), sizeof(struct in_addr));
+ off = skip;
+ break;
+
+ case IPOPT_EOL:
+ off = skip;
+ break;
+
+ case IPOPT_NOP:
+ off++;
+ break;
+
+ default: /* Some other option, just skip it */
+ off += ptr[off + 1];
+ break;
+ }
+
+ /* Sanity check */
+ if (off > skip)
+ {
+ DPRINTF(("ah_new_output(): malformed IPv4 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return EMSGSIZE;
+ }
+ }
+ }
+
/* Include IP header in authenticator computation */
ahx->Update(&ctx, (unsigned char *) &ipo, sizeof(struct ip));
@@ -652,12 +895,158 @@ ah_new_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
off += ptr[off + 1];
break;
}
+
+ /* Sanity check */
+ if (off > skip)
+ {
+ DPRINTF(("ah_new_output(): malformed IPv4 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return EMSGSIZE;
+ }
}
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
+ /* Copy and "cook" the IPv6 header */
+ m_copydata(m, 0, sizeof(ip6), (unsigned char *) &ip6);
+
+ /* We don't do IPv6 Jumbograms */
+ if (ip6.ip6_plen == 0)
+ {
+ DPRINTF(("ah_new_output(): unsupported IPv6 jumbogram in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return EMSGSIZE;
+ }
+
+ ip6.ip6_flow = 0;
+ ip6.ip6_hlim = 0;
+ ip6.ip6_vfc = IPV6_VERSION; /* This resets some bitfields */
+
+ /*
+ * Note that here we assume that on output, the IPv6 header
+ * and any Type0 Routing Header present have been made to look
+ * like the will at the destination. Note that this is a
+ * different assumption than we made for IPv4 (because of
+ * different option processing in IPv4 and IPv6, and different
+ * code paths from IPv4/IPv6 to here).
+ */
+
+ /* Include IPv6 header in authenticator computation */
+ ahx->Update(&ctx, (unsigned char *) &ip6, sizeof(ip6));
+
+ /* Let's deal with the remaining headers (if any) */
+ if (skip - sizeof(struct ip6_hdr) > 0)
+ {
+ if (m->m_len <= skip)
+ {
+ MALLOC(ptr, unsigned char *,
+ skip - sizeof(struct ip6_hdr), M_XDATA, M_WAITOK);
+
+ /* Copy all the protocol headers after the IPv6 header */
+ m_copydata(m, sizeof(struct ip6_hdr),
+ skip - sizeof(struct ip6_hdr), ptr);
+ }
+ else
+ ptr = mtod(m, unsigned char *) + sizeof(struct ip6_hdr);
+ }
+ else
+ break; /* Done */
+
+ off = ip6.ip6_nxt & 0xff; /* Next header type */
+ for (len = 0; len < skip - sizeof(struct ip6_hdr);)
+ switch (off)
+ {
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ ip6e = (struct ip6_ext *) (ptr + len);
+
+ /*
+ * Process the mutable/immutable options -- borrows
+ * heavily from the KAME code.
+ */
+ for (last = len, count = len + sizeof(struct ip6_ext);
+ count < len + ((ip6e->ip6e_len + 1) << 3);)
+ {
+ if (ptr[count] == IP6OPT_PAD1)
+ {
+ count++;
+ continue;
+ }
+
+ /* Sanity check */
+ if (count + sizeof(struct ip6_ext) > len +
+ ((ip6e->ip6e_len + 1) << 3))
+ {
+ DPRINTF(("ah_new_output(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+
+ /* Free, if we allocated */
+ if (m->m_len < skip)
+ FREE(ptr, M_XDATA);
+ return EMSGSIZE;
+ }
+
+ /*
+ * If mutable option, calculate authenticator
+ * for all immutable fields so far, then include
+ * a zeroed-out version of this option.
+ */
+ if (ptr[count] & IP6OPT_MUTABLE)
+ {
+ /* Calculate immutables */
+ ahx->Update(&ctx, ptr + last, count + 2 - last);
+ last = count + ptr[count + 1];
+
+ /* Calculate "zeroed-out" immutables */
+ ahx->Update(&ctx, ipseczeroes,
+ ptr[count + 1] - 2);
+ }
+
+ count += ptr[count + 1];
+
+ /* Sanity check */
+ if (count > skip - sizeof(struct ip6_hdr))
+ {
+ DPRINTF(("ah_new_output(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+
+ /* Free, if we allocated */
+ if (m->m_len < skip)
+ FREE(ptr, M_XDATA);
+ return EMSGSIZE;
+ }
+ }
+
+ /* Include any trailing immutable options */
+ ahx->Update(&ctx, ptr + last,
+ len + ((ip6e->ip6e_len + 1) << 3) - last);
+
+ len += ((ip6e->ip6e_len + 1) << 3); /* Advance */
+ off = ip6e->ip6e_nxt;
+ break;
+
+ case IPPROTO_ROUTING:
+ ip6e = (struct ip6_ext *) (ptr + len);
+ ahx->Update(&ctx, ptr + len, (ip6e->ip6e_len + 1) << 3);
+ len += ((ip6e->ip6e_len + 1) << 3); /* Advance */
+ off = ip6e->ip6e_nxt;
+ break;
+ }
+
+ /* Free, if we allocated */
+ if (m->m_len < skip)
+ {
+ FREE(ptr, M_XDATA);
+ ptr = NULL;
+ }
+
+ break;
#endif /* INET6 */
default:
diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h
index 992169c9560..e3d9eef94f1 100644
--- a/sys/netinet/ip_ipsp.h
+++ b/sys/netinet/ip_ipsp.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ipsp.h,v 1.48 1999/12/06 07:14:36 angelos Exp $ */
+/* $OpenBSD: ip_ipsp.h,v 1.49 1999/12/08 06:16:56 angelos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
@@ -401,7 +401,7 @@ struct xformsw
#define XFT_AUTH 0x0001
#define XFT_CONF 0x0100
-#define IPSEC_ZEROES_SIZE 64
+#define IPSEC_ZEROES_SIZE 256 /* Larger than an IP6 extension hdr. */
#define IPSEC_KERNFS_BUFSIZE 4096
#if BYTE_ORDER == LITTLE_ENDIAN