]>
git.saurik.com Git - apple/xnu.git/blob - bsd/netinet6/ah_output.c
   2  * Copyright (c) 2008-2011 Apple Inc. All rights reserved. 
   4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 
   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. 
  15  * Please obtain a copy of the License at 
  16  * http://www.opensource.apple.com/apsl/ and read it before using this file. 
  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. 
  26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 
  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 $      */ 
  33  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 
  34  * All rights reserved. 
  36  * Redistribution and use in source and binary forms, with or without 
  37  * modification, are permitted provided that the following conditions 
  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. 
  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 
  62  * RFC1826/2402 authentication header. 
  67 #include <sys/param.h> 
  68 #include <sys/systm.h> 
  69 #include <sys/malloc.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> 
  77 #include <sys/kernel.h> 
  78 #include <sys/syslog.h> 
  81 #include <net/route.h> 
  83 #include <netinet/in.h> 
  85 #include <netinet/in_systm.h> 
  86 #include <netinet/ip.h> 
  87 #include <netinet/in_var.h> 
  90 #include <netinet/ip6.h> 
  91 #include <netinet6/ip6_var.h> 
  92 #include <netinet/icmp6.h> 
  95 #include <netinet6/ipsec.h> 
  97 #include <netinet6/ipsec6.h> 
  99 #include <netinet6/ah.h> 
 101 #include <netinet6/ah6.h> 
 103 #include <netkey/key.h> 
 104 #include <netkey/keydb.h> 
 106 #include <net/net_osdep.h> 
 109 static struct in_addr 
*ah4_finaldst(struct mbuf 
*); 
 112 extern lck_mtx_t 
*sadb_mutex
; 
 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. 
 121         struct ipsecrequest 
*isr
; 
 126                 panic("ah_hdrsiz: NULL was passed.\n"); 
 128         if (isr
->saidx
.proto 
!= IPPROTO_AH
) 
 129                 panic("unsupported mode passed to ah_hdrsiz"); 
 134                 lck_mtx_lock(sadb_mutex
); 
 135                 const struct ah_algorithm 
*algo
; 
 138                 /*%%%%% this needs to change - no sav in ipsecrequest any more */ 
 139                 if (isr
->sav 
== NULL
) 
 141                 if (isr
->sav
->state 
!= SADB_SASTATE_MATURE
 
 142                  && isr
->sav
->state 
!= SADB_SASTATE_DYING
) 
 145                 /* we need transport mode AH. */ 
 146                 algo 
= ah_algorithm_lookup(isr
->sav
->alg_auth
); 
 152                  * right now we don't calcurate the padding size.  simply 
 153                  * treat the padding size as constant, for simplicity. 
 155                  * XXX variable size padding support 
 157                 hdrsiz 
= (((*algo
->sumsiz
)(isr
->sav
) + 3) & ~(4 - 1)); 
 158                 if (isr
->sav
->flags 
& SADB_X_EXT_OLD
) 
 159                         hdrsiz 
+= sizeof(struct ah
); 
 161                         hdrsiz 
+= sizeof(struct newah
); 
 163                 lck_mtx_unlock(sadb_mutex
); 
 170     //lck_mtx_unlock(sadb_mutex); 
 172          *      sizeof(struct newah) > sizeof(struct ah). 
 173          *      16 = (16 + 3) & ~(4 - 1). 
 175         return sizeof(struct newah
) + 16; 
 180  * Modify the packet so that it includes the authentication data. 
 181  * The mbuf passed must start with IPv4 header. 
 183  * assumes that the first mbuf contains IPv4 header + option only. 
 184  * the function does not modify m. 
 189         struct secasvar 
*sav
; 
 191         const struct ah_algorithm 
*algo
; 
 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)*/ 
 199         struct in_addr dst 
= { 0 }; 
 200         struct in_addr 
*finaldst
; 
 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
); 
 216         algo 
= ah_algorithm_lookup(sav
->alg_auth
); 
 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
); 
 227          * determine the size to grow. 
 229         if (sav
->flags 
& SADB_X_EXT_OLD
) { 
 231                 plen 
= ((*algo
->sumsiz
)(sav
) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ 
 232                 ahlen 
= plen 
+ sizeof(struct ah
); 
 235                 plen 
= ((*algo
->sumsiz
)(sav
) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ 
 236                 ahlen 
= plen 
+ sizeof(struct newah
); 
 240          * grow the mbuf to accomodate AH. 
 242         ip 
= mtod(m
, struct ip 
*); 
 244         hlen 
= IP_VHL_HL(ip
->ip_vhl
) << 2; 
 246         hlen 
= ip
->ip_hl 
<< 2; 
 249         if (m
->m_len 
!= hlen
) 
 250                 panic("ah4_output: assumption failed (first mbuf length)"); 
 251         if (M_LEADINGSPACE(m
->m_next
) < ahlen
) { 
 253                 MGET(n
, M_DONTWAIT
, MT_DATA
); 
 255                         ipseclog((LOG_DEBUG
, "ENOBUFS in ah4_output %d\n", 
 261                 n
->m_next 
= m
->m_next
; 
 263                 m
->m_pkthdr
.len 
+= ahlen
; 
 264                 ahdrpos 
= mtod(n
, u_char 
*); 
 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 
*); 
 272         ip 
= mtod(m
, struct ip 
*);      /*just to be sure*/ 
 277         if (sav
->flags 
& SADB_X_EXT_OLD
) { 
 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); 
 286                 bzero(ahdr 
+ 1, plen
); 
 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); 
 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
); 
 307                 lck_mtx_lock(sadb_mutex
); 
 308                 sav
->replay
->count
++; 
 309                 lck_mtx_unlock(sadb_mutex
); 
 311                  * XXX sequence number must not be cycled, if the SA is 
 312                  * installed by IKE daemon. 
 314                 ahdr
->ah_seq 
= htonl(sav
->replay
->count
); 
 315                 bzero(ahdr 
+ 1, plen
); 
 319          * modify IPv4 header. 
 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
); 
 325                 ipseclog((LOG_ERR
, "IPv4 AH output: size exceeds limit\n")); 
 326                 IPSEC_STAT_INCREMENT(ipsecstat
.out_inval
); 
 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. 
 338         finaldst 
= ah4_finaldst(m
); 
 340                 dst
.s_addr 
= ip
->ip_dst
.s_addr
; 
 341                 ip
->ip_dst
.s_addr 
= finaldst
->s_addr
; 
 345          * calcurate the checksum, based on security association 
 346          * and the algorithm specified. 
 348         error 
= ah4_calccksum(m
, (caddr_t
)ahsumpos
, plen
, algo
, sav
); 
 351                     "error after ah4_calccksum, called from ah4_output")); 
 354                 IPSEC_STAT_INCREMENT(ipsecstat
.out_inval
); 
 359                 ip 
= mtod(m
, struct ip 
*);      /*just to make sure*/ 
 360                 ip
->ip_dst
.s_addr 
= dst
.s_addr
; 
 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
); 
 372 /* Calculate AH length */ 
 375         struct secasvar 
*sav
; 
 377         const struct ah_algorithm 
*algo
; 
 380         algo 
= ah_algorithm_lookup(sav
->alg_auth
); 
 383         if (sav
->flags 
& SADB_X_EXT_OLD
) { 
 385                 plen 
= ((*algo
->sumsiz
)(sav
) + 3) & ~(4 - 1);   /*XXX pad to 8byte?*/ 
 386                 ahlen 
= plen 
+ sizeof(struct ah
); 
 389                 plen 
= ((*algo
->sumsiz
)(sav
) + 3) & ~(4 - 1);   /*XXX pad to 8byte?*/ 
 390                 ahlen 
= plen 
+ sizeof(struct newah
); 
 398  * Fill in the Authentication Header and calculate checksum. 
 401 ah6_output(m
, nexthdrp
, md
, sav
) 
 405         struct secasvar 
*sav
; 
 409         const struct ah_algorithm 
*algo
; 
 411         u_char 
*ahsumpos 
= NULL
; 
 412         size_t plen
;    /*AH payload size in bytes*/ 
 417         if (m
->m_len 
< sizeof(struct ip6_hdr
)) { 
 418                 ipseclog((LOG_DEBUG
, "ah6_output: first mbuf too short\n")); 
 423         ahlen 
= ah_hdrlen(sav
); 
 427         for (mprev 
= m
; mprev 
&& mprev
->m_next 
!= md
; mprev 
= mprev
->m_next
) 
 429         if (!mprev 
|| mprev
->m_next 
!= md
) { 
 430                 ipseclog((LOG_DEBUG
, "ah6_output: md is not in chain\n")); 
 435         MGET(mah
, M_DONTWAIT
, MT_DATA
); 
 441                 MCLGET(mah
, M_DONTWAIT
); 
 442                 if ((mah
->m_flags 
& M_EXT
) == 0) { 
 451         m
->m_pkthdr
.len 
+= ahlen
; 
 454         if (m
->m_pkthdr
.len 
- sizeof(struct ip6_hdr
) > IPV6_MAXPACKET
) { 
 456                     "ip6_output: AH with IPv6 jumbogram is not supported\n")); 
 460         ip6 
= mtod(m
, struct ip6_hdr 
*); 
 461         ip6
->ip6_plen 
= htons(m
->m_pkthdr
.len 
- sizeof(struct ip6_hdr
)); 
 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
); 
 472         algo 
= ah_algorithm_lookup(sav
->alg_auth
); 
 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
); 
 485         if (sav
->flags 
& SADB_X_EXT_OLD
) { 
 486                 struct ah 
*ahdr 
= mtod(mah
, struct ah 
*); 
 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); 
 495                 bzero(ahdr 
+ 1, plen
); 
 497                 struct newah 
*ahdr 
= mtod(mah
, struct newah 
*); 
 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); 
 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
); 
 517                 lck_mtx_lock(sadb_mutex
); 
 518                 sav
->replay
->count
++; 
 519                 lck_mtx_unlock(sadb_mutex
); 
 521                  * XXX sequence number must not be cycled, if the SA is 
 522                  * installed by IKE daemon. 
 524                 ahdr
->ah_seq 
= htonl(sav
->replay
->count
); 
 525                 bzero(ahdr 
+ 1, plen
); 
 529          * calcurate the checksum, based on security association 
 530          * and the algorithm specified. 
 532         error 
= ah6_calccksum(m
, (caddr_t
)ahsumpos
, plen
, algo
, sav
); 
 534                 IPSEC_STAT_INCREMENT(ipsec6stat
.out_inval
); 
 537                 IPSEC_STAT_INCREMENT(ipsec6stat
.out_success
); 
 538                 key_sa_recordxfer(sav
, m
); 
 540         IPSEC_STAT_INCREMENT(ipsec6stat
.out_ahhist
[sav
->alg_auth
]); 
 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, 
 553  * The mbuf must be pulled up toward, at least, ip option part. 
 555 static struct in_addr 
* 
 566                 panic("ah4_finaldst: m == NULL"); 
 567         ip 
= mtod(m
, struct ip 
*); 
 569         hlen 
= IP_VHL_HL(ip
->ip_vhl
) << 2; 
 571         hlen 
= ip
->ip_hl 
<< 2; 
 574         if (m
->m_len 
< hlen
) { 
 576                     "ah4_finaldst: parameter mbuf wrong (not pulled up)\n")); 
 580         if (hlen 
== sizeof(struct ip
)) 
 583         optlen 
= hlen 
- sizeof(struct ip
); 
 585                 ipseclog((LOG_DEBUG
, "ah4_finaldst: wrong optlen %d\n", 
 590         q 
= (u_char 
*)(ip 
+ 1); 
 593                 if (i 
+ IPOPT_OPTVAL 
>= optlen
) 
 595                 if (q
[i 
+ IPOPT_OPTVAL
] == IPOPT_EOL 
|| 
 596                     q
[i 
+ IPOPT_OPTVAL
] == IPOPT_NOP 
|| 
 597                     i 
+ IPOPT_OLEN 
< optlen
) 
 602                 switch (q
[i 
+ IPOPT_OPTVAL
]) { 
 604                         i 
= optlen
;     /* bye */ 
 611                         if (q
[i 
+ IPOPT_OLEN
] < 2 + sizeof(struct in_addr
) || 
 612                             optlen 
- i 
< q
[i 
+ IPOPT_OLEN
]) { 
 614                                     "ip_finaldst: invalid IP option " 
 615                                     "(code=%02x len=%02x)\n", 
 616                                     q
[i 
+ IPOPT_OPTVAL
], q
[i 
+ IPOPT_OLEN
])); 
 619                         i 
+= q
[i 
+ IPOPT_OLEN
] - sizeof(struct in_addr
); 
 620                         return (struct in_addr 
*)(void *)(q 
+ i
); 
 622                         if (q
[i 
+ IPOPT_OLEN
] < 2 || 
 623                             optlen 
- i 
< q
[i 
+ IPOPT_OLEN
]) { 
 625                                     "ip_finaldst: invalid IP option " 
 626                                     "(code=%02x len=%02x)\n", 
 627                                     q
[i 
+ IPOPT_OPTVAL
], q
[i 
+ IPOPT_OLEN
])); 
 630                         i 
+= q
[i 
+ IPOPT_OLEN
];