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_output.c,v 1.1.2.3 2001/07/03 11:01:49 ume Exp $	*/
30/*	$KAME: ah_output.c,v 1.30 2001/02/21 00:50:53 itojun 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#define _IP_VHL
66
67#include <sys/param.h>
68#include <sys/systm.h>
69#include <sys/malloc.h>
70#include <sys/mbuf.h>
71#include <sys/domain.h>
72#include <sys/protosw.h>
73#include <sys/socket.h>
74#include <sys/socketvar.h>
75#include <sys/errno.h>
76#include <sys/time.h>
77#include <sys/kernel.h>
78#include <sys/syslog.h>
79
80#include <net/if.h>
81#include <net/route.h>
82
83#include <netinet/in.h>
84
85#include <netinet/in_systm.h>
86#include <netinet/ip.h>
87#include <netinet/in_var.h>
88
89#if INET6
90#include <netinet/ip6.h>
91#include <netinet6/ip6_var.h>
92#include <netinet/icmp6.h>
93#endif
94
95#include <netinet6/ipsec.h>
96#if INET6
97#include <netinet6/ipsec6.h>
98#endif
99#include <netinet6/ah.h>
100#if INET6
101#include <netinet6/ah6.h>
102#endif
103#include <netkey/key.h>
104#include <netkey/keydb.h>
105
106#include <net/net_osdep.h>
107
108#if INET
109static struct in_addr *ah4_finaldst(struct mbuf *);
110#endif
111
112extern lck_mtx_t *sadb_mutex;
113
114/*
115 * compute AH header size.
116 * transport mode only.  for tunnel mode, we should implement
117 * virtual interface, and control MTU/MSS by the interface MTU.
118 */
119size_t
120ah_hdrsiz(isr)
121	struct ipsecrequest *isr;
122{
123
124	/* sanity check */
125	if (isr == NULL)
126		panic("ah_hdrsiz: NULL was passed.\n");
127
128	if (isr->saidx.proto != IPPROTO_AH)
129		panic("unsupported mode passed to ah_hdrsiz");
130
131#if 0
132	{
133
134		lck_mtx_lock(sadb_mutex);
135		const struct ah_algorithm *algo;
136		size_t hdrsiz;
137
138		/*%%%%% this needs to change - no sav in ipsecrequest any more */
139		if (isr->sav == NULL)
140			goto estimate;
141		if (isr->sav->state != SADB_SASTATE_MATURE
142		 && isr->sav->state != SADB_SASTATE_DYING)
143			goto estimate;
144
145		/* we need transport mode AH. */
146		algo = ah_algorithm_lookup(isr->sav->alg_auth);
147		if (!algo)
148			goto estimate;
149
150		/*
151		 * XXX
152		 * right now we don't calcurate the padding size.  simply
153		 * treat the padding size as constant, for simplicity.
154		 *
155		 * XXX variable size padding support
156		 */
157		hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1));
158		if (isr->sav->flags & SADB_X_EXT_OLD)
159			hdrsiz += sizeof(struct ah);
160		else
161			hdrsiz += sizeof(struct newah);
162
163		lck_mtx_unlock(sadb_mutex);
164		return hdrsiz;
165	}
166
167estimate:
168#endif
169
170    //lck_mtx_unlock(sadb_mutex);
171	/* ASSUMING:
172	 *	sizeof(struct newah) > sizeof(struct ah).
173	 *	16 = (16 + 3) & ~(4 - 1).
174	 */
175	return sizeof(struct newah) + 16;
176}
177
178#if INET
179/*
180 * Modify the packet so that it includes the authentication data.
181 * The mbuf passed must start with IPv4 header.
182 *
183 * assumes that the first mbuf contains IPv4 header + option only.
184 * the function does not modify m.
185 */
186int
187ah4_output(m, sav)
188	struct mbuf *m;
189	struct secasvar *sav;
190{
191	const struct ah_algorithm *algo;
192	u_int32_t spi;
193	u_char *ahdrpos;
194	u_char *ahsumpos = NULL;
195	size_t hlen = 0;	/*IP header+option in bytes*/
196	size_t plen = 0;	/*AH payload size in bytes*/
197	size_t ahlen = 0;	/*plen + sizeof(ah)*/
198	struct ip *ip;
199	struct in_addr dst = { 0 };
200	struct in_addr *finaldst;
201	int error;
202
203	/* sanity checks */
204	if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
205		ip = mtod(m, struct ip *);
206		ipseclog((LOG_DEBUG, "ah4_output: internal error: "
207			"sav->replay is null: %x->%x, SPI=%u\n",
208			(u_int32_t)ntohl(ip->ip_src.s_addr),
209			(u_int32_t)ntohl(ip->ip_dst.s_addr),
210			(u_int32_t)ntohl(sav->spi)));
211		IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
212		m_freem(m);
213		return EINVAL;
214	}
215
216	algo = ah_algorithm_lookup(sav->alg_auth);
217	if (!algo) {
218		ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: "
219		    "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
220		IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
221		m_freem(m);
222		return EINVAL;
223	}
224	spi = sav->spi;
225
226	/*
227	 * determine the size to grow.
228	 */
229	if (sav->flags & SADB_X_EXT_OLD) {
230		/* RFC 1826 */
231		plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/
232		ahlen = plen + sizeof(struct ah);
233	} else {
234		/* RFC 2402 */
235		plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/
236		ahlen = plen + sizeof(struct newah);
237	}
238
239	/*
240	 * grow the mbuf to accomodate AH.
241	 */
242	ip = mtod(m, struct ip *);
243#ifdef _IP_VHL
244	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
245#else
246	hlen = ip->ip_hl << 2;
247#endif
248
249	if (m->m_len != hlen)
250		panic("ah4_output: assumption failed (first mbuf length)");
251	if (M_LEADINGSPACE(m->m_next) < ahlen) {
252		struct mbuf *n;
253		MGET(n, M_DONTWAIT, MT_DATA);
254		if (!n) {
255			ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n",
256			    __LINE__));
257			m_freem(m);
258			return ENOBUFS;
259		}
260		n->m_len = ahlen;
261		n->m_next = m->m_next;
262		m->m_next = n;
263		m->m_pkthdr.len += ahlen;
264		ahdrpos = mtod(n, u_char *);
265	} else {
266		m->m_next->m_len += ahlen;
267		m->m_next->m_data -= ahlen;
268		m->m_pkthdr.len += ahlen;
269		ahdrpos = mtod(m->m_next, u_char *);
270	}
271
272	ip = mtod(m, struct ip *);	/*just to be sure*/
273
274	/*
275	 * initialize AH.
276	 */
277	if (sav->flags & SADB_X_EXT_OLD) {
278		struct ah *ahdr;
279
280		ahdr = (struct ah *)(void *)ahdrpos;
281		ahsumpos = (u_char *)(ahdr + 1);
282		ahdr->ah_len = plen >> 2;
283		ahdr->ah_nxt = ip->ip_p;
284		ahdr->ah_reserve = htons(0);
285		ahdr->ah_spi = spi;
286		bzero(ahdr + 1, plen);
287	} else {
288		struct newah *ahdr;
289
290		ahdr = (struct newah *)(void *)ahdrpos;
291		ahsumpos = (u_char *)(ahdr + 1);
292		ahdr->ah_len = (plen >> 2) + 1;	/* plus one for seq# */
293		ahdr->ah_nxt = ip->ip_p;
294		ahdr->ah_reserve = htons(0);
295		ahdr->ah_spi = spi;
296		if (sav->replay->count == ~0) {
297			if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
298				/* XXX Is it noisy ? */
299				ipseclog((LOG_WARNING,
300				    "replay counter overflowed. %s\n",
301				    ipsec_logsastr(sav)));
302				IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
303				m_freem(m);
304				return EINVAL;
305			}
306		}
307		lck_mtx_lock(sadb_mutex);
308		sav->replay->count++;
309		lck_mtx_unlock(sadb_mutex);
310		/*
311		 * XXX sequence number must not be cycled, if the SA is
312		 * installed by IKE daemon.
313		 */
314		ahdr->ah_seq = htonl(sav->replay->count);
315		bzero(ahdr + 1, plen);
316	}
317
318	/*
319	 * modify IPv4 header.
320	 */
321	ip->ip_p = IPPROTO_AH;
322	if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len)))
323		ip->ip_len = htons(ntohs(ip->ip_len) + ahlen);
324	else {
325		ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n"));
326		IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
327		m_freem(m);
328		return EMSGSIZE;
329	}
330
331	/*
332	 * If there is source routing option, update destination field in
333	 * the IPv4 header to the final destination.
334	 * Note that we do not need to update source routing option itself
335	 * (as done in IPv4 AH processing -- see ip6_output()), since
336	 * source routing option is not part of the ICV computation.
337	 */
338	finaldst = ah4_finaldst(m);
339	if (finaldst) {
340		dst.s_addr = ip->ip_dst.s_addr;
341		ip->ip_dst.s_addr = finaldst->s_addr;
342	}
343
344	/*
345	 * calcurate the checksum, based on security association
346	 * and the algorithm specified.
347	 */
348	error = ah4_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
349	if (error) {
350		ipseclog((LOG_ERR,
351		    "error after ah4_calccksum, called from ah4_output"));
352		m_freem(m);
353		m = NULL;
354		IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
355		return error;
356	}
357
358	if (finaldst) {
359		ip = mtod(m, struct ip *);	/*just to make sure*/
360		ip->ip_dst.s_addr = dst.s_addr;
361	}
362	lck_mtx_lock(sadb_stat_mutex);
363	ipsecstat.out_success++;
364	ipsecstat.out_ahhist[sav->alg_auth]++;
365	lck_mtx_unlock(sadb_stat_mutex);
366	key_sa_recordxfer(sav, m);
367
368	return 0;
369}
370#endif
371
372/* Calculate AH length */
373int
374ah_hdrlen(sav)
375	struct secasvar *sav;
376{
377	const struct ah_algorithm *algo;
378	int plen, ahlen;
379
380	algo = ah_algorithm_lookup(sav->alg_auth);
381	if (!algo)
382		return 0;
383	if (sav->flags & SADB_X_EXT_OLD) {
384		/* RFC 1826 */
385		plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1);	/*XXX pad to 8byte?*/
386		ahlen = plen + sizeof(struct ah);
387	} else {
388		/* RFC 2402 */
389		plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1);	/*XXX pad to 8byte?*/
390		ahlen = plen + sizeof(struct newah);
391	}
392
393	return(ahlen);
394}
395
396#if INET6
397/*
398 * Fill in the Authentication Header and calculate checksum.
399 */
400int
401ah6_output(m, nexthdrp, md, sav)
402	struct mbuf *m;
403	u_char *nexthdrp;
404	struct mbuf *md;
405	struct secasvar *sav;
406{
407	struct mbuf *mprev;
408	struct mbuf *mah;
409	const struct ah_algorithm *algo;
410	u_int32_t spi;
411	u_char *ahsumpos = NULL;
412	size_t plen;	/*AH payload size in bytes*/
413	int error = 0;
414	int ahlen;
415	struct ip6_hdr *ip6;
416
417	if (m->m_len < sizeof(struct ip6_hdr)) {
418		ipseclog((LOG_DEBUG, "ah6_output: first mbuf too short\n"));
419		m_freem(m);
420		return EINVAL;
421	}
422
423	ahlen = ah_hdrlen(sav);
424	if (ahlen == 0)
425		return 0;
426
427	for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
428		;
429	if (!mprev || mprev->m_next != md) {
430		ipseclog((LOG_DEBUG, "ah6_output: md is not in chain\n"));
431		m_freem(m);
432		return EINVAL;
433	}
434
435	MGET(mah, M_DONTWAIT, MT_DATA);
436	if (!mah) {
437		m_freem(m);
438		return ENOBUFS;
439	}
440	if (ahlen > MLEN) {
441		MCLGET(mah, M_DONTWAIT);
442		if ((mah->m_flags & M_EXT) == 0) {
443			m_free(mah);
444			m_freem(m);
445			return ENOBUFS;
446		}
447	}
448	mah->m_len = ahlen;
449	mah->m_next = md;
450	mprev->m_next = mah;
451	m->m_pkthdr.len += ahlen;
452
453	/* fix plen */
454	if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) {
455		ipseclog((LOG_ERR,
456		    "ip6_output: AH with IPv6 jumbogram is not supported\n"));
457		m_freem(m);
458		return EINVAL;
459	}
460	ip6 = mtod(m, struct ip6_hdr *);
461	ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
462
463	if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
464		ipseclog((LOG_DEBUG, "ah6_output: internal error: "
465			  "sav->replay is null: SPI=%u\n",
466			  (u_int32_t)ntohl(sav->spi)));
467		IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
468		m_freem(m);
469		return EINVAL;
470	}
471
472	algo = ah_algorithm_lookup(sav->alg_auth);
473	if (!algo) {
474		ipseclog((LOG_ERR, "ah6_output: unsupported algorithm: "
475		    "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
476		IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
477		m_freem(m);
478		return EINVAL;
479	}
480	spi = sav->spi;
481
482	/*
483	 * initialize AH.
484	 */
485	if (sav->flags & SADB_X_EXT_OLD) {
486		struct ah *ahdr = mtod(mah, struct ah *);
487
488		plen = mah->m_len - sizeof(struct ah);
489		ahsumpos = (u_char *)(ahdr + 1);
490		ahdr->ah_nxt = *nexthdrp;
491		*nexthdrp = IPPROTO_AH;
492		ahdr->ah_len = plen >> 2;
493		ahdr->ah_reserve = htons(0);
494		ahdr->ah_spi = spi;
495		bzero(ahdr + 1, plen);
496	} else {
497		struct newah *ahdr = mtod(mah, struct newah *);
498
499		plen = mah->m_len - sizeof(struct newah);
500		ahsumpos = (u_char *)(ahdr + 1);
501		ahdr->ah_nxt = *nexthdrp;
502		*nexthdrp = IPPROTO_AH;
503		ahdr->ah_len = (plen >> 2) + 1;	/* plus one for seq# */
504		ahdr->ah_reserve = htons(0);
505		ahdr->ah_spi = spi;
506		if (sav->replay->count == ~0) {
507			if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
508				/* XXX Is it noisy ? */
509				ipseclog((LOG_WARNING,
510				     "replay counter overflowed. %s\n",
511				    ipsec_logsastr(sav)));
512				IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
513				m_freem(m);
514				return EINVAL;
515			}
516		}
517		lck_mtx_lock(sadb_mutex);
518		sav->replay->count++;
519		lck_mtx_unlock(sadb_mutex);
520		/*
521		 * XXX sequence number must not be cycled, if the SA is
522		 * installed by IKE daemon.
523		 */
524		ahdr->ah_seq = htonl(sav->replay->count);
525		bzero(ahdr + 1, plen);
526	}
527
528	/*
529	 * calcurate the checksum, based on security association
530	 * and the algorithm specified.
531	 */
532	error = ah6_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
533	if (error) {
534		IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
535		m_freem(m);
536	} else {
537		IPSEC_STAT_INCREMENT(ipsec6stat.out_success);
538		key_sa_recordxfer(sav, m);
539	}
540	IPSEC_STAT_INCREMENT(ipsec6stat.out_ahhist[sav->alg_auth]);
541
542	return(error);
543}
544#endif
545
546#if INET
547/*
548 * Find the final destination if there is loose/strict source routing option.
549 * Returns NULL if there's no source routing options.
550 * Returns NULL on errors too.
551 * Note that this function will return a pointer INTO the given parameter,
552 * struct mbuf *m.
553 * The mbuf must be pulled up toward, at least, ip option part.
554 */
555static struct in_addr *
556ah4_finaldst(m)
557	struct mbuf *m;
558{
559	struct ip *ip;
560	int optlen;
561	u_char *q;
562	int i;
563	int hlen;
564
565	if (!m)
566		panic("ah4_finaldst: m == NULL");
567	ip = mtod(m, struct ip *);
568#ifdef _IP_VHL
569	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
570#else
571	hlen = ip->ip_hl << 2;
572#endif
573
574	if (m->m_len < hlen) {
575		ipseclog((LOG_DEBUG,
576		    "ah4_finaldst: parameter mbuf wrong (not pulled up)\n"));
577		return NULL;
578	}
579
580	if (hlen == sizeof(struct ip))
581		return NULL;
582
583	optlen = hlen - sizeof(struct ip);
584	if (optlen < 0) {
585		ipseclog((LOG_DEBUG, "ah4_finaldst: wrong optlen %d\n",
586		    optlen));
587		return NULL;
588	}
589
590	q = (u_char *)(ip + 1);
591	i = 0;
592	while (i < optlen) {
593		if (i + IPOPT_OPTVAL >= optlen)
594			return NULL;
595		if (q[i + IPOPT_OPTVAL] == IPOPT_EOL ||
596		    q[i + IPOPT_OPTVAL] == IPOPT_NOP ||
597		    i + IPOPT_OLEN < optlen)
598			;
599		else
600			return NULL;
601
602		switch (q[i + IPOPT_OPTVAL]) {
603		case IPOPT_EOL:
604			i = optlen;	/* bye */
605			break;
606		case IPOPT_NOP:
607			i++;
608			break;
609		case IPOPT_LSRR:
610		case IPOPT_SSRR:
611			if (q[i + IPOPT_OLEN] < 2 + sizeof(struct in_addr) ||
612			    optlen - i < q[i + IPOPT_OLEN]) {
613				ipseclog((LOG_ERR,
614				    "ip_finaldst: invalid IP option "
615				    "(code=%02x len=%02x)\n",
616				    q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
617				return NULL;
618			}
619			i += q[i + IPOPT_OLEN] - sizeof(struct in_addr);
620			return (struct in_addr *)(void *)(q + i);
621		default:
622			if (q[i + IPOPT_OLEN] < 2 ||
623			    optlen - i < q[i + IPOPT_OLEN]) {
624				ipseclog((LOG_ERR,
625				    "ip_finaldst: invalid IP option "
626				    "(code=%02x len=%02x)\n",
627				    q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
628				return NULL;
629			}
630			i += q[i + IPOPT_OLEN];
631			break;
632		}
633	}
634	return NULL;
635}
636#endif
637