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