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