Example #1
0
/*
 * IPsec input callback for INET protocols.
 * This routine is called as the transform callback.
 * Takes care of filtering and other sanity checks on
 * the processed packet.
 */
int
ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
    int protoff)
{
	char buf[INET6_ADDRSTRLEN];
	struct ipsec_ctx_data ctx;
	int prot, af, sproto, isr_prot;
	struct ip *ip;
	struct m_tag *mtag;
	struct tdb_ident *tdbi;
	struct secasindex *saidx;
	int error;
#ifdef INET6
#ifdef notyet
	char ip6buf[INET6_ADDRSTRLEN];
#endif
#endif

	IPSEC_ASSERT(m != NULL, ("null mbuf"));
	IPSEC_ASSERT(sav != NULL, ("null SA"));
	IPSEC_ASSERT(sav->sah != NULL, ("null SAH"));
	saidx = &sav->sah->saidx;
	af = saidx->dst.sa.sa_family;
	IPSEC_ASSERT(af == AF_INET, ("unexpected af %u", af));
	sproto = saidx->proto;
	IPSEC_ASSERT(sproto == IPPROTO_ESP || sproto == IPPROTO_AH ||
		sproto == IPPROTO_IPCOMP,
		("unexpected security protocol %u", sproto));

	/* Sanity check */
	if (m == NULL) {
		DPRINTF(("%s: null mbuf", __func__));
		IPSEC_ISTAT(sproto, badkcr);
		KEY_FREESAV(&sav);
		return EINVAL;
	}

	if (skip != 0) {
		/*
		 * Fix IPv4 header
		 * XXXGL: do we need this entire block?
		 */
		if (m->m_len < skip && (m = m_pullup(m, skip)) == NULL) {
			DPRINTF(("%s: processing failed for SA %s/%08lx\n",
			    __func__, ipsec_address(&sav->sah->saidx.dst,
			    buf, sizeof(buf)), (u_long) ntohl(sav->spi)));
			IPSEC_ISTAT(sproto, hdrops);
			error = ENOBUFS;
			goto bad;
		}

		ip = mtod(m, struct ip *);
		ip->ip_len = htons(m->m_pkthdr.len);
		ip->ip_sum = 0;
		ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
	} else {
Example #2
0
/* assumes that ip header and ah header are contiguous on mbuf */
void*
ah4_ctlinput(int cmd, const struct sockaddr *sa, void *v)
{
	struct ip *ip = v;
	struct ah *ah;
	struct icmp *icp;
	struct secasvar *sav;

	if (sa->sa_family != AF_INET ||
		sa->sa_len != sizeof(struct sockaddr_in))
		return NULL; 
	if ((unsigned)cmd >= PRC_NCMDS)
		return NULL;

	if (cmd == PRC_MSGSIZE && ip_mtudisc && ip && ip->ip_v == 4) {
		/*
		 * Check to see if we have a valid SA corresponding to
		 * the address in the ICMP message payload.
		 */
		ah = (struct ah *)((char *)ip + (ip->ip_hl << 2));
		sav = KEY_ALLOCSA((const union sockaddr_union *)sa,
					   	IPPROTO_AH, ah->ah_spi, 0, 0);

		if (sav) {
        	if (sav->state == SADB_SASTATE_MATURE ||
                sav->state == SADB_SASTATE_DYING) {

				/*
				 * Now that we've validated that we are actually 
				 * communicating with the host indicated in the 	
				 * ICMP message, locate the ICMP header, 
				 * recalculate the new MTU, and create the
		 		 * corresponding routing entry.
		 		 */
				icp = (struct icmp *)((char *)ip - 
									  offsetof(struct icmp, icmp_ip));
				icmp_mtudisc(icp, ip->ip_dst);

			}
			KEY_FREESAV(&sav);
		}
Example #3
0
/*
 * ipsec_common_input gets called when an IPsec-protected packet
 * is received by IPv4 or IPv6.  Its job is to find the right SA
 * and call the appropriate transform.  The transform callback
 * takes care of further processing (like ingress filtering).
 */
int
ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
{
	char buf[INET6_ADDRSTRLEN];
	union sockaddr_union dst_address;
	struct secasvar *sav;
	u_int32_t spi;
	int error;
#ifdef INET
#ifdef IPSEC_NAT_T
	struct m_tag *tag;
#endif
#endif

	IPSEC_ISTAT(sproto, input);

	IPSEC_ASSERT(m != NULL, ("null packet"));

	IPSEC_ASSERT(sproto == IPPROTO_ESP || sproto == IPPROTO_AH ||
		sproto == IPPROTO_IPCOMP,
		("unexpected security protocol %u", sproto));

	if ((sproto == IPPROTO_ESP && !V_esp_enable) ||
	    (sproto == IPPROTO_AH && !V_ah_enable) ||
	    (sproto == IPPROTO_IPCOMP && !V_ipcomp_enable)) {
		m_freem(m);
		IPSEC_ISTAT(sproto, pdrops);
		return EOPNOTSUPP;
	}

	if (m->m_pkthdr.len - skip < 2 * sizeof (u_int32_t)) {
		m_freem(m);
		IPSEC_ISTAT(sproto, hdrops);
		DPRINTF(("%s: packet too small\n", __func__));
		return EINVAL;
	}

	/* Retrieve the SPI from the relevant IPsec header */
	if (sproto == IPPROTO_ESP)
		m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi);
	else if (sproto == IPPROTO_AH)
		m_copydata(m, skip + sizeof(u_int32_t), sizeof(u_int32_t),
		    (caddr_t) &spi);
	else if (sproto == IPPROTO_IPCOMP) {
		u_int16_t cpi;
		m_copydata(m, skip + sizeof(u_int16_t), sizeof(u_int16_t),
		    (caddr_t) &cpi);
		spi = ntohl(htons(cpi));
	}

	/*
	 * Find the SA and (indirectly) call the appropriate
	 * kernel crypto routine. The resulting mbuf chain is a valid
	 * IP packet ready to go through input processing.
	 */
	bzero(&dst_address, sizeof (dst_address));
	dst_address.sa.sa_family = af;
	switch (af) {
#ifdef INET
	case AF_INET:
		dst_address.sin.sin_len = sizeof(struct sockaddr_in);
		m_copydata(m, offsetof(struct ip, ip_dst),
		    sizeof(struct in_addr),
		    (caddr_t) &dst_address.sin.sin_addr);
#ifdef IPSEC_NAT_T
		/* Find the source port for NAT-T; see udp*_espdecap. */
		tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL);
		if (tag != NULL)
			dst_address.sin.sin_port = ((u_int16_t *)(tag + 1))[1];
#endif /* IPSEC_NAT_T */
		break;
#endif /* INET */
#ifdef INET6
	case AF_INET6:
		dst_address.sin6.sin6_len = sizeof(struct sockaddr_in6);
		m_copydata(m, offsetof(struct ip6_hdr, ip6_dst),
		    sizeof(struct in6_addr),
		    (caddr_t) &dst_address.sin6.sin6_addr);
		/* We keep addresses in SADB without embedded scope id */
		if (IN6_IS_SCOPE_LINKLOCAL(&dst_address.sin6.sin6_addr)) {
			/* XXX: sa6_recoverscope() */
			dst_address.sin6.sin6_scope_id =
			    ntohs(dst_address.sin6.sin6_addr.s6_addr16[1]);
			dst_address.sin6.sin6_addr.s6_addr16[1] = 0;
		}
		break;
#endif /* INET6 */
	default:
		DPRINTF(("%s: unsupported protocol family %u\n", __func__, af));
		m_freem(m);
		IPSEC_ISTAT(sproto, nopf);
		return EPFNOSUPPORT;
	}

	/* NB: only pass dst since key_allocsa follows RFC2401 */
	sav = KEY_ALLOCSA(&dst_address, sproto, spi);
	if (sav == NULL) {
		DPRINTF(("%s: no key association found for SA %s/%08lx/%u\n",
		    __func__, ipsec_address(&dst_address, buf, sizeof(buf)),
		    (u_long) ntohl(spi), sproto));
		IPSEC_ISTAT(sproto, notdb);
		m_freem(m);
		return ENOENT;
	}

	if (sav->tdb_xform == NULL) {
		DPRINTF(("%s: attempted to use uninitialized SA %s/%08lx/%u\n",
		    __func__, ipsec_address(&dst_address, buf, sizeof(buf)),
		    (u_long) ntohl(spi), sproto));
		IPSEC_ISTAT(sproto, noxform);
		KEY_FREESAV(&sav);
		m_freem(m);
		return ENXIO;
	}

	/*
	 * Call appropriate transform and return -- callback takes care of
	 * everything else.
	 */
	error = (*sav->tdb_xform->xf_input)(m, sav, skip, protoff);
	KEY_FREESAV(&sav);
	return error;
}
int
ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
{
	struct tdb_ident *tdbi;
	struct m_tag *mtag;
	struct secasvar *sav;
	struct secasindex *saidx;
	int error;

	IPSEC_ASSERT(m != NULL, ("null mbuf"));
	IPSEC_ASSERT(isr != NULL, ("null ISR"));
	sav = isr->sav;
	IPSEC_ASSERT(sav != NULL, ("null SA"));
	IPSEC_ASSERT(sav->sah != NULL, ("null SAH"));

	saidx = &sav->sah->saidx;
	switch (saidx->dst.sa.sa_family) {
#ifdef INET
	case AF_INET:
		/* Fix the header length, for AH processing. */
		mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len);
		break;
#endif /* INET */
#ifdef INET6
	case AF_INET6:
		/* Fix the header length, for AH processing. */
		if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) {
			error = ENXIO;
			goto bad;
		}
		if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) {
			/* No jumbogram support. */
			error = ENXIO;	/*?*/
			goto bad;
		}
		mtod(m, struct ip6_hdr *)->ip6_plen =
			htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
		break;
#endif /* INET6 */
	default:
		DPRINTF(("%s: unknown protocol family %u\n", __func__,
		    saidx->dst.sa.sa_family));
		error = ENXIO;
		goto bad;
	}

	/*
	 * Add a record of what we've done or what needs to be done to the
	 * packet.
	 */
	mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE,
			sizeof(struct tdb_ident), M_NOWAIT);
	if (mtag == NULL) {
		DPRINTF(("%s: could not get packet tag\n", __func__));
		error = ENOMEM;
		goto bad;
	}

	tdbi = (struct tdb_ident *)(mtag + 1);
	tdbi->dst = saidx->dst;
	tdbi->proto = saidx->proto;
	tdbi->spi = sav->spi;
	m_tag_prepend(m, mtag);

	/*
	 * If there's another (bundled) SA to apply, do so.
	 * Note that this puts a burden on the kernel stack size.
	 * If this is a problem we'll need to introduce a queue
	 * to set the packet on so we can unwind the stack before
	 * doing further processing.
	 */
	if (isr->next) {
		V_ipsec4stat.ips_out_bundlesa++;
		return ipsec4_process_packet(m, isr->next, 0, 0);
	}
	key_sa_recordxfer(sav, m);		/* record data transfer */

	/*
	 * We're done with IPsec processing, transmit the packet using the
	 * appropriate network protocol (IP or IPv6). SPD lookup will be
	 * performed again there.
	 */
	switch (saidx->dst.sa.sa_family) {
#ifdef INET
	struct ip *ip;
	case AF_INET:
		ip = mtod(m, struct ip *);
		ip->ip_len = ntohs(ip->ip_len);
		ip->ip_off = ntohs(ip->ip_off);

#ifdef IPSEC_NAT_T
		/*
		 * If NAT-T is enabled, now that all IPsec processing is done
		 * insert UDP encapsulation header after IP header.
		 */
		if (sav->natt_type) {
#ifdef _IP_VHL
			const int hlen = IP_VHL_HL(ip->ip_vhl);
#else
			const int hlen = (ip->ip_hl << 2);
#endif
			int size, off;
			struct mbuf *mi;
			struct udphdr *udp;

			size = sizeof(struct udphdr);
			if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) {
				/*
				 * draft-ietf-ipsec-nat-t-ike-0[01].txt and
				 * draft-ietf-ipsec-udp-encaps-(00/)01.txt,
				 * ignoring possible AH mode
				 * non-IKE marker + non-ESP marker
				 * from draft-ietf-ipsec-udp-encaps-00.txt.
				 */
				size += sizeof(u_int64_t);
			}
			mi = m_makespace(m, hlen, size, &off);
			if (mi == NULL) {
				DPRINTF(("%s: m_makespace for udphdr failed\n",
				    __func__));
				error = ENOBUFS;
				goto bad;
			}

			udp = (struct udphdr *)(mtod(mi, caddr_t) + off);
			if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
				udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT);
			else
				udp->uh_sport =
					KEY_PORTFROMSADDR(&sav->sah->saidx.src);
			udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst);
			udp->uh_sum = 0;
			udp->uh_ulen = htons(m->m_pkthdr.len - hlen);
			ip->ip_len = m->m_pkthdr.len;
			ip->ip_p = IPPROTO_UDP;

			if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
				*(u_int64_t *)(udp + 1) = 0;
		}
#endif /* IPSEC_NAT_T */

		return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL);
#endif /* INET */
#ifdef INET6
	case AF_INET6:
		/*
		 * We don't need massage, IPv6 header fields are always in
		 * net endian.
		 */
		return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
#endif /* INET6 */
	}
	panic("ipsec_process_done");
bad:
	m_freem(m);
	KEY_FREESAV(&sav);
	return (error);
}
Example #5
0
int
ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
{
	struct tdb_ident *tdbi;
	struct m_tag *mtag;
	struct secasvar *sav;
	struct secasindex *saidx;
	int error;

	KASSERT(m != NULL, ("ipsec_process_done: null mbuf"));
	KASSERT(isr != NULL, ("ipsec_process_done: null ISR"));
	sav = isr->sav;
	KASSERT(sav != NULL, ("ipsec_process_done: null SA"));
	KASSERT(sav->sah != NULL, ("ipsec_process_done: null SAH"));

	saidx = &sav->sah->saidx;
	switch (saidx->dst.sa.sa_family) {
#ifdef INET
	case AF_INET:
		/* Fix the header length, for AH processing. */
		mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len);
		break;
#endif /* INET */
#ifdef INET6
	case AF_INET6:
		/* Fix the header length, for AH processing. */
		if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) {
			error = ENXIO;
			goto bad;
		}
		if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) {
			/* No jumbogram support. */
			error = ENXIO;	/*?*/
			goto bad;
		}
		mtod(m, struct ip6_hdr *)->ip6_plen =
			htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
		break;
#endif /* INET6 */
	default:
		DPRINTF(("ipsec_process_done: unknown protocol family %u\n",
		    saidx->dst.sa.sa_family));
		error = ENXIO;
		goto bad;
	}

	/*
	 * Add a record of what we've done or what needs to be done to the
	 * packet.
	 */
	mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE,
			 sizeof(struct tdb_ident), MB_DONTWAIT);
	if (mtag == NULL) {
		DPRINTF(("ipsec_process_done: could not get packet tag\n"));
		error = ENOMEM;
		goto bad;
	}

	tdbi = (struct tdb_ident *)m_tag_data(mtag);
	tdbi->dst = saidx->dst;
	tdbi->proto = saidx->proto;
	tdbi->spi = sav->spi;
	m_tag_prepend(m, mtag);

	/*
	 * If there's another (bundled) SA to apply, do so.
	 * Note that this puts a burden on the kernel stack size.
	 * If this is a problem we'll need to introduce a queue
	 * to set the packet on so we can unwind the stack before
	 * doing further processing.
	 */
	if (isr->next) {
		newipsecstat.ips_out_bundlesa++;
		return ipsec4_process_packet(m, isr->next, 0, 0);
	}

	/*
	 * We're done with IPsec processing, transmit the packet using the
	 * appropriate network protocol (IP or IPv6). SPD lookup will be
	 * performed again there.
	 */
	switch (saidx->dst.sa.sa_family) {
#ifdef INET
	struct ip *ip;
	case AF_INET:
		ip = mtod(m, struct ip *);
		ip->ip_len = ntohs(ip->ip_len);
		ip->ip_off = ntohs(ip->ip_off);

		return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL);
#endif /* INET */
#ifdef INET6
	case AF_INET6:
		/*
		 * We don't need massage, IPv6 header fields are always in
		 * net endian.
		 */
		return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
#endif /* INET6 */
	}
	panic("ipsec_process_done");
bad:
	m_freem(m);
	KEY_FREESAV(&sav);
	return (error);
}
Example #6
0
/*
 * ESP output callback from the crypto driver.
 */
static int
esp_output_cb(struct cryptop *crp)
{
	struct tdb_crypto *tc;
	struct ipsecrequest *isr;
	struct secasvar *sav;
	struct mbuf *m;
	int s, err, error;

	tc = (struct tdb_crypto *) crp->crp_opaque;
	KASSERT(tc != NULL, ("esp_output_cb: null opaque data area!"));
	m = (struct mbuf *) crp->crp_buf;

	s = splnet();

	isr = tc->tc_isr;
	sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
	if (sav == NULL) {
		espstat.esps_notdb++;
		DPRINTF(("esp_output_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;
	}
	KASSERT(isr->sav == sav,
		("esp_output_cb: SA changed was %p now %p\n", isr->sav, sav));

	/* Check for crypto errors. */
	if (crp->crp_etype) {
		/* Reset 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_output_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_output_cb: bogus returned buffer from crypto\n"));
		error = EINVAL;
		goto bad;
	}
	espstat.esps_hist[sav->alg_enc]++;
	if (sav->tdb_authalgxform != NULL)
		ahstat.ahs_hist[sav->alg_auth]++;

	/* Release crypto descriptors. */
	free(tc, M_XDATA);
	crypto_freereq(crp);

	/* NB: m is reclaimed by ipsec_process_done. */
	err = ipsec_process_done(m, isr);
	KEY_FREESAV(&sav);
	splx(s);
	return err;
bad:
	if (sav)
		KEY_FREESAV(&sav);
	splx(s);
	if (m)
		m_freem(m);
	free(tc, M_XDATA);
	crypto_freereq(crp);
	return error;
}
Example #7
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;
}
/*
 * ESP input callback from the crypto driver.
 */
static int
esp_input_cb(struct cryptop *crp)
{
    u_int8_t lastthree[3], aalg[AH_ALEN_MAX];
    int s, hlen, skip, protoff, error;
    struct mbuf *m;
    struct cryptodesc *crd;
    const struct auth_hash *esph;
    const struct enc_xform *espx;
    struct tdb_crypto *tc;
    struct m_tag *mtag;
    struct secasvar *sav;
    struct secasindex *saidx;
    void *ptr;
    u_int16_t dport;
    u_int16_t sport;

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

    tc = (struct tdb_crypto *) crp->crp_opaque;
    IPSEC_ASSERT(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;

    /* find the source port for NAT-T */
    nat_t_ports_get(m, &dport, &sport);

    s = splsoftnet();
    mutex_enter(softnet_lock);

    sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi, sport, dport);
    if (sav == NULL) {
        ESP_STATINC(ESP_STAT_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;
    IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
                 saidx->dst.sa.sa_family == AF_INET6,
                 ("esp_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);
            mutex_exit(softnet_lock);
            splx(s);
            return crypto_dispatch(crp);
        }

        ESP_STATINC(ESP_STAT_NOXFORM);
        DPRINTF(("esp_input_cb: crypto error %d\n", crp->crp_etype));
        error = crp->crp_etype;
        goto bad;
    }

    /* Shouldn't happen... */
    if (m == NULL) {
        ESP_STATINC(ESP_STAT_CRYPTO);
        DPRINTF(("esp_input_cb: bogus returned buffer from crypto\n"));
        error = EINVAL;
        goto bad;
    }
    ESP_STATINC(ESP_STAT_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.
         */
        AH_STATINC(AH_STAT_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 = (tc + 1);

            /* Verify authenticator */
            if (!consttime_memequal(ptr, aalg, esph->authsize)) {
                DPRINTF(("esp_input_cb: "
                         "authentication hash mismatch for packet in SA %s/%08lx\n",
                         ipsec_address(&saidx->dst),
                         (u_long) ntohl(sav->spi)));
                ESP_STATINC(ESP_STAT_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;

    /*
     * Update replay sequence number, if appropriate.
     */
    if (sav->replay) {
        u_int32_t seq;

        m_copydata(m, skip + offsetof(struct newesp, esp_seq),
                   sizeof (seq), &seq);
        if (ipsec_updatereplay(ntohl(seq), sav)) {
            DPRINTF(("%s: packet replay check for %s\n", __func__,
                     ipsec_logsastr(sav)));
            ESP_STATINC(ESP_STAT_REPLAY);
            error = ENOBUFS;
            goto bad;
        }
    }
Example #9
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 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;
	IPSEC_ASSERT(crd != NULL, ("null crypto descriptor!"));

	tc = (struct tdb_crypto *) crp->crp_opaque;
	IPSEC_ASSERT(tc != NULL, ("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;

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

	saidx = &sav->sah->saidx;
	IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
		saidx->dst.sa.sa_family == AF_INET6,
		("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);
			error = crypto_dispatch(crp);
			return error;
		}

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

	/* Shouldn't happen... */
	if (m == NULL) {
		V_espstat.esps_crypto++;
		DPRINTF(("%s: bogus returned buffer from crypto\n", __func__));
		error = EINVAL;
		goto bad;
	}
	V_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.
		 */
		V_ahstat.ahs_hist[sav->alg_auth]++;
		if (mtag == NULL) {
			/* Copy the authenticator from the packet */
			m_copydata(m, m->m_pkthdr.len - AH_HMAC_HASHLEN,
				AH_HMAC_HASHLEN, aalg);

			ptr = (caddr_t) (tc + 1);

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

		/* Remove trailing authenticator */
		m_adj(m, -AH_HMAC_HASHLEN);
	}

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

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

	/*
	 * Update replay sequence number, if appropriate.
	 */
	if (sav->replay) {
		u_int32_t seq;

		m_copydata(m, skip + offsetof(struct newesp, esp_seq),
			   sizeof (seq), (caddr_t) &seq);
		if (ipsec_updatereplay(ntohl(seq), sav)) {
			DPRINTF(("%s: packet replay check for %s\n", __func__,
			    ipsec_logsastr(sav)));
			V_espstat.esps_replay++;
			error = ENOBUFS;
			goto bad;
		}
	}