1/*	$FreeBSD: src/sys/netinet6/ah_input.c,v 1.1.2.6 2002/04/28 05:40:26 suz Exp $	*/
2/*	$KAME: ah_input.c,v 1.67 2002/01/07 11:39:56 kjc Exp $	*/
3
4/*
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33/*
34 * RFC1826/2402 authentication header.
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/malloc.h>
40#include <sys/mbuf.h>
41#include <sys/domain.h>
42#include <sys/protosw.h>
43#include <sys/socket.h>
44#include <sys/errno.h>
45#include <sys/time.h>
46#include <sys/kernel.h>
47#include <sys/syslog.h>
48
49#include <net/if.h>
50#include <net/route.h>
51#include <kern/cpu_number.h>
52#include <kern/locks.h>
53
54#include <netinet/in.h>
55#include <netinet/in_systm.h>
56#include <netinet/in_var.h>
57#include <netinet/ip.h>
58#include <netinet/ip_var.h>
59#include <netinet/ip_ecn.h>
60#include <netinet/in_pcb.h>
61#if INET6
62#include <netinet6/ip6_ecn.h>
63#endif
64
65#if INET6
66#include <netinet/ip6.h>
67#include <netinet6/ip6_var.h>
68#include <netinet6/in6_pcb.h>
69#include <netinet/icmp6.h>
70#include <netinet6/ip6protosw.h>
71#endif
72
73#include <netinet6/ipsec.h>
74#if INET6
75#include <netinet6/ipsec6.h>
76#endif
77#include <netinet6/ah.h>
78#if INET6
79#include <netinet6/ah6.h>
80#endif
81#include <netkey/key.h>
82#include <netkey/keydb.h>
83#if IPSEC_DEBUG
84#include <netkey/key_debug.h>
85#else
86#define	KEYDEBUG(lev,arg)
87#endif
88
89#include <net/kpi_protocol.h>
90#include <netinet/kpi_ipfilter_var.h>
91
92#include <net/net_osdep.h>
93
94#define IPLEN_FLIPPED
95
96#if INET
97extern struct protosw inetsw[];
98
99void
100ah4_input(struct mbuf *m, int off)
101{
102	struct ip *ip;
103	struct ah *ah;
104	u_int32_t spi;
105	const struct ah_algorithm *algo;
106	size_t siz;
107	size_t siz1;
108	u_char *cksum;
109	struct secasvar *sav = NULL;
110	u_int16_t nxt;
111	size_t hlen;
112	size_t stripsiz = 0;
113	sa_family_t ifamily;
114
115#ifndef PULLDOWN_TEST
116	if (m->m_len < off + sizeof(struct newah)) {
117		m = m_pullup(m, off + sizeof(struct newah));
118		if (!m) {
119			ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
120				"dropping the packet for simplicity\n"));
121			IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
122			goto fail;
123		}
124	}
125
126	ip = mtod(m, struct ip *);
127	ah = (struct ah *)(((caddr_t)ip) + off);
128#else
129	ip = mtod(m, struct ip *);
130	IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
131	if (ah == NULL) {
132		ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
133			"dropping the packet for simplicity\n"));
134		IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
135		goto fail;
136	}
137#endif
138	nxt = ah->ah_nxt;
139#ifdef _IP_VHL
140	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
141#else
142	hlen = ip->ip_hl << 2;
143#endif
144
145	/* find the sassoc. */
146	spi = ah->ah_spi;
147
148	if ((sav = key_allocsa(AF_INET,
149	                      (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
150	                      IPPROTO_AH, spi)) == 0) {
151		ipseclog((LOG_WARNING,
152		    "IPv4 AH input: no key association found for spi %u\n",
153		    (u_int32_t)ntohl(spi)));
154		IPSEC_STAT_INCREMENT(ipsecstat.in_nosa);
155		goto fail;
156	}
157	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
158		printf("DP ah4_input called to allocate SA:%p\n", sav));
159	if (sav->state != SADB_SASTATE_MATURE
160	 && sav->state != SADB_SASTATE_DYING) {
161		ipseclog((LOG_DEBUG,
162		    "IPv4 AH input: non-mature/dying SA found for spi %u\n",
163		    (u_int32_t)ntohl(spi)));
164		IPSEC_STAT_INCREMENT(ipsecstat.in_badspi);
165		goto fail;
166	}
167
168	algo = ah_algorithm_lookup(sav->alg_auth);
169	if (!algo) {
170		ipseclog((LOG_DEBUG, "IPv4 AH input: "
171		    "unsupported authentication algorithm for spi %u\n",
172		    (u_int32_t)ntohl(spi)));
173		IPSEC_STAT_INCREMENT(ipsecstat.in_badspi);
174		goto fail;
175	}
176
177	siz = (*algo->sumsiz)(sav);
178	siz1 = ((siz + 3) & ~(4 - 1));
179
180	/*
181	 * sanity checks for header, 1.
182	 */
183    {
184	int sizoff;
185
186	sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
187
188	/*
189	 * Here, we do not do "siz1 == siz".  This is because the way
190	 * RFC240[34] section 2 is written.  They do not require truncation
191	 * to 96 bits.
192	 * For example, Microsoft IPsec stack attaches 160 bits of
193	 * authentication data for both hmac-md5 and hmac-sha1.  For hmac-sha1,
194	 * 32 bits of padding is attached.
195	 *
196	 * There are two downsides to this specification.
197	 * They have no real harm, however, they leave us fuzzy feeling.
198	 * - if we attach more than 96 bits of authentication data onto AH,
199	 *   we will never notice about possible modification by rogue
200	 *   intermediate nodes.
201	 *   Since extra bits in AH checksum is never used, this constitutes
202	 *   no real issue, however, it is wacky.
203	 * - even if the peer attaches big authentication data, we will never
204	 *   notice the difference, since longer authentication data will just
205	 *   work.
206	 *
207	 * We may need some clarification in the spec.
208	 */
209	if (siz1 < siz) {
210		ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input "
211		    "(%lu, should be at least %lu): %s\n",
212		    (u_long)siz1, (u_long)siz,
213		    ipsec4_logpacketstr(ip, spi)));
214		IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
215		goto fail;
216	}
217	if ((ah->ah_len << 2) - sizoff != siz1) {
218		ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input "
219		    "(%d should be %lu): %s\n",
220		    (ah->ah_len << 2) - sizoff, (u_long)siz1,
221		    ipsec4_logpacketstr(ip, spi)));
222		IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
223		goto fail;
224	}
225
226#ifndef PULLDOWN_TEST
227	if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) {
228		m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1);
229		if (!m) {
230			ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
231			IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
232			goto fail;
233		}
234
235		ip = mtod(m, struct ip *);
236		ah = (struct ah *)(((caddr_t)ip) + off);
237	}
238#else
239	IP6_EXTHDR_GET(ah, struct ah *, m, off,
240		sizeof(struct ah) + sizoff + siz1);
241	if (ah == NULL) {
242		ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
243		IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
244		goto fail;
245	}
246#endif
247    }
248
249	/*
250	 * check for sequence number.
251	 */
252	if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
253		if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
254			; /*okey*/
255		else {
256			IPSEC_STAT_INCREMENT(ipsecstat.in_ahreplay);
257			ipseclog((LOG_WARNING,
258			    "replay packet in IPv4 AH input: %s %s\n",
259			    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
260			goto fail;
261		}
262	}
263
264	/*
265	 * alright, it seems sane.  now we are going to check the
266	 * cryptographic checksum.
267	 */
268	cksum = _MALLOC(siz1, M_TEMP, M_NOWAIT);
269	if (!cksum) {
270		ipseclog((LOG_DEBUG, "IPv4 AH input: "
271		    "couldn't alloc temporary region for cksum\n"));
272		IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
273		goto fail;
274	}
275
276	/*
277	 * some of IP header fields are flipped to the host endian.
278	 * convert them back to network endian.  VERY stupid.
279	 */
280	ip->ip_len = htons(ip->ip_len + hlen);
281	ip->ip_off = htons(ip->ip_off);
282	if (ah4_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
283		FREE(cksum, M_TEMP);
284		IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
285		goto fail;
286	}
287	IPSEC_STAT_INCREMENT(ipsecstat.in_ahhist[sav->alg_auth]);
288	/*
289	 * flip them back.
290	 */
291	ip->ip_len = ntohs(ip->ip_len) - hlen;
292	ip->ip_off = ntohs(ip->ip_off);
293
294    {
295	caddr_t sumpos = NULL;
296
297	if (sav->flags & SADB_X_EXT_OLD) {
298		/* RFC 1826 */
299		sumpos = (caddr_t)(ah + 1);
300	} else {
301		/* RFC 2402 */
302		sumpos = (caddr_t)(((struct newah *)ah) + 1);
303	}
304
305	if (bcmp(sumpos, cksum, siz) != 0) {
306		ipseclog((LOG_WARNING,
307		    "checksum mismatch in IPv4 AH input: %s %s\n",
308		    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
309		FREE(cksum, M_TEMP);
310		IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthfail);
311		goto fail;
312	}
313    }
314
315	FREE(cksum, M_TEMP);
316
317	m->m_flags |= M_AUTHIPHDR;
318	m->m_flags |= M_AUTHIPDGM;
319
320#if 0
321	/*
322	 * looks okey, but we need more sanity check.
323	 * XXX should elaborate.
324	 */
325	if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) {
326		struct ip *nip;
327		size_t sizoff;
328
329		sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
330
331		if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) {
332			m = m_pullup(m, off + sizeof(struct ah)
333					+ sizoff + siz1 + hlen);
334			if (!m) {
335				ipseclog((LOG_DEBUG,
336				    "IPv4 AH input: can't pullup\n"));
337				IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
338				goto fail;
339			}
340		}
341
342		nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1);
343		if (nip->ip_src.s_addr != ip->ip_src.s_addr
344		 || nip->ip_dst.s_addr != ip->ip_dst.s_addr) {
345			m->m_flags &= ~M_AUTHIPHDR;
346			m->m_flags &= ~M_AUTHIPDGM;
347		}
348	}
349#if INET6
350	else if (ah->ah_nxt == IPPROTO_IPV6) {
351		m->m_flags &= ~M_AUTHIPHDR;
352		m->m_flags &= ~M_AUTHIPDGM;
353	}
354#endif /*INET6*/
355#endif /*0*/
356
357	if (m->m_flags & M_AUTHIPHDR
358	 && m->m_flags & M_AUTHIPDGM) {
359#if 0
360		ipseclog((LOG_DEBUG,
361		    "IPv4 AH input: authentication succeess\n"));
362#endif
363		IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthsucc);
364	} else {
365		ipseclog((LOG_WARNING,
366		    "authentication failed in IPv4 AH input: %s %s\n",
367		    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
368		IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthfail);
369		goto fail;
370	}
371
372	/*
373	 * update sequence number.
374	 */
375	if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
376		if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
377			IPSEC_STAT_INCREMENT(ipsecstat.in_ahreplay);
378			goto fail;
379		}
380	}
381
382	/* was it transmitted over the IPsec tunnel SA? */
383	if (sav->flags & SADB_X_EXT_OLD) {
384		/* RFC 1826 */
385		stripsiz = sizeof(struct ah) + siz1;
386	} else {
387		/* RFC 2402 */
388		stripsiz = sizeof(struct newah) + siz1;
389	}
390	if (ipsec4_tunnel_validate(m, off + stripsiz, nxt, sav, &ifamily)) {
391		/*
392		 * strip off all the headers that precedes AH.
393		 *	IP xx AH IP' payload -> IP' payload
394		 *
395		 * XXX more sanity checks
396		 * XXX relationship with gif?
397		 */
398		u_int8_t tos;
399
400		if (ifamily == AF_INET6) {
401			ipseclog((LOG_NOTICE, "ipsec tunnel protocol mismatch "
402			    "in IPv4 AH input: %s\n", ipsec_logsastr(sav)));
403			goto fail;
404		}
405		tos = ip->ip_tos;
406		m_adj(m, off + stripsiz);
407		if (m->m_len < sizeof(*ip)) {
408			m = m_pullup(m, sizeof(*ip));
409			if (!m) {
410				IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
411				goto fail;
412			}
413		}
414		ip = mtod(m, struct ip *);
415		/* ECN consideration. */
416		ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
417		if (!key_checktunnelsanity(sav, AF_INET,
418			    (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
419			ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
420			    "in IPv4 AH input: %s %s\n",
421			    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
422			IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
423			goto fail;
424		}
425
426#if 1
427		/*
428		 * Should the inner packet be considered authentic?
429		 * My current answer is: NO.
430		 *
431		 * host1 -- gw1 === gw2 -- host2
432		 *	In this case, gw2 can trust the	authenticity of the
433		 *	outer packet, but NOT inner.  Packet may be altered
434		 *	between host1 and gw1.
435		 *
436		 * host1 -- gw1 === host2
437		 *	This case falls into the same scenario as above.
438		 *
439		 * host1 === host2
440		 *	This case is the only case when we may be able to leave
441		 *	M_AUTHIPHDR and M_AUTHIPDGM set.
442		 *	However, if host1 is wrongly configured, and allows
443		 *	attacker to inject some packet with src=host1 and
444		 *	dst=host2, you are in risk.
445		 */
446		m->m_flags &= ~M_AUTHIPHDR;
447		m->m_flags &= ~M_AUTHIPDGM;
448#endif
449
450		key_sa_recordxfer(sav, m);
451		if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
452		    ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) {
453			IPSEC_STAT_INCREMENT(ipsecstat.in_nomem);
454			goto fail;
455		}
456		proto_input(PF_INET, m);
457		nxt = IPPROTO_DONE;
458	} else {
459		/*
460		 * strip off AH.
461		 */
462
463		ip = mtod(m, struct ip *);
464#ifndef PULLDOWN_TEST
465		/*
466		 * We do deep-copy since KAME requires that
467		 * the packet is placed in a single external mbuf.
468		 */
469		ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off);
470		m->m_data += stripsiz;
471		m->m_len -= stripsiz;
472		m->m_pkthdr.len -= stripsiz;
473#else
474		/*
475		 * even in m_pulldown case, we need to strip off AH so that
476		 * we can compute checksum for multiple AH correctly.
477		 */
478		if (m->m_len >= stripsiz + off) {
479			ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off);
480			m->m_data += stripsiz;
481			m->m_len -= stripsiz;
482			m->m_pkthdr.len -= stripsiz;
483		} else {
484			/*
485			 * this comes with no copy if the boundary is on
486			 * cluster
487			 */
488			struct mbuf *n;
489
490			n = m_split(m, off, M_DONTWAIT);
491			if (n == NULL) {
492				/* m is retained by m_split */
493				goto fail;
494			}
495			m_adj(n, stripsiz);
496			/* m_cat does not update m_pkthdr.len */
497			m->m_pkthdr.len += n->m_pkthdr.len;
498			m_cat(m, n);
499		}
500#endif
501
502		if (m->m_len < sizeof(*ip)) {
503			m = m_pullup(m, sizeof(*ip));
504			if (m == NULL) {
505				IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
506				goto fail;
507			}
508		}
509		ip = mtod(m, struct ip *);
510#ifdef IPLEN_FLIPPED
511		ip->ip_len = ip->ip_len - stripsiz;
512#else
513		ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz);
514#endif
515		ip->ip_p = nxt;
516		/* forget about IP hdr checksum, the check has already been passed */
517
518		key_sa_recordxfer(sav, m);
519		if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
520			IPSEC_STAT_INCREMENT(ipsecstat.in_nomem);
521			goto fail;
522		}
523
524		if (nxt != IPPROTO_DONE) {
525			if ((ip_protox[nxt]->pr_flags & PR_LASTHDR) != 0 &&
526			    ipsec4_in_reject(m, NULL)) {
527				IPSEC_STAT_INCREMENT(ipsecstat.in_polvio);
528				goto fail;
529			}
530			ip_proto_dispatch_in(m, off, nxt, 0);
531		} else
532			m_freem(m);
533		m = NULL;
534	}
535
536	if (sav) {
537		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
538			printf("DP ah4_input call free SA:%p\n", sav));
539		key_freesav(sav, KEY_SADB_UNLOCKED);
540	}
541	IPSEC_STAT_INCREMENT(ipsecstat.in_success);
542	return;
543
544fail:
545	if (sav) {
546		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
547			printf("DP ah4_input call free SA:%p\n", sav));
548		key_freesav(sav, KEY_SADB_UNLOCKED);
549	}
550	if (m)
551		m_freem(m);
552	return;
553}
554#endif /* INET */
555
556#if INET6
557int
558ah6_input(mp, offp)
559	struct mbuf **mp;
560	int *offp;
561{
562	struct mbuf *m = *mp;
563	int off = *offp;
564	struct ip6_hdr *ip6;
565	struct ah *ah;
566	u_int32_t spi;
567	const struct ah_algorithm *algo;
568	size_t siz;
569	size_t siz1;
570	u_char *cksum;
571	struct secasvar *sav = NULL;
572	u_int16_t nxt;
573	size_t stripsiz = 0;
574
575
576#ifndef PULLDOWN_TEST
577	IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), {return IPPROTO_DONE;});
578	ah = (struct ah *)(mtod(m, caddr_t) + off);
579#else
580	IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
581	if (ah == NULL) {
582		ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n"));
583		ipsec6stat.in_inval++;
584		return IPPROTO_DONE;
585	}
586#endif
587	ip6 = mtod(m, struct ip6_hdr *);
588	nxt = ah->ah_nxt;
589
590	/* find the sassoc.  */
591	spi = ah->ah_spi;
592
593	if (ntohs(ip6->ip6_plen) == 0) {
594		ipseclog((LOG_ERR, "IPv6 AH input: "
595		    "AH with IPv6 jumbogram is not supported.\n"));
596		IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
597		goto fail;
598	}
599
600	if ((sav = key_allocsa(AF_INET6,
601	                      (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
602	                      IPPROTO_AH, spi)) == 0) {
603		ipseclog((LOG_WARNING,
604		    "IPv6 AH input: no key association found for spi %u\n",
605		    (u_int32_t)ntohl(spi)));
606		IPSEC_STAT_INCREMENT(ipsec6stat.in_nosa);
607		goto fail;
608	}
609	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
610		printf("DP ah6_input called to allocate SA:%p\n", sav));
611	if (sav->state != SADB_SASTATE_MATURE
612	 && sav->state != SADB_SASTATE_DYING) {
613		ipseclog((LOG_DEBUG,
614		    "IPv6 AH input: non-mature/dying SA found for spi %u; ",
615		    (u_int32_t)ntohl(spi)));
616		IPSEC_STAT_INCREMENT(ipsec6stat.in_badspi);
617		goto fail;
618	}
619
620	algo = ah_algorithm_lookup(sav->alg_auth);
621	if (!algo) {
622		ipseclog((LOG_DEBUG, "IPv6 AH input: "
623		    "unsupported authentication algorithm for spi %u\n",
624		    (u_int32_t)ntohl(spi)));
625		IPSEC_STAT_INCREMENT(ipsec6stat.in_badspi);
626		goto fail;
627	}
628
629	siz = (*algo->sumsiz)(sav);
630	siz1 = ((siz + 3) & ~(4 - 1));
631
632	/*
633	 * sanity checks for header, 1.
634	 */
635    {
636	int sizoff;
637
638	sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
639
640	/*
641	 * Here, we do not do "siz1 == siz".  See ah4_input() for complete
642	 * description.
643	 */
644	if (siz1 < siz) {
645		ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input "
646		    "(%lu, should be at least %lu): %s\n",
647		    (u_long)siz1, (u_long)siz,
648		    ipsec6_logpacketstr(ip6, spi)));
649		IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
650		goto fail;
651	}
652	if ((ah->ah_len << 2) - sizoff != siz1) {
653		ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input "
654		    "(%d should be %lu): %s\n",
655		    (ah->ah_len << 2) - sizoff, (u_long)siz1,
656		    ipsec6_logpacketstr(ip6, spi)));
657		IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
658		goto fail;
659	}
660#ifndef PULLDOWN_TEST
661	IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1,
662		{return IPPROTO_DONE;});
663#else
664	IP6_EXTHDR_GET(ah, struct ah *, m, off,
665		sizeof(struct ah) + sizoff + siz1);
666	if (ah == NULL) {
667		ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part"));
668		IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
669		m = NULL;
670		goto fail;
671	}
672#endif
673    }
674
675	/*
676	 * check for sequence number.
677	 */
678	if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
679		if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
680			; /*okey*/
681		else {
682			IPSEC_STAT_INCREMENT(ipsec6stat.in_ahreplay);
683			ipseclog((LOG_WARNING,
684			    "replay packet in IPv6 AH input: %s %s\n",
685			    ipsec6_logpacketstr(ip6, spi),
686			    ipsec_logsastr(sav)));
687			goto fail;
688		}
689	}
690
691	/*
692	 * alright, it seems sane.  now we are going to check the
693	 * cryptographic checksum.
694	 */
695	cksum = _MALLOC(siz1, M_TEMP, M_NOWAIT);
696	if (!cksum) {
697		ipseclog((LOG_DEBUG, "IPv6 AH input: "
698		    "couldn't alloc temporary region for cksum\n"));
699		IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
700		goto fail;
701	}
702
703	if (ah6_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
704		FREE(cksum, M_TEMP);
705		IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
706		goto fail;
707	}
708	IPSEC_STAT_INCREMENT(ipsec6stat.in_ahhist[sav->alg_auth]);
709
710    {
711	caddr_t sumpos = NULL;
712
713	if (sav->flags & SADB_X_EXT_OLD) {
714		/* RFC 1826 */
715		sumpos = (caddr_t)(ah + 1);
716	} else {
717		/* RFC 2402 */
718		sumpos = (caddr_t)(((struct newah *)ah) + 1);
719	}
720
721	if (bcmp(sumpos, cksum, siz) != 0) {
722		ipseclog((LOG_WARNING,
723		    "checksum mismatch in IPv6 AH input: %s %s\n",
724		    ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
725		FREE(cksum, M_TEMP);
726		IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthfail);
727		goto fail;
728	}
729    }
730
731	FREE(cksum, M_TEMP);
732
733	m->m_flags |= M_AUTHIPHDR;
734	m->m_flags |= M_AUTHIPDGM;
735
736#if 0
737	/*
738	 * looks okey, but we need more sanity check.
739	 * XXX should elaborate.
740	 */
741	if (ah->ah_nxt == IPPROTO_IPV6) {
742		struct ip6_hdr *nip6;
743		size_t sizoff;
744
745		sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
746
747		IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1
748				+ sizeof(struct ip6_hdr),
749				{return IPPROTO_DONE;});
750
751		nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1);
752		if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src)
753		 || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) {
754			m->m_flags &= ~M_AUTHIPHDR;
755			m->m_flags &= ~M_AUTHIPDGM;
756		}
757	} else if (ah->ah_nxt == IPPROTO_IPIP) {
758		m->m_flags &= ~M_AUTHIPHDR;
759		m->m_flags &= ~M_AUTHIPDGM;
760	} else if (ah->ah_nxt == IPPROTO_IP) {
761		m->m_flags &= ~M_AUTHIPHDR;
762		m->m_flags &= ~M_AUTHIPDGM;
763	}
764#endif
765
766	if (m->m_flags & M_AUTHIPHDR
767	 && m->m_flags & M_AUTHIPDGM) {
768#if 0
769		ipseclog((LOG_DEBUG,
770		    "IPv6 AH input: authentication succeess\n"));
771#endif
772		IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthsucc);
773	} else {
774		ipseclog((LOG_WARNING,
775		    "authentication failed in IPv6 AH input: %s %s\n",
776		    ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
777		IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthfail);
778		goto fail;
779	}
780
781	/*
782	 * update sequence number.
783	 */
784	if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
785		if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
786			IPSEC_STAT_INCREMENT(ipsec6stat.in_ahreplay);
787			goto fail;
788		}
789	}
790
791	/* was it transmitted over the IPsec tunnel SA? */
792	if (sav->flags & SADB_X_EXT_OLD) {
793		/* RFC 1826 */
794		stripsiz = sizeof(struct ah) + siz1;
795	} else {
796		/* RFC 2402 */
797		stripsiz = sizeof(struct newah) + siz1;
798	}
799	if (ipsec6_tunnel_validate(m, off + stripsiz, nxt, sav)) {
800		/*
801		 * strip off all the headers that precedes AH.
802		 *	IP6 xx AH IP6' payload -> IP6' payload
803		 *
804		 * XXX more sanity checks
805		 * XXX relationship with gif?
806		 */
807		u_int32_t flowinfo;	/*net endian*/
808
809		flowinfo = ip6->ip6_flow;
810		m_adj(m, off + stripsiz);
811		if (m->m_len < sizeof(*ip6)) {
812			/*
813			 * m_pullup is prohibited in KAME IPv6 input processing
814			 * but there's no other way!
815			 */
816			m = m_pullup(m, sizeof(*ip6));
817			if (!m) {
818				IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
819				goto fail;
820			}
821		}
822		ip6 = mtod(m, struct ip6_hdr *);
823		/* ECN consideration. */
824		ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow);
825		if (!key_checktunnelsanity(sav, AF_INET6,
826			    (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) {
827			ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
828			    "in IPv6 AH input: %s %s\n",
829			    ipsec6_logpacketstr(ip6, spi),
830			    ipsec_logsastr(sav)));
831			IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
832			goto fail;
833		}
834
835#if 1
836		/*
837		 * should the inner packet be considered authentic?
838		 * see comment in ah4_input().
839		 */
840		m->m_flags &= ~M_AUTHIPHDR;
841		m->m_flags &= ~M_AUTHIPDGM;
842#endif
843
844		key_sa_recordxfer(sav, m);
845		if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
846		    ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) {
847			IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem);
848			goto fail;
849		}
850		proto_input(PF_INET6, m);
851		nxt = IPPROTO_DONE;
852	} else {
853		/*
854		 * strip off AH.
855		 */
856		char *prvnxtp;
857
858		/*
859		 * Copy the value of the next header field of AH to the
860		 * next header field of the previous header.
861		 * This is necessary because AH will be stripped off below.
862		 */
863		prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
864		*prvnxtp = nxt;
865
866		ip6 = mtod(m, struct ip6_hdr *);
867#ifndef PULLDOWN_TEST
868		/*
869		 * We do deep-copy since KAME requires that
870		 * the packet is placed in a single mbuf.
871		 */
872		ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
873		m->m_data += stripsiz;
874		m->m_len -= stripsiz;
875		m->m_pkthdr.len -= stripsiz;
876#else
877		/*
878		 * even in m_pulldown case, we need to strip off AH so that
879		 * we can compute checksum for multiple AH correctly.
880		 */
881		if (m->m_len >= stripsiz + off) {
882			ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
883			m->m_data += stripsiz;
884			m->m_len -= stripsiz;
885			m->m_pkthdr.len -= stripsiz;
886		} else {
887			/*
888			 * this comes with no copy if the boundary is on
889			 * cluster
890			 */
891			struct mbuf *n;
892
893			n = m_split(m, off, M_DONTWAIT);
894			if (n == NULL) {
895				/* m is retained by m_split */
896				goto fail;
897			}
898			m_adj(n, stripsiz);
899			/* m_cat does not update m_pkthdr.len */
900			m->m_pkthdr.len += n->m_pkthdr.len;
901			m_cat(m, n);
902		}
903#endif
904		ip6 = mtod(m, struct ip6_hdr *);
905		/* XXX jumbogram */
906		ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
907
908		key_sa_recordxfer(sav, m);
909		if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
910			IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem);
911			goto fail;
912		}
913	}
914
915	*offp = off;
916	*mp = m;
917
918	if (sav) {
919		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
920			printf("DP ah6_input call free SA:%p\n", sav));
921		key_freesav(sav, KEY_SADB_UNLOCKED);
922	}
923	IPSEC_STAT_INCREMENT(ipsec6stat.in_success);
924	return nxt;
925
926fail:
927	if (sav) {
928		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
929			printf("DP ah6_input call free SA:%p\n", sav));
930		key_freesav(sav, KEY_SADB_UNLOCKED);
931	}
932	if (m)
933		m_freem(m);
934	return IPPROTO_DONE;
935}
936
937void
938ah6_ctlinput(cmd, sa, d)
939	int cmd;
940	struct sockaddr *sa;
941	void *d;
942{
943	const struct newah *ahp;
944	struct newah ah;
945	struct secasvar *sav;
946	struct ip6_hdr *ip6;
947	struct mbuf *m;
948	struct ip6ctlparam *ip6cp = NULL;
949	int off;
950	struct sockaddr_in6 *sa6_src, *sa6_dst;
951
952	if (sa->sa_family != AF_INET6 ||
953	    sa->sa_len != sizeof(struct sockaddr_in6))
954		return;
955	if ((unsigned)cmd >= PRC_NCMDS)
956		return;
957
958	/* if the parameter is from icmp6, decode it. */
959	if (d != NULL) {
960		ip6cp = (struct ip6ctlparam *)d;
961		m = ip6cp->ip6c_m;
962		ip6 = ip6cp->ip6c_ip6;
963		off = ip6cp->ip6c_off;
964	} else {
965		m = NULL;
966		ip6 = NULL;
967	}
968
969	if (ip6) {
970		/*
971		 * XXX: We assume that when ip6 is non NULL,
972		 * M and OFF are valid.
973		 */
974
975		/* check if we can safely examine src and dst ports */
976		if (m->m_pkthdr.len < off + sizeof(ah))
977			return;
978
979		if (m->m_len < off + sizeof(ah)) {
980			/*
981			 * this should be rare case,
982			 * so we compromise on this copy...
983			 */
984			m_copydata(m, off, sizeof(ah), (caddr_t)&ah);
985			ahp = &ah;
986		} else
987			ahp = (struct newah *)(mtod(m, caddr_t) + off);
988
989		if (cmd == PRC_MSGSIZE) {
990			int valid = 0;
991
992			/*
993			 * Check to see if we have a valid SA corresponding to
994			 * the address in the ICMP message payload.
995			 */
996			sa6_src = ip6cp->ip6c_src;
997			sa6_dst = (struct sockaddr_in6 *)sa;
998			sav = key_allocsa(AF_INET6,
999					  (caddr_t)&sa6_src->sin6_addr,
1000					  (caddr_t)&sa6_dst->sin6_addr,
1001					  IPPROTO_AH, ahp->ah_spi);
1002			if (sav) {
1003				if (sav->state == SADB_SASTATE_MATURE ||
1004				    sav->state == SADB_SASTATE_DYING)
1005					valid++;
1006				key_freesav(sav, KEY_SADB_UNLOCKED);
1007			}
1008
1009			/* XXX Further validation? */
1010
1011			/*
1012			 * Depending on the value of "valid" and routing table
1013			 * size (mtudisc_{hi,lo}wat), we will:
1014			 * - recalcurate the new MTU and create the
1015			 *   corresponding routing entry, or
1016			 * - ignore the MTU change notification.
1017			 */
1018			icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
1019		}
1020
1021		/* we normally notify single pcb here */
1022	} else {
1023		/* we normally notify any pcb here */
1024	}
1025}
1026#endif /* INET6 */
1027