Beispiel #1
0
/*
 * ESP input callback from the crypto driver.
 */
static int
esp_input_cb(struct cryptop *crp)
{
	u_int8_t lastthree[3], aalg[AH_HMAC_HASHLEN];
	int s, hlen, skip, protoff, error;
	struct mbuf *m;
	struct cryptodesc *crd;
	struct auth_hash *esph;
	struct enc_xform *espx;
	struct tdb_crypto *tc;
	struct m_tag *mtag;
	struct secasvar *sav;
	struct secasindex *saidx;
	caddr_t ptr;

	crd = crp->crp_desc;
	KASSERT(crd != NULL, ("esp_input_cb: null crypto descriptor!"));

	tc = (struct tdb_crypto *) crp->crp_opaque;
	KASSERT(tc != NULL, ("esp_input_cb: null opaque crypto data area!"));
	skip = tc->tc_skip;
	protoff = tc->tc_protoff;
	mtag = (struct m_tag *) tc->tc_ptr;
	m = (struct mbuf *) crp->crp_buf;

	s = splnet();

	sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
	if (sav == NULL) {
		espstat.esps_notdb++;
		DPRINTF(("esp_input_cb: SA expired while in crypto "
		    "(SA %s/%08lx proto %u)\n", ipsec_address(&tc->tc_dst),
		    (u_long) ntohl(tc->tc_spi), tc->tc_proto));
		error = ENOBUFS;		/*XXX*/
		goto bad;
	}

	saidx = &sav->sah->saidx;
	KASSERT(saidx->dst.sa.sa_family == AF_INET ||
		saidx->dst.sa.sa_family == AF_INET6,
		("ah_input_cb: unexpected protocol family %u",
		 saidx->dst.sa.sa_family));

	esph = sav->tdb_authalgxform;
	espx = sav->tdb_encalgxform;

	/* Check for crypto errors */
	if (crp->crp_etype) {
		/* Reset the session ID */
		if (sav->tdb_cryptoid != 0)
			sav->tdb_cryptoid = crp->crp_sid;

		if (crp->crp_etype == EAGAIN) {
			KEY_FREESAV(&sav);
			splx(s);
			return crypto_dispatch(crp);
		}

		espstat.esps_noxform++;
		DPRINTF(("esp_input_cb: crypto error %d\n", crp->crp_etype));
		error = crp->crp_etype;
		goto bad;
	}

	/* Shouldn't happen... */
	if (m == NULL) {
		espstat.esps_crypto++;
		DPRINTF(("esp_input_cb: bogus returned buffer from crypto\n"));
		error = EINVAL;
		goto bad;
	}
	espstat.esps_hist[sav->alg_enc]++;

	/* If authentication was performed, check now. */
	if (esph != NULL) {
		/*
		 * If we have a tag, it means an IPsec-aware NIC did
		 * the verification for us.  Otherwise we need to
		 * check the authentication calculation.
		 */
		ahstat.ahs_hist[sav->alg_auth]++;
		if (mtag == NULL) {
			/* Copy the authenticator from the packet */
			m_copydata(m, m->m_pkthdr.len - esph->authsize,
				esph->authsize, aalg);

			ptr = (caddr_t) (tc + 1);

			/* Verify authenticator */
			if (bcmp(ptr, aalg, esph->authsize) != 0) {
				DPRINTF(("esp_input_cb: "
		    "authentication hash mismatch for packet in SA %s/%08lx\n",
				    ipsec_address(&saidx->dst),
				    (u_long) ntohl(sav->spi)));
				espstat.esps_badauth++;
				error = EACCES;
				goto bad;
			}
		}

		/* Remove trailing authenticator */
		m_adj(m, -(esph->authsize));
	}

	/* Release the crypto descriptors */
	free(tc, M_XDATA), tc = NULL;
	crypto_freereq(crp), crp = NULL;

	/*
	 * Packet is now decrypted.
	 */
	m->m_flags |= M_DECRYPTED;

	/* Determine the ESP header length */
	if (sav->flags & SADB_X_EXT_OLD)
		hlen = sizeof (struct esp) + sav->ivlen;
	else
		hlen = sizeof (struct newesp) + sav->ivlen;

	/* Remove the ESP header and IV from the mbuf. */
	error = m_striphdr(m, skip, hlen);
	if (error) {
		espstat.esps_hdrops++;
		DPRINTF(("esp_input_cb: bad mbuf chain, SA %s/%08lx\n",
		    ipsec_address(&sav->sah->saidx.dst),
		    (u_long) ntohl(sav->spi)));
		goto bad;
	}

	/* Save the last three bytes of decrypted data */
	m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree);

	/* Verify pad length */
	if (lastthree[1] + 2 > m->m_pkthdr.len - skip) {
		espstat.esps_badilen++;
		DPRINTF(("esp_input_cb: invalid padding length %d "
			 "for %u byte packet in SA %s/%08lx\n",
			 lastthree[1], m->m_pkthdr.len - skip,
			 ipsec_address(&sav->sah->saidx.dst),
			 (u_long) ntohl(sav->spi)));
		error = EINVAL;
		goto bad;
	}

	/* Verify correct decryption by checking the last padding bytes */
	if ((sav->flags & SADB_X_EXT_PMASK) != SADB_X_EXT_PRAND) {
		if (lastthree[1] != lastthree[0] && lastthree[1] != 0) {
			espstat.esps_badenc++;
			DPRINTF(("esp_input_cb: decryption failed "
				"for packet in SA %s/%08lx\n",
				ipsec_address(&sav->sah->saidx.dst),
				(u_long) ntohl(sav->spi)));
DPRINTF(("esp_input_cb: %x %x\n", lastthree[0], lastthree[1]));
			error = EINVAL;
			goto bad;
		}
	}

	/* Trim the mbuf chain to remove trailing authenticator and padding */
	m_adj(m, -(lastthree[1] + 2));

	/* Restore the Next Protocol field */
	m_copyback(m, protoff, sizeof (u_int8_t), lastthree + 2);

	IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, mtag);

	KEY_FREESAV(&sav);
	splx(s);
	return error;
bad:
	if (sav)
		KEY_FREESAV(&sav);
	splx(s);
	if (m != NULL)
		m_freem(m);
	if (tc != NULL)
		free(tc, M_XDATA);
	if (crp != NULL)
		crypto_freereq(crp);
	return error;
}
+++ sys/netipsec/xform_ipcomp.c	29 Mar 2011 19:24:04 -0000
@@ -326,6 +326,14 @@ ipcomp_input_cb(struct cryptop *crp)
 	/* Keep the next protocol field */
 	addr = (uint8_t*) mtod(m, struct ip *) + skip;
 	nproto = ((struct ipcomp *) addr)->comp_nxt;
+	if (nproto == IPPROTO_IPCOMP || nproto == IPPROTO_AH || nproto == IPPROTO_ESP) {
+		IPCOMP_STATINC(IPCOMP_STAT_HDROPS);
+		DPRINTF(("ipcomp_input_cb: nested ipcomp, IPCA %s/%08lx\n",
+			 ipsec_address(&sav->sah->saidx.dst),
+			 (u_long) ntohl(sav->spi)));
+		error = EINVAL;
+		goto bad;
+	}
 
 	/* Remove the IPCOMP header */
 	error = m_striphdr(m, skip, hlen);
-------------- next part --------------
Index: sys/netinet6/ipcomp_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/ipcomp_input.c,v
retrieving revision 1.36
diff -u -u -p -r1.36 ipcomp_input.c
--- sys/netinet6/ipcomp_input.c	5 May 2008 13:41:30 -0000	1.36
+++ sys/netinet6/ipcomp_input.c	29 Mar 2011 19:19:00 -0000
@@ -148,6 +148,13 @@ ipcomp4_input(m, va_alist)
 	ipcomp = mtod(md, struct ipcomp *);
 	ip = mtod(m, struct ip *);
 	nxt = ipcomp->comp_nxt;
+	if (nxt == IPPROTO_IPCOMP || nxt == IPPROTO_AH || nxt == IPPROTO_ESP) {
+		/* nested ipcomp - possible attack, not likely useful */
+		ipseclog((LOG_DEBUG, "IPv4 IPComp input: nested ipcomp "
Beispiel #3
0
/*
 * Potentially decap ESP in UDP frame.  Check for an ESP header.
 * If present, strip the UDP header and push the result through IPSec.
 *
 * Returns error if mbuf consumed and/or processed, otherwise 0.
 */
int
udp_ipsec_input(struct mbuf *m, int off, int af)
{
	union sockaddr_union dst;
	struct secasvar *sav;
	struct udphdr *udp;
	struct ip *ip;
	uint32_t spi;
	int hlen;

	/*
	 * Just return if packet doesn't have enough data.
	 * We need at least [IP header + UDP header + ESP header].
	 * NAT-Keepalive packet has only one byte of payload, so it
	 * by default will not be processed.
	 */
	if (m->m_pkthdr.len < off + sizeof(struct esp))
		return (0);

	m_copydata(m, off, sizeof(uint32_t), (caddr_t)&spi);
	if (spi == 0)	/* Non-ESP marker. */
		return (0);

	/*
	 * Find SA and check that it is configured for UDP
	 * encapsulation.
	 */
	bzero(&dst, sizeof(dst));
	dst.sa.sa_family = af;
	switch (af) {
#ifdef INET
	case AF_INET:
		dst.sin.sin_len = sizeof(struct sockaddr_in);
		ip = mtod(m, struct ip *);
		ip->ip_p = IPPROTO_ESP;
		off = offsetof(struct ip, ip_p);
		hlen = ip->ip_hl << 2;
		dst.sin.sin_addr = ip->ip_dst;
		break;
#endif
#ifdef INET6
	case AF_INET6:
		/* Not yet */
		/* FALLTHROUGH */
#endif
	default:
		ESPSTAT_INC(esps_nopf);
		m_freem(m);
		return (EPFNOSUPPORT);
	}

	sav = key_allocsa(&dst, IPPROTO_ESP, spi);
	if (sav == NULL) {
		ESPSTAT_INC(esps_notdb);
		m_freem(m);
		return (ENOENT);
	}
	udp = mtodo(m, hlen);
	if (sav->natt == NULL ||
	    sav->natt->sport != udp->uh_sport ||
	    sav->natt->dport != udp->uh_dport) {
		/* XXXAE: should we check source address? */
		ESPSTAT_INC(esps_notdb);
		key_freesav(&sav);
		m_freem(m);
		return (ENOENT);
	}
	/*
	 * Remove the UDP header
	 * Before:
	 *   <--- off --->
	 *   +----+------+-----+
	 *   | IP |  UDP | ESP |
	 *   +----+------+-----+
	 *        <-skip->
	 * After:
	 *          +----+-----+
	 *          | IP | ESP |
	 *          +----+-----+
	 *   <-skip->
	 */
	m_striphdr(m, hlen, sizeof(*udp));
	/*
	 * We cannot yet update the cksums so clear any h/w cksum flags
	 * as they are no longer valid.
	 */
	if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID)
		m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
	/*
	 * We can update ip_len and ip_sum here, but ipsec4_input_cb()
	 * will do this anyway, so don't touch them here.
	 */
	ESPSTAT_INC(esps_input);
	(*sav->tdb_xform->xf_input)(m, sav, hlen, off);
	return (EINPROGRESS);	/* Consumed by IPsec. */
}