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