1105197Ssam/*	$FreeBSD: releng/10.2/sys/netipsec/xform_ah.c 283901 2015-06-02 03:14:42Z ae $	*/
2105197Ssam/*	$OpenBSD: ip_ah.c,v 1.63 2001/06/26 06:18:58 angelos Exp $ */
3139823Simp/*-
4105197Ssam * The authors of this code are John Ioannidis (ji@tla.org),
5105197Ssam * Angelos D. Keromytis (kermit@csd.uch.gr) and
6105197Ssam * Niels Provos (provos@physnet.uni-hamburg.de).
7105197Ssam *
8105197Ssam * The original version of this code was written by John Ioannidis
9105197Ssam * for BSD/OS in Athens, Greece, in November 1995.
10105197Ssam *
11105197Ssam * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
12105197Ssam * by Angelos D. Keromytis.
13105197Ssam *
14105197Ssam * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
15105197Ssam * and Niels Provos.
16105197Ssam *
17105197Ssam * Additional features in 1999 by Angelos D. Keromytis and Niklas Hallqvist.
18105197Ssam *
19105197Ssam * Copyright (c) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
20105197Ssam * Angelos D. Keromytis and Niels Provos.
21105197Ssam * Copyright (c) 1999 Niklas Hallqvist.
22105197Ssam * Copyright (c) 2001 Angelos D. Keromytis.
23105197Ssam *
24105197Ssam * Permission to use, copy, and modify this software with or without fee
25105197Ssam * is hereby granted, provided that this entire notice is included in
26105197Ssam * all copies of any software which is or includes a copy or
27105197Ssam * modification of this software.
28105197Ssam * You may use this code under the GNU public license if you so wish. Please
29105197Ssam * contribute changes back to the authors under this freer than GPL license
30105197Ssam * so that we may further the use of strong encryption without limitations to
31105197Ssam * all.
32105197Ssam *
33105197Ssam * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
34105197Ssam * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
35105197Ssam * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
36105197Ssam * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
37105197Ssam * PURPOSE.
38105197Ssam */
39105197Ssam#include "opt_inet.h"
40105197Ssam#include "opt_inet6.h"
41105197Ssam
42105197Ssam#include <sys/param.h>
43105197Ssam#include <sys/systm.h>
44105197Ssam#include <sys/mbuf.h>
45105197Ssam#include <sys/socket.h>
46105197Ssam#include <sys/syslog.h>
47105197Ssam#include <sys/kernel.h>
48105197Ssam#include <sys/sysctl.h>
49105197Ssam
50105197Ssam#include <net/if.h>
51195699Srwatson#include <net/vnet.h>
52105197Ssam
53105197Ssam#include <netinet/in.h>
54105197Ssam#include <netinet/in_systm.h>
55105197Ssam#include <netinet/ip.h>
56105197Ssam#include <netinet/ip_ecn.h>
57105197Ssam#include <netinet/ip6.h>
58105197Ssam
59105197Ssam#include <netipsec/ipsec.h>
60105197Ssam#include <netipsec/ah.h>
61105197Ssam#include <netipsec/ah_var.h>
62105197Ssam#include <netipsec/xform.h>
63105197Ssam
64105197Ssam#ifdef INET6
65105197Ssam#include <netinet6/ip6_var.h>
66105197Ssam#include <netipsec/ipsec6.h>
67105197Ssam#include <netinet6/ip6_ecn.h>
68105197Ssam#endif
69105197Ssam
70105197Ssam#include <netipsec/key.h>
71105197Ssam#include <netipsec/key_debug.h>
72105197Ssam
73105197Ssam#include <opencrypto/cryptodev.h>
74105197Ssam
75105197Ssam/*
76105197Ssam * Return header size in bytes.  The old protocol did not support
77105197Ssam * the replay counter; the new protocol always includes the counter.
78105197Ssam */
79105197Ssam#define HDRSIZE(sav) \
80105197Ssam	(((sav)->flags & SADB_X_EXT_OLD) ? \
81105197Ssam		sizeof (struct ah) : sizeof (struct ah) + sizeof (u_int32_t))
82105197Ssam/*
83105197Ssam * Return authenticator size in bytes.  The old protocol is known
84158704Spjd * to use a fixed 16-byte authenticator.  The new algorithm use 12-byte
85158704Spjd * authenticator.
86105197Ssam */
87218794Svanhu#define	AUTHSIZE(sav)	ah_authsize(sav)
88105197Ssam
89195699SrwatsonVNET_DEFINE(int, ah_enable) = 1;	/* control flow of packets with AH */
90195699SrwatsonVNET_DEFINE(int, ah_cleartos) = 1;	/* clear ip_tos when doing AH calc */
91253088SaeVNET_PCPUSTAT_DEFINE(struct ahstat, ahstat);
92253088SaeVNET_PCPUSTAT_SYSINIT(ahstat);
93105197Ssam
94253088Sae#ifdef VIMAGE
95253088SaeVNET_PCPUSTAT_SYSUNINIT(ahstat);
96253088Sae#endif /* VIMAGE */
97253088Sae
98221129Sbz#ifdef INET
99105197SsamSYSCTL_DECL(_net_inet_ah);
100195699SrwatsonSYSCTL_VNET_INT(_net_inet_ah, OID_AUTO,
101195699Srwatson	ah_enable,	CTLFLAG_RW,	&VNET_NAME(ah_enable),	0, "");
102195699SrwatsonSYSCTL_VNET_INT(_net_inet_ah, OID_AUTO,
103195699Srwatson	ah_cleartos,	CTLFLAG_RW,	&VNET_NAME(ah_cleartos), 0, "");
104253088SaeSYSCTL_VNET_PCPUSTAT(_net_inet_ah, IPSECCTL_STATS, stats, struct ahstat,
105253088Sae    ahstat, "AH statistics (struct ahstat, netipsec/ah_var.h)");
106221129Sbz#endif
107105197Ssam
108105197Ssamstatic unsigned char ipseczeroes[256];	/* larger than an ip6 extension hdr */
109105197Ssam
110105197Ssamstatic int ah_input_cb(struct cryptop*);
111105197Ssamstatic int ah_output_cb(struct cryptop*);
112105197Ssam
113218794Svanhustatic int
114218794Svanhuah_authsize(struct secasvar *sav)
115218794Svanhu{
116218794Svanhu
117218794Svanhu	IPSEC_ASSERT(sav != NULL, ("%s: sav == NULL", __func__));
118218794Svanhu
119218794Svanhu	if (sav->flags & SADB_X_EXT_OLD)
120218794Svanhu		return 16;
121218794Svanhu
122218794Svanhu	switch (sav->alg_auth) {
123218794Svanhu	case SADB_X_AALG_SHA2_256:
124218794Svanhu		return 16;
125218794Svanhu	case SADB_X_AALG_SHA2_384:
126218794Svanhu		return 24;
127218794Svanhu	case SADB_X_AALG_SHA2_512:
128218794Svanhu		return 32;
129218794Svanhu	default:
130218794Svanhu		return AH_HMAC_HASHLEN;
131218794Svanhu	}
132218794Svanhu	/* NOTREACHED */
133218794Svanhu}
134105197Ssam/*
135105197Ssam * NB: this is public for use by the PF_KEY support.
136105197Ssam */
137105197Ssamstruct auth_hash *
138105197Ssamah_algorithm_lookup(int alg)
139105197Ssam{
140171133Sgnn	if (alg > SADB_AALG_MAX)
141105197Ssam		return NULL;
142105197Ssam	switch (alg) {
143105197Ssam	case SADB_X_AALG_NULL:
144105197Ssam		return &auth_hash_null;
145105197Ssam	case SADB_AALG_MD5HMAC:
146158704Spjd		return &auth_hash_hmac_md5;
147105197Ssam	case SADB_AALG_SHA1HMAC:
148158704Spjd		return &auth_hash_hmac_sha1;
149105197Ssam	case SADB_X_AALG_RIPEMD160HMAC:
150158704Spjd		return &auth_hash_hmac_ripemd_160;
151105197Ssam	case SADB_X_AALG_MD5:
152105197Ssam		return &auth_hash_key_md5;
153105197Ssam	case SADB_X_AALG_SHA:
154105197Ssam		return &auth_hash_key_sha1;
155105197Ssam	case SADB_X_AALG_SHA2_256:
156105197Ssam		return &auth_hash_hmac_sha2_256;
157105197Ssam	case SADB_X_AALG_SHA2_384:
158105197Ssam		return &auth_hash_hmac_sha2_384;
159105197Ssam	case SADB_X_AALG_SHA2_512:
160105197Ssam		return &auth_hash_hmac_sha2_512;
161105197Ssam	}
162105197Ssam	return NULL;
163105197Ssam}
164105197Ssam
165105197Ssamsize_t
166105197Ssamah_hdrsiz(struct secasvar *sav)
167105197Ssam{
168105197Ssam	size_t size;
169105197Ssam
170105197Ssam	if (sav != NULL) {
171105197Ssam		int authsize;
172120585Ssam		IPSEC_ASSERT(sav->tdb_authalgxform != NULL, ("null xform"));
173105197Ssam		/*XXX not right for null algorithm--does it matter??*/
174105197Ssam		authsize = AUTHSIZE(sav);
175105197Ssam		size = roundup(authsize, sizeof (u_int32_t)) + HDRSIZE(sav);
176105197Ssam	} else {
177105197Ssam		/* default guess */
178105197Ssam		size = sizeof (struct ah) + sizeof (u_int32_t) + 16;
179105197Ssam	}
180105197Ssam	return size;
181105197Ssam}
182105197Ssam
183105197Ssam/*
184105197Ssam * NB: public for use by esp_init.
185105197Ssam */
186105197Ssamint
187105197Ssamah_init0(struct secasvar *sav, struct xformsw *xsp, struct cryptoini *cria)
188105197Ssam{
189105197Ssam	struct auth_hash *thash;
190105197Ssam	int keylen;
191105197Ssam
192105197Ssam	thash = ah_algorithm_lookup(sav->alg_auth);
193105197Ssam	if (thash == NULL) {
194120585Ssam		DPRINTF(("%s: unsupported authentication algorithm %u\n",
195120585Ssam			__func__, sav->alg_auth));
196105197Ssam		return EINVAL;
197105197Ssam	}
198105197Ssam	/*
199105197Ssam	 * Verify the replay state block allocation is consistent with
200105197Ssam	 * the protocol type.  We check here so we can make assumptions
201105197Ssam	 * later during protocol processing.
202105197Ssam	 */
203105197Ssam	/* NB: replay state is setup elsewhere (sigh) */
204105197Ssam	if (((sav->flags&SADB_X_EXT_OLD) == 0) ^ (sav->replay != NULL)) {
205120585Ssam		DPRINTF(("%s: replay state block inconsistency, "
206120585Ssam			"%s algorithm %s replay state\n", __func__,
207105197Ssam			(sav->flags & SADB_X_EXT_OLD) ? "old" : "new",
208105197Ssam			sav->replay == NULL ? "without" : "with"));
209105197Ssam		return EINVAL;
210105197Ssam	}
211105197Ssam	if (sav->key_auth == NULL) {
212120585Ssam		DPRINTF(("%s: no authentication key for %s algorithm\n",
213120585Ssam			__func__, thash->name));
214105197Ssam		return EINVAL;
215105197Ssam	}
216105197Ssam	keylen = _KEYLEN(sav->key_auth);
217105197Ssam	if (keylen != thash->keysize && thash->keysize != 0) {
218120585Ssam		DPRINTF(("%s: invalid keylength %d, algorithm %s requires "
219120585Ssam			"keysize %d\n", __func__,
220105197Ssam			 keylen, thash->name, thash->keysize));
221105197Ssam		return EINVAL;
222105197Ssam	}
223105197Ssam
224105197Ssam	sav->tdb_xform = xsp;
225105197Ssam	sav->tdb_authalgxform = thash;
226105197Ssam
227105197Ssam	/* Initialize crypto session. */
228105197Ssam	bzero(cria, sizeof (*cria));
229105197Ssam	cria->cri_alg = sav->tdb_authalgxform->type;
230105197Ssam	cria->cri_klen = _KEYBITS(sav->key_auth);
231157123Sgnn	cria->cri_key = sav->key_auth->key_data;
232158704Spjd	cria->cri_mlen = AUTHSIZE(sav);
233105197Ssam
234105197Ssam	return 0;
235105197Ssam}
236105197Ssam
237105197Ssam/*
238105197Ssam * ah_init() is called when an SPI is being set up.
239105197Ssam */
240105197Ssamstatic int
241105197Ssamah_init(struct secasvar *sav, struct xformsw *xsp)
242105197Ssam{
243105197Ssam	struct cryptoini cria;
244105197Ssam	int error;
245105197Ssam
246105197Ssam	error = ah_init0(sav, xsp, &cria);
247105197Ssam	return error ? error :
248181803Sbz		 crypto_newsession(&sav->tdb_cryptoid, &cria, V_crypto_support);
249105197Ssam}
250105197Ssam
251105197Ssam/*
252105197Ssam * Paranoia.
253105197Ssam *
254105197Ssam * NB: public for use by esp_zeroize (XXX).
255105197Ssam */
256105197Ssamint
257105197Ssamah_zeroize(struct secasvar *sav)
258105197Ssam{
259105197Ssam	int err;
260105197Ssam
261105197Ssam	if (sav->key_auth)
262157123Sgnn		bzero(sav->key_auth->key_data, _KEYLEN(sav->key_auth));
263105197Ssam
264105197Ssam	err = crypto_freesession(sav->tdb_cryptoid);
265105197Ssam	sav->tdb_cryptoid = 0;
266105197Ssam	sav->tdb_authalgxform = NULL;
267105197Ssam	sav->tdb_xform = NULL;
268105197Ssam	return err;
269105197Ssam}
270105197Ssam
271105197Ssam/*
272105197Ssam * Massage IPv4/IPv6 headers for AH processing.
273105197Ssam */
274105197Ssamstatic int
275105197Ssamah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out)
276105197Ssam{
277105197Ssam	struct mbuf *m = *m0;
278105197Ssam	unsigned char *ptr;
279105197Ssam	int off, count;
280105197Ssam
281105197Ssam#ifdef INET
282105197Ssam	struct ip *ip;
283105197Ssam#endif /* INET */
284105197Ssam
285105197Ssam#ifdef INET6
286105197Ssam	struct ip6_ext *ip6e;
287105197Ssam	struct ip6_hdr ip6;
288105197Ssam	int alloc, len, ad;
289105197Ssam#endif /* INET6 */
290105197Ssam
291105197Ssam	switch (proto) {
292105197Ssam#ifdef INET
293105197Ssam	case AF_INET:
294105197Ssam		/*
295105197Ssam		 * This is the least painful way of dealing with IPv4 header
296105197Ssam		 * and option processing -- just make sure they're in
297105197Ssam		 * contiguous memory.
298105197Ssam		 */
299105197Ssam		*m0 = m = m_pullup(m, skip);
300105197Ssam		if (m == NULL) {
301120585Ssam			DPRINTF(("%s: m_pullup failed\n", __func__));
302105197Ssam			return ENOBUFS;
303105197Ssam		}
304105197Ssam
305105197Ssam		/* Fix the IP header */
306105197Ssam		ip = mtod(m, struct ip *);
307181803Sbz		if (V_ah_cleartos)
308105197Ssam			ip->ip_tos = 0;
309105197Ssam		ip->ip_ttl = 0;
310105197Ssam		ip->ip_sum = 0;
311105197Ssam
312241919Sglebius		if (alg == CRYPTO_MD5_KPDK || alg == CRYPTO_SHA1_KPDK)
313241919Sglebius			ip->ip_off &= htons(IP_DF);
314241919Sglebius		else
315241919Sglebius			ip->ip_off = htons(0);
316105197Ssam
317105197Ssam		ptr = mtod(m, unsigned char *) + sizeof(struct ip);
318105197Ssam
319105197Ssam		/* IPv4 option processing */
320105197Ssam		for (off = sizeof(struct ip); off < skip;) {
321105197Ssam			if (ptr[off] == IPOPT_EOL || ptr[off] == IPOPT_NOP ||
322105197Ssam			    off + 1 < skip)
323105197Ssam				;
324105197Ssam			else {
325120585Ssam				DPRINTF(("%s: illegal IPv4 option length for "
326120585Ssam					"option %d\n", __func__, ptr[off]));
327105197Ssam
328105197Ssam				m_freem(m);
329105197Ssam				return EINVAL;
330105197Ssam			}
331105197Ssam
332105197Ssam			switch (ptr[off]) {
333105197Ssam			case IPOPT_EOL:
334105197Ssam				off = skip;  /* End the loop. */
335105197Ssam				break;
336105197Ssam
337105197Ssam			case IPOPT_NOP:
338105197Ssam				off++;
339105197Ssam				break;
340105197Ssam
341105197Ssam			case IPOPT_SECURITY:	/* 0x82 */
342105197Ssam			case 0x85:	/* Extended security. */
343105197Ssam			case 0x86:	/* Commercial security. */
344105197Ssam			case 0x94:	/* Router alert */
345105197Ssam			case 0x95:	/* RFC1770 */
346105197Ssam				/* Sanity check for option length. */
347105197Ssam				if (ptr[off + 1] < 2) {
348120585Ssam					DPRINTF(("%s: illegal IPv4 option "
349120585Ssam						"length for option %d\n",
350120585Ssam						__func__, ptr[off]));
351105197Ssam
352105197Ssam					m_freem(m);
353105197Ssam					return EINVAL;
354105197Ssam				}
355105197Ssam
356105197Ssam				off += ptr[off + 1];
357105197Ssam				break;
358105197Ssam
359105197Ssam			case IPOPT_LSRR:
360105197Ssam			case IPOPT_SSRR:
361105197Ssam				/* Sanity check for option length. */
362105197Ssam				if (ptr[off + 1] < 2) {
363120585Ssam					DPRINTF(("%s: illegal IPv4 option "
364120585Ssam						"length for option %d\n",
365120585Ssam						__func__, ptr[off]));
366105197Ssam
367105197Ssam					m_freem(m);
368105197Ssam					return EINVAL;
369105197Ssam				}
370105197Ssam
371105197Ssam				/*
372105197Ssam				 * On output, if we have either of the
373105197Ssam				 * source routing options, we should
374105197Ssam				 * swap the destination address of the
375105197Ssam				 * IP header with the last address
376105197Ssam				 * specified in the option, as that is
377105197Ssam				 * what the destination's IP header
378105197Ssam				 * will look like.
379105197Ssam				 */
380105197Ssam				if (out)
381105197Ssam					bcopy(ptr + off + ptr[off + 1] -
382105197Ssam					    sizeof(struct in_addr),
383105197Ssam					    &(ip->ip_dst), sizeof(struct in_addr));
384105197Ssam
385105197Ssam				/* Fall through */
386105197Ssam			default:
387105197Ssam				/* Sanity check for option length. */
388105197Ssam				if (ptr[off + 1] < 2) {
389120585Ssam					DPRINTF(("%s: illegal IPv4 option "
390120585Ssam						"length for option %d\n",
391120585Ssam						__func__, ptr[off]));
392105197Ssam					m_freem(m);
393105197Ssam					return EINVAL;
394105197Ssam				}
395105197Ssam
396105197Ssam				/* Zeroize all other options. */
397105197Ssam				count = ptr[off + 1];
398105197Ssam				bcopy(ipseczeroes, ptr, count);
399105197Ssam				off += count;
400105197Ssam				break;
401105197Ssam			}
402105197Ssam
403105197Ssam			/* Sanity check. */
404105197Ssam			if (off > skip)	{
405120585Ssam				DPRINTF(("%s: malformed IPv4 options header\n",
406120585Ssam					__func__));
407105197Ssam
408105197Ssam				m_freem(m);
409105197Ssam				return EINVAL;
410105197Ssam			}
411105197Ssam		}
412105197Ssam
413105197Ssam		break;
414105197Ssam#endif /* INET */
415105197Ssam
416105197Ssam#ifdef INET6
417105197Ssam	case AF_INET6:  /* Ugly... */
418105197Ssam		/* Copy and "cook" the IPv6 header. */
419105197Ssam		m_copydata(m, 0, sizeof(ip6), (caddr_t) &ip6);
420105197Ssam
421105197Ssam		/* We don't do IPv6 Jumbograms. */
422105197Ssam		if (ip6.ip6_plen == 0) {
423120585Ssam			DPRINTF(("%s: unsupported IPv6 jumbogram\n", __func__));
424105197Ssam			m_freem(m);
425105197Ssam			return EMSGSIZE;
426105197Ssam		}
427105197Ssam
428105197Ssam		ip6.ip6_flow = 0;
429105197Ssam		ip6.ip6_hlim = 0;
430105197Ssam		ip6.ip6_vfc &= ~IPV6_VERSION_MASK;
431105197Ssam		ip6.ip6_vfc |= IPV6_VERSION;
432105197Ssam
433105197Ssam		/* Scoped address handling. */
434105197Ssam		if (IN6_IS_SCOPE_LINKLOCAL(&ip6.ip6_src))
435105197Ssam			ip6.ip6_src.s6_addr16[1] = 0;
436105197Ssam		if (IN6_IS_SCOPE_LINKLOCAL(&ip6.ip6_dst))
437105197Ssam			ip6.ip6_dst.s6_addr16[1] = 0;
438105197Ssam
439105197Ssam		/* Done with IPv6 header. */
440105197Ssam		m_copyback(m, 0, sizeof(struct ip6_hdr), (caddr_t) &ip6);
441105197Ssam
442105197Ssam		/* Let's deal with the remaining headers (if any). */
443105197Ssam		if (skip - sizeof(struct ip6_hdr) > 0) {
444105197Ssam			if (m->m_len <= skip) {
445105197Ssam				ptr = (unsigned char *) malloc(
446105197Ssam				    skip - sizeof(struct ip6_hdr),
447105197Ssam				    M_XDATA, M_NOWAIT);
448105197Ssam				if (ptr == NULL) {
449120585Ssam					DPRINTF(("%s: failed to allocate memory"
450120585Ssam						"for IPv6 headers\n",__func__));
451105197Ssam					m_freem(m);
452105197Ssam					return ENOBUFS;
453105197Ssam				}
454105197Ssam
455105197Ssam				/*
456105197Ssam				 * Copy all the protocol headers after
457105197Ssam				 * the IPv6 header.
458105197Ssam				 */
459105197Ssam				m_copydata(m, sizeof(struct ip6_hdr),
460105197Ssam				    skip - sizeof(struct ip6_hdr), ptr);
461105197Ssam				alloc = 1;
462105197Ssam			} else {
463105197Ssam				/* No need to allocate memory. */
464105197Ssam				ptr = mtod(m, unsigned char *) +
465105197Ssam				    sizeof(struct ip6_hdr);
466105197Ssam				alloc = 0;
467105197Ssam			}
468105197Ssam		} else
469105197Ssam			break;
470105197Ssam
471105197Ssam		off = ip6.ip6_nxt & 0xff; /* Next header type. */
472105197Ssam
473105197Ssam		for (len = 0; len < skip - sizeof(struct ip6_hdr);)
474105197Ssam			switch (off) {
475105197Ssam			case IPPROTO_HOPOPTS:
476105197Ssam			case IPPROTO_DSTOPTS:
477105197Ssam				ip6e = (struct ip6_ext *) (ptr + len);
478105197Ssam
479105197Ssam				/*
480105197Ssam				 * Process the mutable/immutable
481105197Ssam				 * options -- borrows heavily from the
482105197Ssam				 * KAME code.
483105197Ssam				 */
484105197Ssam				for (count = len + sizeof(struct ip6_ext);
485105197Ssam				     count < len + ((ip6e->ip6e_len + 1) << 3);) {
486105197Ssam					if (ptr[count] == IP6OPT_PAD1) {
487105197Ssam						count++;
488105197Ssam						continue; /* Skip padding. */
489105197Ssam					}
490105197Ssam
491105197Ssam					/* Sanity check. */
492105197Ssam					if (count > len +
493105197Ssam					    ((ip6e->ip6e_len + 1) << 3)) {
494105197Ssam						m_freem(m);
495105197Ssam
496105197Ssam						/* Free, if we allocated. */
497105197Ssam						if (alloc)
498184205Sdes							free(ptr, M_XDATA);
499105197Ssam						return EINVAL;
500105197Ssam					}
501105197Ssam
502105197Ssam					ad = ptr[count + 1];
503105197Ssam
504105197Ssam					/* If mutable option, zeroize. */
505105197Ssam					if (ptr[count] & IP6OPT_MUTABLE)
506105197Ssam						bcopy(ipseczeroes, ptr + count,
507105197Ssam						    ptr[count + 1]);
508105197Ssam
509105197Ssam					count += ad;
510105197Ssam
511105197Ssam					/* Sanity check. */
512105197Ssam					if (count >
513105197Ssam					    skip - sizeof(struct ip6_hdr)) {
514105197Ssam						m_freem(m);
515105197Ssam
516105197Ssam						/* Free, if we allocated. */
517105197Ssam						if (alloc)
518184205Sdes							free(ptr, M_XDATA);
519105197Ssam						return EINVAL;
520105197Ssam					}
521105197Ssam				}
522105197Ssam
523105197Ssam				/* Advance. */
524105197Ssam				len += ((ip6e->ip6e_len + 1) << 3);
525105197Ssam				off = ip6e->ip6e_nxt;
526105197Ssam				break;
527105197Ssam
528105197Ssam			case IPPROTO_ROUTING:
529105197Ssam				/*
530105197Ssam				 * Always include routing headers in
531105197Ssam				 * computation.
532105197Ssam				 */
533105197Ssam				ip6e = (struct ip6_ext *) (ptr + len);
534105197Ssam				len += ((ip6e->ip6e_len + 1) << 3);
535105197Ssam				off = ip6e->ip6e_nxt;
536105197Ssam				break;
537105197Ssam
538105197Ssam			default:
539120585Ssam				DPRINTF(("%s: unexpected IPv6 header type %d",
540120585Ssam					__func__, off));
541105197Ssam				if (alloc)
542184205Sdes					free(ptr, M_XDATA);
543105197Ssam				m_freem(m);
544105197Ssam				return EINVAL;
545105197Ssam			}
546105197Ssam
547105197Ssam		/* Copyback and free, if we allocated. */
548105197Ssam		if (alloc) {
549105197Ssam			m_copyback(m, sizeof(struct ip6_hdr),
550105197Ssam			    skip - sizeof(struct ip6_hdr), ptr);
551105197Ssam			free(ptr, M_XDATA);
552105197Ssam		}
553105197Ssam
554105197Ssam		break;
555105197Ssam#endif /* INET6 */
556105197Ssam	}
557105197Ssam
558105197Ssam	return 0;
559105197Ssam}
560105197Ssam
561105197Ssam/*
562105197Ssam * ah_input() gets called to verify that an input packet
563105197Ssam * passes authentication.
564105197Ssam */
565105197Ssamstatic int
566105197Ssamah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
567105197Ssam{
568105197Ssam	struct auth_hash *ahx;
569105197Ssam	struct tdb_ident *tdbi;
570105197Ssam	struct tdb_crypto *tc;
571105197Ssam	struct m_tag *mtag;
572105197Ssam	struct newah *ah;
573105197Ssam	int hl, rplen, authsize;
574105197Ssam
575105197Ssam	struct cryptodesc *crda;
576105197Ssam	struct cryptop *crp;
577105197Ssam
578120585Ssam	IPSEC_ASSERT(sav != NULL, ("null SA"));
579120585Ssam	IPSEC_ASSERT(sav->key_auth != NULL, ("null authentication key"));
580120585Ssam	IPSEC_ASSERT(sav->tdb_authalgxform != NULL,
581120585Ssam		("null authentication xform"));
582105197Ssam
583105197Ssam	/* Figure out header size. */
584105197Ssam	rplen = HDRSIZE(sav);
585105197Ssam
586105197Ssam	/* XXX don't pullup, just copy header */
587105197Ssam	IP6_EXTHDR_GET(ah, struct newah *, m, skip, rplen);
588105197Ssam	if (ah == NULL) {
589105197Ssam		DPRINTF(("ah_input: cannot pullup header\n"));
590252028Sae		AHSTAT_INC(ahs_hdrops);		/*XXX*/
591105197Ssam		m_freem(m);
592105197Ssam		return ENOBUFS;
593105197Ssam	}
594105197Ssam
595105197Ssam	/* Check replay window, if applicable. */
596105197Ssam	if (sav->replay && !ipsec_chkreplay(ntohl(ah->ah_seq), sav)) {
597252028Sae		AHSTAT_INC(ahs_replay);
598120585Ssam		DPRINTF(("%s: packet replay failure: %s\n", __func__,
599105197Ssam			  ipsec_logsastr(sav)));
600105197Ssam		m_freem(m);
601105197Ssam		return ENOBUFS;
602105197Ssam	}
603105197Ssam
604105197Ssam	/* Verify AH header length. */
605105197Ssam	hl = ah->ah_len * sizeof (u_int32_t);
606105197Ssam	ahx = sav->tdb_authalgxform;
607105197Ssam	authsize = AUTHSIZE(sav);
608105197Ssam	if (hl != authsize + rplen - sizeof (struct ah)) {
609120585Ssam		DPRINTF(("%s: bad authenticator length %u (expecting %lu)"
610120585Ssam			" for packet in SA %s/%08lx\n", __func__,
611105197Ssam			hl, (u_long) (authsize + rplen - sizeof (struct ah)),
612105197Ssam			ipsec_address(&sav->sah->saidx.dst),
613105197Ssam			(u_long) ntohl(sav->spi)));
614252028Sae		AHSTAT_INC(ahs_badauthl);
615105197Ssam		m_freem(m);
616105197Ssam		return EACCES;
617105197Ssam	}
618252028Sae	AHSTAT_ADD(ahs_ibytes, m->m_pkthdr.len - skip - hl);
619105197Ssam
620105197Ssam	/* Get crypto descriptors. */
621105197Ssam	crp = crypto_getreq(1);
622105197Ssam	if (crp == NULL) {
623120585Ssam		DPRINTF(("%s: failed to acquire crypto descriptor\n",__func__));
624252028Sae		AHSTAT_INC(ahs_crypto);
625105197Ssam		m_freem(m);
626105197Ssam		return ENOBUFS;
627105197Ssam	}
628105197Ssam
629105197Ssam	crda = crp->crp_desc;
630120585Ssam	IPSEC_ASSERT(crda != NULL, ("null crypto descriptor"));
631105197Ssam
632105197Ssam	crda->crd_skip = 0;
633105197Ssam	crda->crd_len = m->m_pkthdr.len;
634105197Ssam	crda->crd_inject = skip + rplen;
635105197Ssam
636105197Ssam	/* Authentication operation. */
637105197Ssam	crda->crd_alg = ahx->type;
638105197Ssam	crda->crd_klen = _KEYBITS(sav->key_auth);
639157123Sgnn	crda->crd_key = sav->key_auth->key_data;
640105197Ssam
641105197Ssam	/* Find out if we've already done crypto. */
642105197Ssam	for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, NULL);
643105197Ssam	     mtag != NULL;
644105197Ssam	     mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, mtag)) {
645105197Ssam		tdbi = (struct tdb_ident *) (mtag + 1);
646105197Ssam		if (tdbi->proto == sav->sah->saidx.proto &&
647105197Ssam		    tdbi->spi == sav->spi &&
648105197Ssam		    !bcmp(&tdbi->dst, &sav->sah->saidx.dst,
649105197Ssam			  sizeof (union sockaddr_union)))
650105197Ssam			break;
651105197Ssam	}
652105197Ssam
653105197Ssam	/* Allocate IPsec-specific opaque crypto info. */
654105197Ssam	if (mtag == NULL) {
655105197Ssam		tc = (struct tdb_crypto *) malloc(sizeof (struct tdb_crypto) +
656105197Ssam			skip + rplen + authsize, M_XDATA, M_NOWAIT|M_ZERO);
657105197Ssam	} else {
658105197Ssam		/* Hash verification has already been done successfully. */
659105197Ssam		tc = (struct tdb_crypto *) malloc(sizeof (struct tdb_crypto),
660105197Ssam						    M_XDATA, M_NOWAIT|M_ZERO);
661105197Ssam	}
662105197Ssam	if (tc == NULL) {
663120585Ssam		DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
664252028Sae		AHSTAT_INC(ahs_crypto);
665105197Ssam		crypto_freereq(crp);
666105197Ssam		m_freem(m);
667105197Ssam		return ENOBUFS;
668105197Ssam	}
669105197Ssam
670105197Ssam	/* Only save information if crypto processing is needed. */
671105197Ssam	if (mtag == NULL) {
672105197Ssam		int error;
673105197Ssam
674105197Ssam		/*
675105197Ssam		 * Save the authenticator, the skipped portion of the packet,
676105197Ssam		 * and the AH header.
677105197Ssam		 */
678105197Ssam		m_copydata(m, 0, skip + rplen + authsize, (caddr_t)(tc+1));
679105197Ssam
680105197Ssam		/* Zeroize the authenticator on the packet. */
681105197Ssam		m_copyback(m, skip + rplen, authsize, ipseczeroes);
682105197Ssam
683105197Ssam		/* "Massage" the packet headers for crypto processing. */
684105197Ssam		error = ah_massage_headers(&m, sav->sah->saidx.dst.sa.sa_family,
685105197Ssam		    skip, ahx->type, 0);
686105197Ssam		if (error != 0) {
687105197Ssam			/* NB: mbuf is free'd by ah_massage_headers */
688252028Sae			AHSTAT_INC(ahs_hdrops);
689105197Ssam			free(tc, M_XDATA);
690105197Ssam			crypto_freereq(crp);
691105197Ssam			return error;
692105197Ssam		}
693105197Ssam	}
694105197Ssam
695105197Ssam	/* Crypto operation descriptor. */
696105197Ssam	crp->crp_ilen = m->m_pkthdr.len; /* Total input length. */
697117058Ssam	crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
698105197Ssam	crp->crp_buf = (caddr_t) m;
699105197Ssam	crp->crp_callback = ah_input_cb;
700105197Ssam	crp->crp_sid = sav->tdb_cryptoid;
701105197Ssam	crp->crp_opaque = (caddr_t) tc;
702105197Ssam
703105197Ssam	/* These are passed as-is to the callback. */
704105197Ssam	tc->tc_spi = sav->spi;
705105197Ssam	tc->tc_dst = sav->sah->saidx.dst;
706105197Ssam	tc->tc_proto = sav->sah->saidx.proto;
707105197Ssam	tc->tc_nxt = ah->ah_nxt;
708105197Ssam	tc->tc_protoff = protoff;
709105197Ssam	tc->tc_skip = skip;
710105197Ssam	tc->tc_ptr = (caddr_t) mtag; /* Save the mtag we've identified. */
711220206Sfabient	KEY_ADDREFSA(sav);
712220206Sfabient	tc->tc_sav = sav;
713105197Ssam
714105197Ssam	if (mtag == NULL)
715105197Ssam		return crypto_dispatch(crp);
716105197Ssam	else
717105197Ssam		return ah_input_cb(crp);
718105197Ssam}
719105197Ssam
720105197Ssam/*
721105197Ssam * AH input callback from the crypto driver.
722105197Ssam */
723105197Ssamstatic int
724105197Ssamah_input_cb(struct cryptop *crp)
725105197Ssam{
726105197Ssam	int rplen, error, skip, protoff;
727105197Ssam	unsigned char calc[AH_ALEN_MAX];
728105197Ssam	struct mbuf *m;
729105197Ssam	struct cryptodesc *crd;
730105197Ssam	struct auth_hash *ahx;
731105197Ssam	struct tdb_crypto *tc;
732105197Ssam	struct m_tag *mtag;
733105197Ssam	struct secasvar *sav;
734105197Ssam	struct secasindex *saidx;
735105197Ssam	u_int8_t nxt;
736105197Ssam	caddr_t ptr;
737119643Ssam	int authsize;
738105197Ssam
739105197Ssam	crd = crp->crp_desc;
740105197Ssam
741105197Ssam	tc = (struct tdb_crypto *) crp->crp_opaque;
742120585Ssam	IPSEC_ASSERT(tc != NULL, ("null opaque crypto data area!"));
743105197Ssam	skip = tc->tc_skip;
744105197Ssam	nxt = tc->tc_nxt;
745105197Ssam	protoff = tc->tc_protoff;
746105197Ssam	mtag = (struct m_tag *) tc->tc_ptr;
747105197Ssam	m = (struct mbuf *) crp->crp_buf;
748105197Ssam
749220206Sfabient	sav = tc->tc_sav;
750220206Sfabient	IPSEC_ASSERT(sav != NULL, ("null SA!"));
751105197Ssam
752105197Ssam	saidx = &sav->sah->saidx;
753120585Ssam	IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
754105197Ssam		saidx->dst.sa.sa_family == AF_INET6,
755120585Ssam		("unexpected protocol family %u", saidx->dst.sa.sa_family));
756105197Ssam
757105197Ssam	ahx = (struct auth_hash *) sav->tdb_authalgxform;
758105197Ssam
759105197Ssam	/* Check for crypto errors. */
760105197Ssam	if (crp->crp_etype) {
761105197Ssam		if (sav->tdb_cryptoid != 0)
762105197Ssam			sav->tdb_cryptoid = crp->crp_sid;
763105197Ssam
764228009Spjd		if (crp->crp_etype == EAGAIN)
765228009Spjd			return (crypto_dispatch(crp));
766105197Ssam
767252028Sae		AHSTAT_INC(ahs_noxform);
768120585Ssam		DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
769105197Ssam		error = crp->crp_etype;
770105197Ssam		goto bad;
771105197Ssam	} else {
772252028Sae		AHSTAT_INC(ahs_hist[sav->alg_auth]);
773105197Ssam		crypto_freereq(crp);		/* No longer needed. */
774105197Ssam		crp = NULL;
775105197Ssam	}
776105197Ssam
777105197Ssam	/* Shouldn't happen... */
778105197Ssam	if (m == NULL) {
779252028Sae		AHSTAT_INC(ahs_crypto);
780120585Ssam		DPRINTF(("%s: bogus returned buffer from crypto\n", __func__));
781105197Ssam		error = EINVAL;
782105197Ssam		goto bad;
783105197Ssam	}
784105197Ssam
785105197Ssam	/* Figure out header size. */
786105197Ssam	rplen = HDRSIZE(sav);
787105197Ssam	authsize = AUTHSIZE(sav);
788105197Ssam
789105197Ssam	/* Copy authenticator off the packet. */
790105197Ssam	m_copydata(m, skip + rplen, authsize, calc);
791105197Ssam
792105197Ssam	/*
793105197Ssam	 * If we have an mtag, we don't need to verify the authenticator --
794105197Ssam	 * it has been verified by an IPsec-aware NIC.
795105197Ssam	 */
796105197Ssam	if (mtag == NULL) {
797105197Ssam		ptr = (caddr_t) (tc + 1);
798105197Ssam
799105197Ssam		/* Verify authenticator. */
800105197Ssam		if (bcmp(ptr + skip + rplen, calc, authsize)) {
801120585Ssam			DPRINTF(("%s: authentication hash mismatch for packet "
802120585Ssam			    "in SA %s/%08lx\n", __func__,
803105197Ssam			    ipsec_address(&saidx->dst),
804105197Ssam			    (u_long) ntohl(sav->spi)));
805252028Sae			AHSTAT_INC(ahs_badauth);
806105197Ssam			error = EACCES;
807105197Ssam			goto bad;
808105197Ssam		}
809105197Ssam
810105197Ssam		/* Fix the Next Protocol field. */
811105197Ssam		((u_int8_t *) ptr)[protoff] = nxt;
812105197Ssam
813105197Ssam		/* Copyback the saved (uncooked) network headers. */
814105197Ssam		m_copyback(m, 0, skip, ptr);
815105197Ssam	} else {
816105197Ssam		/* Fix the Next Protocol field. */
817105197Ssam		m_copyback(m, protoff, sizeof(u_int8_t), &nxt);
818105197Ssam	}
819105197Ssam
820105197Ssam	free(tc, M_XDATA), tc = NULL;			/* No longer needed */
821105197Ssam
822105197Ssam	/*
823105197Ssam	 * Header is now authenticated.
824105197Ssam	 */
825105197Ssam	m->m_flags |= M_AUTHIPHDR|M_AUTHIPDGM;
826105197Ssam
827105197Ssam	/*
828105197Ssam	 * Update replay sequence number, if appropriate.
829105197Ssam	 */
830105197Ssam	if (sav->replay) {
831105197Ssam		u_int32_t seq;
832105197Ssam
833105197Ssam		m_copydata(m, skip + offsetof(struct newah, ah_seq),
834105197Ssam			   sizeof (seq), (caddr_t) &seq);
835105197Ssam		if (ipsec_updatereplay(ntohl(seq), sav)) {
836252028Sae			AHSTAT_INC(ahs_replay);
837105197Ssam			error = ENOBUFS;			/*XXX as above*/
838105197Ssam			goto bad;
839105197Ssam		}
840105197Ssam	}
841105197Ssam
842105197Ssam	/*
843105197Ssam	 * Remove the AH header and authenticator from the mbuf.
844105197Ssam	 */
845105197Ssam	error = m_striphdr(m, skip, rplen + authsize);
846105197Ssam	if (error) {
847120585Ssam		DPRINTF(("%s: mangled mbuf chain for SA %s/%08lx\n", __func__,
848105197Ssam		    ipsec_address(&saidx->dst), (u_long) ntohl(sav->spi)));
849105197Ssam
850252028Sae		AHSTAT_INC(ahs_hdrops);
851105197Ssam		goto bad;
852105197Ssam	}
853105197Ssam
854221129Sbz	switch (saidx->dst.sa.sa_family) {
855221129Sbz#ifdef INET6
856221129Sbz	case AF_INET6:
857221129Sbz		error = ipsec6_common_input_cb(m, sav, skip, protoff, mtag);
858221129Sbz		break;
859221129Sbz#endif
860221129Sbz#ifdef INET
861221129Sbz	case AF_INET:
862221129Sbz		error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag);
863221129Sbz		break;
864221129Sbz#endif
865221129Sbz	default:
866221129Sbz		panic("%s: Unexpected address family: %d saidx=%p", __func__,
867221129Sbz		    saidx->dst.sa.sa_family, saidx);
868221129Sbz	}
869105197Ssam
870105197Ssam	KEY_FREESAV(&sav);
871105197Ssam	return error;
872105197Ssambad:
873105197Ssam	if (sav)
874105197Ssam		KEY_FREESAV(&sav);
875105197Ssam	if (m != NULL)
876105197Ssam		m_freem(m);
877105197Ssam	if (tc != NULL)
878105197Ssam		free(tc, M_XDATA);
879105197Ssam	if (crp != NULL)
880105197Ssam		crypto_freereq(crp);
881105197Ssam	return error;
882105197Ssam}
883105197Ssam
884105197Ssam/*
885105197Ssam * AH output routine, called by ipsec[46]_process_packet().
886105197Ssam */
887105197Ssamstatic int
888105197Ssamah_output(
889105197Ssam	struct mbuf *m,
890105197Ssam	struct ipsecrequest *isr,
891105197Ssam	struct mbuf **mp,
892105197Ssam	int skip,
893105197Ssam	int protoff)
894105197Ssam{
895105197Ssam	struct secasvar *sav;
896105197Ssam	struct auth_hash *ahx;
897105197Ssam	struct cryptodesc *crda;
898105197Ssam	struct tdb_crypto *tc;
899105197Ssam	struct mbuf *mi;
900105197Ssam	struct cryptop *crp;
901105197Ssam	u_int16_t iplen;
902105197Ssam	int error, rplen, authsize, maxpacketsize, roff;
903105197Ssam	u_int8_t prot;
904105197Ssam	struct newah *ah;
905105197Ssam
906105197Ssam	sav = isr->sav;
907120585Ssam	IPSEC_ASSERT(sav != NULL, ("null SA"));
908105197Ssam	ahx = sav->tdb_authalgxform;
909120585Ssam	IPSEC_ASSERT(ahx != NULL, ("null authentication xform"));
910105197Ssam
911252028Sae	AHSTAT_INC(ahs_output);
912105197Ssam
913105197Ssam	/* Figure out header size. */
914105197Ssam	rplen = HDRSIZE(sav);
915105197Ssam
916105197Ssam	/* Check for maximum packet size violations. */
917105197Ssam	switch (sav->sah->saidx.dst.sa.sa_family) {
918105197Ssam#ifdef INET
919105197Ssam	case AF_INET:
920105197Ssam		maxpacketsize = IP_MAXPACKET;
921105197Ssam		break;
922105197Ssam#endif /* INET */
923105197Ssam#ifdef INET6
924105197Ssam	case AF_INET6:
925105197Ssam		maxpacketsize = IPV6_MAXPACKET;
926105197Ssam		break;
927105197Ssam#endif /* INET6 */
928105197Ssam	default:
929120585Ssam		DPRINTF(("%s: unknown/unsupported protocol family %u, "
930120585Ssam		    "SA %s/%08lx\n", __func__,
931105197Ssam		    sav->sah->saidx.dst.sa.sa_family,
932105197Ssam		    ipsec_address(&sav->sah->saidx.dst),
933105197Ssam		    (u_long) ntohl(sav->spi)));
934252028Sae		AHSTAT_INC(ahs_nopf);
935105197Ssam		error = EPFNOSUPPORT;
936105197Ssam		goto bad;
937105197Ssam	}
938105197Ssam	authsize = AUTHSIZE(sav);
939105197Ssam	if (rplen + authsize + m->m_pkthdr.len > maxpacketsize) {
940120585Ssam		DPRINTF(("%s: packet in SA %s/%08lx got too big "
941120585Ssam		    "(len %u, max len %u)\n", __func__,
942105197Ssam		    ipsec_address(&sav->sah->saidx.dst),
943105197Ssam		    (u_long) ntohl(sav->spi),
944105197Ssam		    rplen + authsize + m->m_pkthdr.len, maxpacketsize));
945252028Sae		AHSTAT_INC(ahs_toobig);
946105197Ssam		error = EMSGSIZE;
947105197Ssam		goto bad;
948105197Ssam	}
949105197Ssam
950105197Ssam	/* Update the counters. */
951252028Sae	AHSTAT_ADD(ahs_obytes, m->m_pkthdr.len - skip);
952105197Ssam
953156756Ssam	m = m_unshare(m, M_NOWAIT);
954105197Ssam	if (m == NULL) {
955120585Ssam		DPRINTF(("%s: cannot clone mbuf chain, SA %s/%08lx\n", __func__,
956105197Ssam		    ipsec_address(&sav->sah->saidx.dst),
957105197Ssam		    (u_long) ntohl(sav->spi)));
958252028Sae		AHSTAT_INC(ahs_hdrops);
959105197Ssam		error = ENOBUFS;
960105197Ssam		goto bad;
961105197Ssam	}
962105197Ssam
963105197Ssam	/* Inject AH header. */
964105197Ssam	mi = m_makespace(m, skip, rplen + authsize, &roff);
965105197Ssam	if (mi == NULL) {
966120585Ssam		DPRINTF(("%s: failed to inject %u byte AH header for SA "
967120585Ssam		    "%s/%08lx\n", __func__,
968105197Ssam		    rplen + authsize,
969105197Ssam		    ipsec_address(&sav->sah->saidx.dst),
970105197Ssam		    (u_long) ntohl(sav->spi)));
971252028Sae		AHSTAT_INC(ahs_hdrops);		/*XXX differs from openbsd */
972105197Ssam		error = ENOBUFS;
973105197Ssam		goto bad;
974105197Ssam	}
975105197Ssam
976105197Ssam	/*
977105197Ssam	 * The AH header is guaranteed by m_makespace() to be in
978105197Ssam	 * contiguous memory, at roff bytes offset into the returned mbuf.
979105197Ssam	 */
980105197Ssam	ah = (struct newah *)(mtod(mi, caddr_t) + roff);
981105197Ssam
982105197Ssam	/* Initialize the AH header. */
983105197Ssam	m_copydata(m, protoff, sizeof(u_int8_t), (caddr_t) &ah->ah_nxt);
984105197Ssam	ah->ah_len = (rplen + authsize - sizeof(struct ah)) / sizeof(u_int32_t);
985105197Ssam	ah->ah_reserve = 0;
986105197Ssam	ah->ah_spi = sav->spi;
987105197Ssam
988105197Ssam	/* Zeroize authenticator. */
989105197Ssam	m_copyback(m, skip + rplen, authsize, ipseczeroes);
990105197Ssam
991105197Ssam	/* Insert packet replay counter, as requested.  */
992105197Ssam	if (sav->replay) {
993105197Ssam		if (sav->replay->count == ~0 &&
994105197Ssam		    (sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
995120585Ssam			DPRINTF(("%s: replay counter wrapped for SA %s/%08lx\n",
996120585Ssam				__func__,
997105197Ssam				ipsec_address(&sav->sah->saidx.dst),
998105197Ssam				(u_long) ntohl(sav->spi)));
999252028Sae			AHSTAT_INC(ahs_wrap);
1000105197Ssam			error = EINVAL;
1001105197Ssam			goto bad;
1002105197Ssam		}
1003157634Spjd#ifdef REGRESSION
1004157613Spjd		/* Emulate replay attack when ipsec_replay is TRUE. */
1005181803Sbz		if (!V_ipsec_replay)
1006157634Spjd#endif
1007157613Spjd			sav->replay->count++;
1008105197Ssam		ah->ah_seq = htonl(sav->replay->count);
1009105197Ssam	}
1010105197Ssam
1011105197Ssam	/* Get crypto descriptors. */
1012105197Ssam	crp = crypto_getreq(1);
1013105197Ssam	if (crp == NULL) {
1014120585Ssam		DPRINTF(("%s: failed to acquire crypto descriptors\n",
1015120585Ssam			__func__));
1016252028Sae		AHSTAT_INC(ahs_crypto);
1017105197Ssam		error = ENOBUFS;
1018105197Ssam		goto bad;
1019105197Ssam	}
1020105197Ssam
1021105197Ssam	crda = crp->crp_desc;
1022105197Ssam
1023105197Ssam	crda->crd_skip = 0;
1024105197Ssam	crda->crd_inject = skip + rplen;
1025105197Ssam	crda->crd_len = m->m_pkthdr.len;
1026105197Ssam
1027105197Ssam	/* Authentication operation. */
1028105197Ssam	crda->crd_alg = ahx->type;
1029157123Sgnn	crda->crd_key = sav->key_auth->key_data;
1030105197Ssam	crda->crd_klen = _KEYBITS(sav->key_auth);
1031105197Ssam
1032105197Ssam	/* Allocate IPsec-specific opaque crypto info. */
1033105197Ssam	tc = (struct tdb_crypto *) malloc(
1034105197Ssam		sizeof(struct tdb_crypto) + skip, M_XDATA, M_NOWAIT|M_ZERO);
1035105197Ssam	if (tc == NULL) {
1036105197Ssam		crypto_freereq(crp);
1037120585Ssam		DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
1038252028Sae		AHSTAT_INC(ahs_crypto);
1039105197Ssam		error = ENOBUFS;
1040105197Ssam		goto bad;
1041105197Ssam	}
1042105197Ssam
1043105197Ssam	/* Save the skipped portion of the packet. */
1044105197Ssam	m_copydata(m, 0, skip, (caddr_t) (tc + 1));
1045105197Ssam
1046105197Ssam	/*
1047105197Ssam	 * Fix IP header length on the header used for
1048105197Ssam	 * authentication. We don't need to fix the original
1049105197Ssam	 * header length as it will be fixed by our caller.
1050105197Ssam	 */
1051105197Ssam	switch (sav->sah->saidx.dst.sa.sa_family) {
1052105197Ssam#ifdef INET
1053105197Ssam	case AF_INET:
1054105197Ssam		bcopy(((caddr_t)(tc + 1)) +
1055105197Ssam		    offsetof(struct ip, ip_len),
1056105197Ssam		    (caddr_t) &iplen, sizeof(u_int16_t));
1057105197Ssam		iplen = htons(ntohs(iplen) + rplen + authsize);
1058105197Ssam		m_copyback(m, offsetof(struct ip, ip_len),
1059105197Ssam		    sizeof(u_int16_t), (caddr_t) &iplen);
1060105197Ssam		break;
1061105197Ssam#endif /* INET */
1062105197Ssam
1063105197Ssam#ifdef INET6
1064105197Ssam	case AF_INET6:
1065105197Ssam		bcopy(((caddr_t)(tc + 1)) +
1066105197Ssam		    offsetof(struct ip6_hdr, ip6_plen),
1067105197Ssam		    (caddr_t) &iplen, sizeof(u_int16_t));
1068105197Ssam		iplen = htons(ntohs(iplen) + rplen + authsize);
1069105197Ssam		m_copyback(m, offsetof(struct ip6_hdr, ip6_plen),
1070105197Ssam		    sizeof(u_int16_t), (caddr_t) &iplen);
1071105197Ssam		break;
1072105197Ssam#endif /* INET6 */
1073105197Ssam	}
1074105197Ssam
1075105197Ssam	/* Fix the Next Header field in saved header. */
1076105197Ssam	((u_int8_t *) (tc + 1))[protoff] = IPPROTO_AH;
1077105197Ssam
1078105197Ssam	/* Update the Next Protocol field in the IP header. */
1079105197Ssam	prot = IPPROTO_AH;
1080105197Ssam	m_copyback(m, protoff, sizeof(u_int8_t), (caddr_t) &prot);
1081105197Ssam
1082105197Ssam	/* "Massage" the packet headers for crypto processing. */
1083105197Ssam	error = ah_massage_headers(&m, sav->sah->saidx.dst.sa.sa_family,
1084105197Ssam			skip, ahx->type, 1);
1085105197Ssam	if (error != 0) {
1086105197Ssam		m = NULL;	/* mbuf was free'd by ah_massage_headers. */
1087105197Ssam		free(tc, M_XDATA);
1088105197Ssam		crypto_freereq(crp);
1089105197Ssam		goto bad;
1090105197Ssam	}
1091105197Ssam
1092105197Ssam	/* Crypto operation descriptor. */
1093105197Ssam	crp->crp_ilen = m->m_pkthdr.len; /* Total input length. */
1094117058Ssam	crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
1095105197Ssam	crp->crp_buf = (caddr_t) m;
1096105197Ssam	crp->crp_callback = ah_output_cb;
1097105197Ssam	crp->crp_sid = sav->tdb_cryptoid;
1098105197Ssam	crp->crp_opaque = (caddr_t) tc;
1099105197Ssam
1100105197Ssam	/* These are passed as-is to the callback. */
1101105197Ssam	tc->tc_isr = isr;
1102220206Sfabient	KEY_ADDREFSA(sav);
1103220206Sfabient	tc->tc_sav = sav;
1104105197Ssam	tc->tc_spi = sav->spi;
1105105197Ssam	tc->tc_dst = sav->sah->saidx.dst;
1106105197Ssam	tc->tc_proto = sav->sah->saidx.proto;
1107105197Ssam	tc->tc_skip = skip;
1108105197Ssam	tc->tc_protoff = protoff;
1109105197Ssam
1110105197Ssam	return crypto_dispatch(crp);
1111105197Ssambad:
1112105197Ssam	if (m)
1113105197Ssam		m_freem(m);
1114105197Ssam	return (error);
1115105197Ssam}
1116105197Ssam
1117105197Ssam/*
1118105197Ssam * AH output callback from the crypto driver.
1119105197Ssam */
1120105197Ssamstatic int
1121105197Ssamah_output_cb(struct cryptop *crp)
1122105197Ssam{
1123105197Ssam	int skip, protoff, error;
1124105197Ssam	struct tdb_crypto *tc;
1125105197Ssam	struct ipsecrequest *isr;
1126105197Ssam	struct secasvar *sav;
1127105197Ssam	struct mbuf *m;
1128105197Ssam	caddr_t ptr;
1129105197Ssam
1130105197Ssam	tc = (struct tdb_crypto *) crp->crp_opaque;
1131120585Ssam	IPSEC_ASSERT(tc != NULL, ("null opaque data area!"));
1132105197Ssam	skip = tc->tc_skip;
1133105197Ssam	protoff = tc->tc_protoff;
1134105197Ssam	ptr = (caddr_t) (tc + 1);
1135105197Ssam	m = (struct mbuf *) crp->crp_buf;
1136105197Ssam
1137105197Ssam	isr = tc->tc_isr;
1138120585Ssam	IPSECREQUEST_LOCK(isr);
1139220206Sfabient	sav = tc->tc_sav;
1140220206Sfabient	/* With the isr lock released SA pointer can be updated. */
1141220206Sfabient	if (sav != isr->sav) {
1142252028Sae		AHSTAT_INC(ahs_notdb);
1143120585Ssam		DPRINTF(("%s: SA expired while in crypto\n", __func__));
1144105197Ssam		error = ENOBUFS;		/*XXX*/
1145105197Ssam		goto bad;
1146105197Ssam	}
1147105197Ssam
1148105197Ssam	/* Check for crypto errors. */
1149105197Ssam	if (crp->crp_etype) {
1150105197Ssam		if (sav->tdb_cryptoid != 0)
1151105197Ssam			sav->tdb_cryptoid = crp->crp_sid;
1152105197Ssam
1153105197Ssam		if (crp->crp_etype == EAGAIN) {
1154120585Ssam			IPSECREQUEST_UNLOCK(isr);
1155228009Spjd			return (crypto_dispatch(crp));
1156105197Ssam		}
1157105197Ssam
1158252028Sae		AHSTAT_INC(ahs_noxform);
1159120585Ssam		DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
1160105197Ssam		error = crp->crp_etype;
1161105197Ssam		goto bad;
1162105197Ssam	}
1163105197Ssam
1164105197Ssam	/* Shouldn't happen... */
1165105197Ssam	if (m == NULL) {
1166252028Sae		AHSTAT_INC(ahs_crypto);
1167120585Ssam		DPRINTF(("%s: bogus returned buffer from crypto\n", __func__));
1168105197Ssam		error = EINVAL;
1169105197Ssam		goto bad;
1170105197Ssam	}
1171252028Sae	AHSTAT_INC(ahs_hist[sav->alg_auth]);
1172105197Ssam
1173105197Ssam	/*
1174105197Ssam	 * Copy original headers (with the new protocol number) back
1175105197Ssam	 * in place.
1176105197Ssam	 */
1177105197Ssam	m_copyback(m, 0, skip, ptr);
1178105197Ssam
1179105197Ssam	/* No longer needed. */
1180105197Ssam	free(tc, M_XDATA);
1181105197Ssam	crypto_freereq(crp);
1182105197Ssam
1183157634Spjd#ifdef REGRESSION
1184157613Spjd	/* Emulate man-in-the-middle attack when ipsec_integrity is TRUE. */
1185181803Sbz	if (V_ipsec_integrity) {
1186157613Spjd		int alen;
1187157613Spjd
1188157613Spjd		/*
1189157613Spjd		 * Corrupt HMAC if we want to test integrity verification of
1190157613Spjd		 * the other side.
1191157613Spjd		 */
1192157613Spjd		alen = AUTHSIZE(sav);
1193157613Spjd		m_copyback(m, m->m_pkthdr.len - alen, alen, ipseczeroes);
1194157613Spjd	}
1195157634Spjd#endif
1196157613Spjd
1197105197Ssam	/* NB: m is reclaimed by ipsec_process_done. */
1198228010Spjd	error = ipsec_process_done(m, isr);
1199105197Ssam	KEY_FREESAV(&sav);
1200120585Ssam	IPSECREQUEST_UNLOCK(isr);
1201228010Spjd	return error;
1202105197Ssambad:
1203105197Ssam	if (sav)
1204105197Ssam		KEY_FREESAV(&sav);
1205120585Ssam	IPSECREQUEST_UNLOCK(isr);
1206105197Ssam	if (m)
1207105197Ssam		m_freem(m);
1208105197Ssam	free(tc, M_XDATA);
1209105197Ssam	crypto_freereq(crp);
1210105197Ssam	return error;
1211105197Ssam}
1212105197Ssam
1213105197Ssamstatic struct xformsw ah_xformsw = {
1214105197Ssam	XF_AH,		XFT_AUTH,	"IPsec AH",
1215105197Ssam	ah_init,	ah_zeroize,	ah_input,	ah_output,
1216105197Ssam};
1217105197Ssam
1218105197Ssamstatic void
1219105197Ssamah_attach(void)
1220105197Ssam{
1221185088Szec
1222190787Szec	xform_register(&ah_xformsw);
1223190787Szec}
1224190787Szec
1225105197SsamSYSINIT(ah_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ah_attach, NULL);
1226