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