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