LCOV - code coverage report
Current view: top level - net/ipv6 - ah6.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 3 353 0.8 %
Date: 2022-03-28 16:04:14 Functions: 1 12 8.3 %
Branches: 2 157 1.3 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-or-later
       2                 :            : /*
       3                 :            :  * Copyright (C)2002 USAGI/WIDE Project
       4                 :            :  *
       5                 :            :  * Authors
       6                 :            :  *
       7                 :            :  *      Mitsuru KANDA @USAGI       : IPv6 Support
       8                 :            :  *      Kazunori MIYAZAWA @USAGI   :
       9                 :            :  *      Kunihiro Ishiguro <kunihiro@ipinfusion.com>
      10                 :            :  *
      11                 :            :  *      This file is derived from net/ipv4/ah.c.
      12                 :            :  */
      13                 :            : 
      14                 :            : #define pr_fmt(fmt) "IPv6: " fmt
      15                 :            : 
      16                 :            : #include <crypto/algapi.h>
      17                 :            : #include <crypto/hash.h>
      18                 :            : #include <linux/module.h>
      19                 :            : #include <linux/slab.h>
      20                 :            : #include <net/ip.h>
      21                 :            : #include <net/ah.h>
      22                 :            : #include <linux/crypto.h>
      23                 :            : #include <linux/pfkeyv2.h>
      24                 :            : #include <linux/string.h>
      25                 :            : #include <linux/scatterlist.h>
      26                 :            : #include <net/ip6_route.h>
      27                 :            : #include <net/icmp.h>
      28                 :            : #include <net/ipv6.h>
      29                 :            : #include <net/protocol.h>
      30                 :            : #include <net/xfrm.h>
      31                 :            : 
      32                 :            : #define IPV6HDR_BASELEN 8
      33                 :            : 
      34                 :            : struct tmp_ext {
      35                 :            : #if IS_ENABLED(CONFIG_IPV6_MIP6)
      36                 :            :                 struct in6_addr saddr;
      37                 :            : #endif
      38                 :            :                 struct in6_addr daddr;
      39                 :            :                 char hdrs[0];
      40                 :            : };
      41                 :            : 
      42                 :            : struct ah_skb_cb {
      43                 :            :         struct xfrm_skb_cb xfrm;
      44                 :            :         void *tmp;
      45                 :            : };
      46                 :            : 
      47                 :            : #define AH_SKB_CB(__skb) ((struct ah_skb_cb *)&((__skb)->cb[0]))
      48                 :            : 
      49                 :          0 : static void *ah_alloc_tmp(struct crypto_ahash *ahash, int nfrags,
      50                 :            :                           unsigned int size)
      51                 :            : {
      52                 :          0 :         unsigned int len;
      53                 :            : 
      54         [ #  # ]:          0 :         len = size + crypto_ahash_digestsize(ahash) +
      55                 :          0 :               (crypto_ahash_alignmask(ahash) &
      56                 :            :                ~(crypto_tfm_ctx_alignment() - 1));
      57                 :            : 
      58                 :          0 :         len = ALIGN(len, crypto_tfm_ctx_alignment());
      59                 :            : 
      60         [ #  # ]:          0 :         len += sizeof(struct ahash_request) + crypto_ahash_reqsize(ahash);
      61                 :          0 :         len = ALIGN(len, __alignof__(struct scatterlist));
      62                 :            : 
      63                 :          0 :         len += sizeof(struct scatterlist) * nfrags;
      64                 :            : 
      65         [ #  # ]:          0 :         return kmalloc(len, GFP_ATOMIC);
      66                 :            : }
      67                 :            : 
      68                 :          0 : static inline struct tmp_ext *ah_tmp_ext(void *base)
      69                 :            : {
      70                 :          0 :         return base + IPV6HDR_BASELEN;
      71                 :            : }
      72                 :            : 
      73                 :          0 : static inline u8 *ah_tmp_auth(u8 *tmp, unsigned int offset)
      74                 :            : {
      75                 :          0 :         return tmp + offset;
      76                 :            : }
      77                 :            : 
      78                 :          0 : static inline u8 *ah_tmp_icv(struct crypto_ahash *ahash, void *tmp,
      79                 :            :                              unsigned int offset)
      80                 :            : {
      81                 :          0 :         return PTR_ALIGN((u8 *)tmp + offset, crypto_ahash_alignmask(ahash) + 1);
      82                 :            : }
      83                 :            : 
      84                 :          0 : static inline struct ahash_request *ah_tmp_req(struct crypto_ahash *ahash,
      85                 :            :                                                u8 *icv)
      86                 :            : {
      87                 :          0 :         struct ahash_request *req;
      88                 :            : 
      89                 :          0 :         req = (void *)PTR_ALIGN(icv + crypto_ahash_digestsize(ahash),
      90                 :            :                                 crypto_tfm_ctx_alignment());
      91                 :            : 
      92         [ #  # ]:          0 :         ahash_request_set_tfm(req, ahash);
      93                 :            : 
      94                 :          0 :         return req;
      95                 :            : }
      96                 :            : 
      97                 :          0 : static inline struct scatterlist *ah_req_sg(struct crypto_ahash *ahash,
      98                 :            :                                              struct ahash_request *req)
      99                 :            : {
     100                 :          0 :         return (void *)ALIGN((unsigned long)(req + 1) +
     101                 :            :                              crypto_ahash_reqsize(ahash),
     102                 :            :                              __alignof__(struct scatterlist));
     103                 :            : }
     104                 :            : 
     105                 :          0 : static bool zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr)
     106                 :            : {
     107                 :          0 :         u8 *opt = (u8 *)opthdr;
     108                 :          0 :         int len = ipv6_optlen(opthdr);
     109                 :          0 :         int off = 0;
     110                 :          0 :         int optlen = 0;
     111                 :            : 
     112                 :          0 :         off += 2;
     113                 :          0 :         len -= 2;
     114                 :            : 
     115         [ #  # ]:          0 :         while (len > 0) {
     116                 :            : 
     117         [ #  # ]:          0 :                 switch (opt[off]) {
     118                 :            : 
     119                 :            :                 case IPV6_TLV_PAD1:
     120                 :            :                         optlen = 1;
     121                 :            :                         break;
     122                 :          0 :                 default:
     123         [ #  # ]:          0 :                         if (len < 2)
     124                 :          0 :                                 goto bad;
     125                 :          0 :                         optlen = opt[off+1]+2;
     126         [ #  # ]:          0 :                         if (len < optlen)
     127                 :          0 :                                 goto bad;
     128         [ #  # ]:          0 :                         if (opt[off] & 0x20)
     129                 :          0 :                                 memset(&opt[off+2], 0, opt[off+1]);
     130                 :            :                         break;
     131                 :            :                 }
     132                 :            : 
     133                 :          0 :                 off += optlen;
     134                 :          0 :                 len -= optlen;
     135                 :            :         }
     136         [ #  # ]:          0 :         if (len == 0)
     137                 :          0 :                 return true;
     138                 :            : 
     139                 :          0 : bad:
     140                 :            :         return false;
     141                 :            : }
     142                 :            : 
     143                 :            : #if IS_ENABLED(CONFIG_IPV6_MIP6)
     144                 :            : /**
     145                 :            :  *      ipv6_rearrange_destopt - rearrange IPv6 destination options header
     146                 :            :  *      @iph: IPv6 header
     147                 :            :  *      @destopt: destionation options header
     148                 :            :  */
     149                 :            : static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt)
     150                 :            : {
     151                 :            :         u8 *opt = (u8 *)destopt;
     152                 :            :         int len = ipv6_optlen(destopt);
     153                 :            :         int off = 0;
     154                 :            :         int optlen = 0;
     155                 :            : 
     156                 :            :         off += 2;
     157                 :            :         len -= 2;
     158                 :            : 
     159                 :            :         while (len > 0) {
     160                 :            : 
     161                 :            :                 switch (opt[off]) {
     162                 :            : 
     163                 :            :                 case IPV6_TLV_PAD1:
     164                 :            :                         optlen = 1;
     165                 :            :                         break;
     166                 :            :                 default:
     167                 :            :                         if (len < 2)
     168                 :            :                                 goto bad;
     169                 :            :                         optlen = opt[off+1]+2;
     170                 :            :                         if (len < optlen)
     171                 :            :                                 goto bad;
     172                 :            : 
     173                 :            :                         /* Rearrange the source address in @iph and the
     174                 :            :                          * addresses in home address option for final source.
     175                 :            :                          * See 11.3.2 of RFC 3775 for details.
     176                 :            :                          */
     177                 :            :                         if (opt[off] == IPV6_TLV_HAO) {
     178                 :            :                                 struct in6_addr final_addr;
     179                 :            :                                 struct ipv6_destopt_hao *hao;
     180                 :            : 
     181                 :            :                                 hao = (struct ipv6_destopt_hao *)&opt[off];
     182                 :            :                                 if (hao->length != sizeof(hao->addr)) {
     183                 :            :                                         net_warn_ratelimited("destopt hao: invalid header length: %u\n",
     184                 :            :                                                              hao->length);
     185                 :            :                                         goto bad;
     186                 :            :                                 }
     187                 :            :                                 final_addr = hao->addr;
     188                 :            :                                 hao->addr = iph->saddr;
     189                 :            :                                 iph->saddr = final_addr;
     190                 :            :                         }
     191                 :            :                         break;
     192                 :            :                 }
     193                 :            : 
     194                 :            :                 off += optlen;
     195                 :            :                 len -= optlen;
     196                 :            :         }
     197                 :            :         /* Note: ok if len == 0 */
     198                 :            : bad:
     199                 :            :         return;
     200                 :            : }
     201                 :            : #else
     202                 :            : static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt) {}
     203                 :            : #endif
     204                 :            : 
     205                 :            : /**
     206                 :            :  *      ipv6_rearrange_rthdr - rearrange IPv6 routing header
     207                 :            :  *      @iph: IPv6 header
     208                 :            :  *      @rthdr: routing header
     209                 :            :  *
     210                 :            :  *      Rearrange the destination address in @iph and the addresses in @rthdr
     211                 :            :  *      so that they appear in the order they will at the final destination.
     212                 :            :  *      See Appendix A2 of RFC 2402 for details.
     213                 :            :  */
     214                 :            : static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
     215                 :            : {
     216                 :            :         int segments, segments_left;
     217                 :            :         struct in6_addr *addrs;
     218                 :            :         struct in6_addr final_addr;
     219                 :            : 
     220                 :            :         segments_left = rthdr->segments_left;
     221                 :            :         if (segments_left == 0)
     222                 :            :                 return;
     223                 :            :         rthdr->segments_left = 0;
     224                 :            : 
     225                 :            :         /* The value of rthdr->hdrlen has been verified either by the system
     226                 :            :          * call if it is locally generated, or by ipv6_rthdr_rcv() for incoming
     227                 :            :          * packets.  So we can assume that it is even and that segments is
     228                 :            :          * greater than or equal to segments_left.
     229                 :            :          *
     230                 :            :          * For the same reason we can assume that this option is of type 0.
     231                 :            :          */
     232                 :            :         segments = rthdr->hdrlen >> 1;
     233                 :            : 
     234                 :            :         addrs = ((struct rt0_hdr *)rthdr)->addr;
     235                 :            :         final_addr = addrs[segments - 1];
     236                 :            : 
     237                 :            :         addrs += segments - segments_left;
     238                 :            :         memmove(addrs + 1, addrs, (segments_left - 1) * sizeof(*addrs));
     239                 :            : 
     240                 :            :         addrs[0] = iph->daddr;
     241                 :            :         iph->daddr = final_addr;
     242                 :            : }
     243                 :            : 
     244                 :            : static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
     245                 :            : {
     246                 :            :         union {
     247                 :            :                 struct ipv6hdr *iph;
     248                 :            :                 struct ipv6_opt_hdr *opth;
     249                 :            :                 struct ipv6_rt_hdr *rth;
     250                 :            :                 char *raw;
     251                 :            :         } exthdr = { .iph = iph };
     252                 :            :         char *end = exthdr.raw + len;
     253                 :            :         int nexthdr = iph->nexthdr;
     254                 :            : 
     255                 :            :         exthdr.iph++;
     256                 :            : 
     257                 :            :         while (exthdr.raw < end) {
     258                 :            :                 switch (nexthdr) {
     259                 :            :                 case NEXTHDR_DEST:
     260                 :            :                         if (dir == XFRM_POLICY_OUT)
     261                 :            :                                 ipv6_rearrange_destopt(iph, exthdr.opth);
     262                 :            :                         /* fall through */
     263                 :            :                 case NEXTHDR_HOP:
     264                 :            :                         if (!zero_out_mutable_opts(exthdr.opth)) {
     265                 :            :                                 net_dbg_ratelimited("overrun %sopts\n",
     266                 :            :                                                     nexthdr == NEXTHDR_HOP ?
     267                 :            :                                                     "hop" : "dest");
     268                 :            :                                 return -EINVAL;
     269                 :            :                         }
     270                 :            :                         break;
     271                 :            : 
     272                 :            :                 case NEXTHDR_ROUTING:
     273                 :            :                         ipv6_rearrange_rthdr(iph, exthdr.rth);
     274                 :            :                         break;
     275                 :            : 
     276                 :            :                 default:
     277                 :            :                         return 0;
     278                 :            :                 }
     279                 :            : 
     280                 :            :                 nexthdr = exthdr.opth->nexthdr;
     281                 :            :                 exthdr.raw += ipv6_optlen(exthdr.opth);
     282                 :            :         }
     283                 :            : 
     284                 :            :         return 0;
     285                 :            : }
     286                 :            : 
     287                 :          0 : static void ah6_output_done(struct crypto_async_request *base, int err)
     288                 :            : {
     289                 :          0 :         int extlen;
     290                 :          0 :         u8 *iph_base;
     291                 :          0 :         u8 *icv;
     292                 :          0 :         struct sk_buff *skb = base->data;
     293         [ #  # ]:          0 :         struct xfrm_state *x = skb_dst(skb)->xfrm;
     294                 :          0 :         struct ah_data *ahp = x->data;
     295         [ #  # ]:          0 :         struct ipv6hdr *top_iph = ipv6_hdr(skb);
     296         [ #  # ]:          0 :         struct ip_auth_hdr *ah = ip_auth_hdr(skb);
     297                 :          0 :         struct tmp_ext *iph_ext;
     298                 :            : 
     299         [ #  # ]:          0 :         extlen = skb_network_header_len(skb) - sizeof(struct ipv6hdr);
     300         [ #  # ]:          0 :         if (extlen)
     301                 :          0 :                 extlen += sizeof(*iph_ext);
     302                 :            : 
     303                 :          0 :         iph_base = AH_SKB_CB(skb)->tmp;
     304                 :          0 :         iph_ext = ah_tmp_ext(iph_base);
     305         [ #  # ]:          0 :         icv = ah_tmp_icv(ahp->ahash, iph_ext, extlen);
     306                 :            : 
     307                 :          0 :         memcpy(ah->auth_data, icv, ahp->icv_trunc_len);
     308                 :          0 :         memcpy(top_iph, iph_base, IPV6HDR_BASELEN);
     309                 :            : 
     310         [ #  # ]:          0 :         if (extlen) {
     311                 :            : #if IS_ENABLED(CONFIG_IPV6_MIP6)
     312                 :            :                 memcpy(&top_iph->saddr, iph_ext, extlen);
     313                 :            : #else
     314                 :          0 :                 memcpy(&top_iph->daddr, iph_ext, extlen);
     315                 :            : #endif
     316                 :            :         }
     317                 :            : 
     318                 :          0 :         kfree(AH_SKB_CB(skb)->tmp);
     319                 :          0 :         xfrm_output_resume(skb, err);
     320                 :          0 : }
     321                 :            : 
     322                 :          0 : static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
     323                 :            : {
     324                 :          0 :         int err;
     325                 :          0 :         int nfrags;
     326                 :          0 :         int extlen;
     327                 :          0 :         u8 *iph_base;
     328                 :          0 :         u8 *icv;
     329                 :          0 :         u8 nexthdr;
     330                 :          0 :         struct sk_buff *trailer;
     331                 :          0 :         struct crypto_ahash *ahash;
     332                 :          0 :         struct ahash_request *req;
     333                 :          0 :         struct scatterlist *sg;
     334                 :          0 :         struct ipv6hdr *top_iph;
     335                 :          0 :         struct ip_auth_hdr *ah;
     336                 :          0 :         struct ah_data *ahp;
     337                 :          0 :         struct tmp_ext *iph_ext;
     338                 :          0 :         int seqhi_len = 0;
     339                 :          0 :         __be32 *seqhi;
     340                 :          0 :         int sglists = 0;
     341                 :          0 :         struct scatterlist *seqhisg;
     342                 :            : 
     343                 :          0 :         ahp = x->data;
     344                 :          0 :         ahash = ahp->ahash;
     345                 :            : 
     346                 :          0 :         err = skb_cow_data(skb, 0, &trailer);
     347         [ #  # ]:          0 :         if (err < 0)
     348                 :          0 :                 goto out;
     349                 :          0 :         nfrags = err;
     350                 :            : 
     351                 :          0 :         skb_push(skb, -skb_network_offset(skb));
     352         [ #  # ]:          0 :         extlen = skb_network_header_len(skb) - sizeof(struct ipv6hdr);
     353         [ #  # ]:          0 :         if (extlen)
     354                 :          0 :                 extlen += sizeof(*iph_ext);
     355                 :            : 
     356         [ #  # ]:          0 :         if (x->props.flags & XFRM_STATE_ESN) {
     357                 :          0 :                 sglists = 1;
     358                 :          0 :                 seqhi_len = sizeof(*seqhi);
     359                 :            :         }
     360                 :          0 :         err = -ENOMEM;
     361                 :          0 :         iph_base = ah_alloc_tmp(ahash, nfrags + sglists, IPV6HDR_BASELEN +
     362                 :          0 :                                 extlen + seqhi_len);
     363         [ #  # ]:          0 :         if (!iph_base)
     364                 :          0 :                 goto out;
     365                 :            : 
     366                 :          0 :         iph_ext = ah_tmp_ext(iph_base);
     367                 :          0 :         seqhi = (__be32 *)((char *)iph_ext + extlen);
     368         [ #  # ]:          0 :         icv = ah_tmp_icv(ahash, seqhi, seqhi_len);
     369         [ #  # ]:          0 :         req = ah_tmp_req(ahash, icv);
     370         [ #  # ]:          0 :         sg = ah_req_sg(ahash, req);
     371                 :          0 :         seqhisg = sg + nfrags;
     372                 :            : 
     373         [ #  # ]:          0 :         ah = ip_auth_hdr(skb);
     374                 :          0 :         memset(ah->auth_data, 0, ahp->icv_trunc_len);
     375                 :            : 
     376         [ #  # ]:          0 :         top_iph = ipv6_hdr(skb);
     377                 :          0 :         top_iph->payload_len = htons(skb->len - sizeof(*top_iph));
     378                 :            : 
     379         [ #  # ]:          0 :         nexthdr = *skb_mac_header(skb);
     380                 :          0 :         *skb_mac_header(skb) = IPPROTO_AH;
     381                 :            : 
     382                 :            :         /* When there are no extension headers, we only need to save the first
     383                 :            :          * 8 bytes of the base IP header.
     384                 :            :          */
     385                 :          0 :         memcpy(iph_base, top_iph, IPV6HDR_BASELEN);
     386                 :            : 
     387         [ #  # ]:          0 :         if (extlen) {
     388                 :            : #if IS_ENABLED(CONFIG_IPV6_MIP6)
     389                 :            :                 memcpy(iph_ext, &top_iph->saddr, extlen);
     390                 :            : #else
     391                 :          0 :                 memcpy(iph_ext, &top_iph->daddr, extlen);
     392                 :            : #endif
     393                 :          0 :                 err = ipv6_clear_mutable_options(top_iph,
     394                 :          0 :                                                  extlen - sizeof(*iph_ext) +
     395                 :            :                                                  sizeof(*top_iph),
     396                 :            :                                                  XFRM_POLICY_OUT);
     397         [ #  # ]:          0 :                 if (err)
     398                 :          0 :                         goto out_free;
     399                 :            :         }
     400                 :            : 
     401                 :          0 :         ah->nexthdr = nexthdr;
     402                 :            : 
     403                 :          0 :         top_iph->priority    = 0;
     404                 :          0 :         top_iph->flow_lbl[0] = 0;
     405                 :          0 :         top_iph->flow_lbl[1] = 0;
     406                 :          0 :         top_iph->flow_lbl[2] = 0;
     407                 :          0 :         top_iph->hop_limit   = 0;
     408                 :            : 
     409                 :          0 :         ah->hdrlen  = (XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len) >> 2) - 2;
     410                 :            : 
     411                 :          0 :         ah->reserved = 0;
     412                 :          0 :         ah->spi = x->id.spi;
     413                 :          0 :         ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
     414                 :            : 
     415                 :          0 :         sg_init_table(sg, nfrags + sglists);
     416                 :          0 :         err = skb_to_sgvec_nomark(skb, sg, 0, skb->len);
     417         [ #  # ]:          0 :         if (unlikely(err < 0))
     418                 :          0 :                 goto out_free;
     419                 :            : 
     420         [ #  # ]:          0 :         if (x->props.flags & XFRM_STATE_ESN) {
     421                 :            :                 /* Attach seqhi sg right after packet payload */
     422                 :          0 :                 *seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
     423                 :          0 :                 sg_set_buf(seqhisg, seqhi, seqhi_len);
     424                 :            :         }
     425                 :          0 :         ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len);
     426                 :          0 :         ahash_request_set_callback(req, 0, ah6_output_done, skb);
     427                 :            : 
     428                 :          0 :         AH_SKB_CB(skb)->tmp = iph_base;
     429                 :            : 
     430                 :          0 :         err = crypto_ahash_digest(req);
     431         [ #  # ]:          0 :         if (err) {
     432         [ #  # ]:          0 :                 if (err == -EINPROGRESS)
     433                 :          0 :                         goto out;
     434                 :            : 
     435         [ #  # ]:          0 :                 if (err == -ENOSPC)
     436                 :          0 :                         err = NET_XMIT_DROP;
     437                 :          0 :                 goto out_free;
     438                 :            :         }
     439                 :            : 
     440                 :          0 :         memcpy(ah->auth_data, icv, ahp->icv_trunc_len);
     441                 :          0 :         memcpy(top_iph, iph_base, IPV6HDR_BASELEN);
     442                 :            : 
     443         [ #  # ]:          0 :         if (extlen) {
     444                 :            : #if IS_ENABLED(CONFIG_IPV6_MIP6)
     445                 :            :                 memcpy(&top_iph->saddr, iph_ext, extlen);
     446                 :            : #else
     447                 :          0 :                 memcpy(&top_iph->daddr, iph_ext, extlen);
     448                 :            : #endif
     449                 :            :         }
     450                 :            : 
     451                 :          0 : out_free:
     452                 :          0 :         kfree(iph_base);
     453                 :          0 : out:
     454                 :          0 :         return err;
     455                 :            : }
     456                 :            : 
     457                 :          0 : static void ah6_input_done(struct crypto_async_request *base, int err)
     458                 :            : {
     459                 :          0 :         u8 *auth_data;
     460                 :          0 :         u8 *icv;
     461                 :          0 :         u8 *work_iph;
     462                 :          0 :         struct sk_buff *skb = base->data;
     463         [ #  # ]:          0 :         struct xfrm_state *x = xfrm_input_state(skb);
     464                 :          0 :         struct ah_data *ahp = x->data;
     465         [ #  # ]:          0 :         struct ip_auth_hdr *ah = ip_auth_hdr(skb);
     466         [ #  # ]:          0 :         int hdr_len = skb_network_header_len(skb);
     467                 :          0 :         int ah_hlen = ipv6_authlen(ah);
     468                 :            : 
     469         [ #  # ]:          0 :         if (err)
     470                 :          0 :                 goto out;
     471                 :            : 
     472                 :          0 :         work_iph = AH_SKB_CB(skb)->tmp;
     473                 :          0 :         auth_data = ah_tmp_auth(work_iph, hdr_len);
     474                 :          0 :         icv = ah_tmp_icv(ahp->ahash, auth_data, ahp->icv_trunc_len);
     475                 :            : 
     476         [ #  # ]:          0 :         err = crypto_memneq(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG : 0;
     477                 :          0 :         if (err)
     478                 :          0 :                 goto out;
     479                 :            : 
     480                 :          0 :         err = ah->nexthdr;
     481                 :            : 
     482                 :          0 :         skb->network_header += ah_hlen;
     483         [ #  # ]:          0 :         memcpy(skb_network_header(skb), work_iph, hdr_len);
     484         [ #  # ]:          0 :         __skb_pull(skb, ah_hlen + hdr_len);
     485         [ #  # ]:          0 :         if (x->props.mode == XFRM_MODE_TUNNEL)
     486                 :          0 :                 skb_reset_transport_header(skb);
     487                 :            :         else
     488                 :          0 :                 skb_set_transport_header(skb, -hdr_len);
     489                 :          0 : out:
     490                 :          0 :         kfree(AH_SKB_CB(skb)->tmp);
     491                 :          0 :         xfrm_input_resume(skb, err);
     492                 :          0 : }
     493                 :            : 
     494                 :            : 
     495                 :            : 
     496                 :          0 : static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
     497                 :            : {
     498                 :            :         /*
     499                 :            :          * Before process AH
     500                 :            :          * [IPv6][Ext1][Ext2][AH][Dest][Payload]
     501                 :            :          * |<-------------->| hdr_len
     502                 :            :          *
     503                 :            :          * To erase AH:
     504                 :            :          * Keeping copy of cleared headers. After AH processing,
     505                 :            :          * Moving the pointer of skb->network_header by using skb_pull as long
     506                 :            :          * as AH header length. Then copy back the copy as long as hdr_len
     507                 :            :          * If destination header following AH exists, copy it into after [Ext2].
     508                 :            :          *
     509                 :            :          * |<>|[IPv6][Ext1][Ext2][Dest][Payload]
     510                 :            :          * There is offset of AH before IPv6 header after the process.
     511                 :            :          */
     512                 :            : 
     513                 :          0 :         u8 *auth_data;
     514                 :          0 :         u8 *icv;
     515                 :          0 :         u8 *work_iph;
     516                 :          0 :         struct sk_buff *trailer;
     517                 :          0 :         struct crypto_ahash *ahash;
     518                 :          0 :         struct ahash_request *req;
     519                 :          0 :         struct scatterlist *sg;
     520                 :          0 :         struct ip_auth_hdr *ah;
     521                 :          0 :         struct ipv6hdr *ip6h;
     522                 :          0 :         struct ah_data *ahp;
     523                 :          0 :         u16 hdr_len;
     524                 :          0 :         u16 ah_hlen;
     525                 :          0 :         int nexthdr;
     526                 :          0 :         int nfrags;
     527                 :          0 :         int err = -ENOMEM;
     528                 :          0 :         int seqhi_len = 0;
     529                 :          0 :         __be32 *seqhi;
     530                 :          0 :         int sglists = 0;
     531                 :          0 :         struct scatterlist *seqhisg;
     532                 :            : 
     533         [ #  # ]:          0 :         if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr)))
     534                 :          0 :                 goto out;
     535                 :            : 
     536                 :            :         /* We are going to _remove_ AH header to keep sockets happy,
     537                 :            :          * so... Later this can change. */
     538         [ #  # ]:          0 :         if (skb_unclone(skb, GFP_ATOMIC))
     539                 :          0 :                 goto out;
     540                 :            : 
     541                 :          0 :         skb->ip_summed = CHECKSUM_NONE;
     542                 :            : 
     543         [ #  # ]:          0 :         hdr_len = skb_network_header_len(skb);
     544                 :          0 :         ah = (struct ip_auth_hdr *)skb->data;
     545                 :          0 :         ahp = x->data;
     546                 :          0 :         ahash = ahp->ahash;
     547                 :            : 
     548                 :          0 :         nexthdr = ah->nexthdr;
     549                 :          0 :         ah_hlen = ipv6_authlen(ah);
     550                 :            : 
     551         [ #  # ]:          0 :         if (ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_full_len) &&
     552         [ #  # ]:          0 :             ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len))
     553                 :          0 :                 goto out;
     554                 :            : 
     555         [ #  # ]:          0 :         if (!pskb_may_pull(skb, ah_hlen))
     556                 :          0 :                 goto out;
     557                 :            : 
     558                 :          0 :         err = skb_cow_data(skb, 0, &trailer);
     559         [ #  # ]:          0 :         if (err < 0)
     560                 :          0 :                 goto out;
     561                 :          0 :         nfrags = err;
     562                 :            : 
     563                 :          0 :         ah = (struct ip_auth_hdr *)skb->data;
     564                 :          0 :         ip6h = ipv6_hdr(skb);
     565                 :            : 
     566                 :          0 :         skb_push(skb, hdr_len);
     567                 :            : 
     568         [ #  # ]:          0 :         if (x->props.flags & XFRM_STATE_ESN) {
     569                 :          0 :                 sglists = 1;
     570                 :          0 :                 seqhi_len = sizeof(*seqhi);
     571                 :            :         }
     572                 :            : 
     573                 :          0 :         work_iph = ah_alloc_tmp(ahash, nfrags + sglists, hdr_len +
     574                 :          0 :                                 ahp->icv_trunc_len + seqhi_len);
     575         [ #  # ]:          0 :         if (!work_iph) {
     576                 :          0 :                 err = -ENOMEM;
     577                 :          0 :                 goto out;
     578                 :            :         }
     579                 :            : 
     580                 :          0 :         auth_data = ah_tmp_auth((u8 *)work_iph, hdr_len);
     581                 :          0 :         seqhi = (__be32 *)(auth_data + ahp->icv_trunc_len);
     582                 :          0 :         icv = ah_tmp_icv(ahash, seqhi, seqhi_len);
     583                 :          0 :         req = ah_tmp_req(ahash, icv);
     584                 :          0 :         sg = ah_req_sg(ahash, req);
     585                 :          0 :         seqhisg = sg + nfrags;
     586                 :            : 
     587                 :          0 :         memcpy(work_iph, ip6h, hdr_len);
     588                 :          0 :         memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
     589                 :          0 :         memset(ah->auth_data, 0, ahp->icv_trunc_len);
     590                 :            : 
     591         [ #  # ]:          0 :         if (ipv6_clear_mutable_options(ip6h, hdr_len, XFRM_POLICY_IN))
     592                 :          0 :                 goto out_free;
     593                 :            : 
     594                 :          0 :         ip6h->priority    = 0;
     595                 :          0 :         ip6h->flow_lbl[0] = 0;
     596                 :          0 :         ip6h->flow_lbl[1] = 0;
     597                 :          0 :         ip6h->flow_lbl[2] = 0;
     598                 :          0 :         ip6h->hop_limit   = 0;
     599                 :            : 
     600                 :          0 :         sg_init_table(sg, nfrags + sglists);
     601                 :          0 :         err = skb_to_sgvec_nomark(skb, sg, 0, skb->len);
     602         [ #  # ]:          0 :         if (unlikely(err < 0))
     603                 :          0 :                 goto out_free;
     604                 :            : 
     605         [ #  # ]:          0 :         if (x->props.flags & XFRM_STATE_ESN) {
     606                 :            :                 /* Attach seqhi sg right after packet payload */
     607                 :          0 :                 *seqhi = XFRM_SKB_CB(skb)->seq.input.hi;
     608                 :          0 :                 sg_set_buf(seqhisg, seqhi, seqhi_len);
     609                 :            :         }
     610                 :            : 
     611                 :          0 :         ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len);
     612                 :          0 :         ahash_request_set_callback(req, 0, ah6_input_done, skb);
     613                 :            : 
     614                 :          0 :         AH_SKB_CB(skb)->tmp = work_iph;
     615                 :            : 
     616                 :          0 :         err = crypto_ahash_digest(req);
     617         [ #  # ]:          0 :         if (err) {
     618         [ #  # ]:          0 :                 if (err == -EINPROGRESS)
     619                 :          0 :                         goto out;
     620                 :            : 
     621                 :          0 :                 goto out_free;
     622                 :            :         }
     623                 :            : 
     624         [ #  # ]:          0 :         err = crypto_memneq(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG : 0;
     625                 :          0 :         if (err)
     626                 :          0 :                 goto out_free;
     627                 :            : 
     628                 :          0 :         skb->network_header += ah_hlen;
     629         [ #  # ]:          0 :         memcpy(skb_network_header(skb), work_iph, hdr_len);
     630         [ #  # ]:          0 :         __skb_pull(skb, ah_hlen + hdr_len);
     631                 :            : 
     632         [ #  # ]:          0 :         if (x->props.mode == XFRM_MODE_TUNNEL)
     633                 :          0 :                 skb_reset_transport_header(skb);
     634                 :            :         else
     635                 :          0 :                 skb_set_transport_header(skb, -hdr_len);
     636                 :            : 
     637                 :            :         err = nexthdr;
     638                 :            : 
     639                 :          0 : out_free:
     640                 :          0 :         kfree(work_iph);
     641                 :          0 : out:
     642                 :          0 :         return err;
     643                 :            : }
     644                 :            : 
     645                 :          0 : static int ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
     646                 :            :                    u8 type, u8 code, int offset, __be32 info)
     647                 :            : {
     648         [ #  # ]:          0 :         struct net *net = dev_net(skb->dev);
     649                 :          0 :         struct ipv6hdr *iph = (struct ipv6hdr *)skb->data;
     650                 :          0 :         struct ip_auth_hdr *ah = (struct ip_auth_hdr *)(skb->data+offset);
     651                 :          0 :         struct xfrm_state *x;
     652                 :            : 
     653                 :          0 :         if (type != ICMPV6_PKT_TOOBIG &&
     654         [ #  # ]:          0 :             type != NDISC_REDIRECT)
     655                 :            :                 return 0;
     656                 :            : 
     657                 :          0 :         x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6);
     658         [ #  # ]:          0 :         if (!x)
     659                 :            :                 return 0;
     660                 :            : 
     661         [ #  # ]:          0 :         if (type == NDISC_REDIRECT)
     662                 :          0 :                 ip6_redirect(skb, net, skb->dev->ifindex, 0,
     663                 :            :                              sock_net_uid(net, NULL));
     664                 :            :         else
     665                 :          0 :                 ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
     666                 :          0 :         xfrm_state_put(x);
     667                 :            : 
     668                 :          0 :         return 0;
     669                 :            : }
     670                 :            : 
     671                 :          0 : static int ah6_init_state(struct xfrm_state *x)
     672                 :            : {
     673                 :          0 :         struct ah_data *ahp = NULL;
     674                 :          0 :         struct xfrm_algo_desc *aalg_desc;
     675                 :          0 :         struct crypto_ahash *ahash;
     676                 :            : 
     677         [ #  # ]:          0 :         if (!x->aalg)
     678                 :          0 :                 goto error;
     679                 :            : 
     680         [ #  # ]:          0 :         if (x->encap)
     681                 :          0 :                 goto error;
     682                 :            : 
     683                 :          0 :         ahp = kzalloc(sizeof(*ahp), GFP_KERNEL);
     684         [ #  # ]:          0 :         if (!ahp)
     685                 :            :                 return -ENOMEM;
     686                 :            : 
     687                 :          0 :         ahash = crypto_alloc_ahash(x->aalg->alg_name, 0, 0);
     688         [ #  # ]:          0 :         if (IS_ERR(ahash))
     689                 :          0 :                 goto error;
     690                 :            : 
     691                 :          0 :         ahp->ahash = ahash;
     692         [ #  # ]:          0 :         if (crypto_ahash_setkey(ahash, x->aalg->alg_key,
     693                 :          0 :                                (x->aalg->alg_key_len + 7) / 8))
     694                 :          0 :                 goto error;
     695                 :            : 
     696                 :            :         /*
     697                 :            :          * Lookup the algorithm description maintained by xfrm_algo,
     698                 :            :          * verify crypto transform properties, and store information
     699                 :            :          * we need for AH processing.  This lookup cannot fail here
     700                 :            :          * after a successful crypto_alloc_hash().
     701                 :            :          */
     702                 :          0 :         aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
     703         [ #  # ]:          0 :         BUG_ON(!aalg_desc);
     704                 :            : 
     705         [ #  # ]:          0 :         if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
     706                 :            :             crypto_ahash_digestsize(ahash)) {
     707                 :          0 :                 pr_info("AH: %s digestsize %u != %hu\n",
     708                 :            :                         x->aalg->alg_name, crypto_ahash_digestsize(ahash),
     709                 :            :                         aalg_desc->uinfo.auth.icv_fullbits/8);
     710                 :          0 :                 goto error;
     711                 :            :         }
     712                 :            : 
     713                 :          0 :         ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
     714                 :          0 :         ahp->icv_trunc_len = x->aalg->alg_trunc_len/8;
     715                 :            : 
     716                 :          0 :         x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) +
     717                 :            :                                           ahp->icv_trunc_len);
     718      [ #  #  # ]:          0 :         switch (x->props.mode) {
     719                 :            :         case XFRM_MODE_BEET:
     720                 :            :         case XFRM_MODE_TRANSPORT:
     721                 :            :                 break;
     722                 :          0 :         case XFRM_MODE_TUNNEL:
     723                 :          0 :                 x->props.header_len += sizeof(struct ipv6hdr);
     724                 :          0 :                 break;
     725                 :          0 :         default:
     726                 :          0 :                 goto error;
     727                 :            :         }
     728                 :          0 :         x->data = ahp;
     729                 :            : 
     730                 :          0 :         return 0;
     731                 :            : 
     732                 :          0 : error:
     733                 :          0 :         if (ahp) {
     734                 :          0 :                 crypto_free_ahash(ahp->ahash);
     735                 :          0 :                 kfree(ahp);
     736                 :            :         }
     737                 :            :         return -EINVAL;
     738                 :            : }
     739                 :            : 
     740                 :          0 : static void ah6_destroy(struct xfrm_state *x)
     741                 :            : {
     742                 :          0 :         struct ah_data *ahp = x->data;
     743                 :            : 
     744         [ #  # ]:          0 :         if (!ahp)
     745                 :            :                 return;
     746                 :            : 
     747                 :          0 :         crypto_free_ahash(ahp->ahash);
     748                 :          0 :         kfree(ahp);
     749                 :            : }
     750                 :            : 
     751                 :          0 : static int ah6_rcv_cb(struct sk_buff *skb, int err)
     752                 :            : {
     753                 :          0 :         return 0;
     754                 :            : }
     755                 :            : 
     756                 :            : static const struct xfrm_type ah6_type = {
     757                 :            :         .description    = "AH6",
     758                 :            :         .owner          = THIS_MODULE,
     759                 :            :         .proto          = IPPROTO_AH,
     760                 :            :         .flags          = XFRM_TYPE_REPLAY_PROT,
     761                 :            :         .init_state     = ah6_init_state,
     762                 :            :         .destructor     = ah6_destroy,
     763                 :            :         .input          = ah6_input,
     764                 :            :         .output         = ah6_output,
     765                 :            :         .hdr_offset     = xfrm6_find_1stfragopt,
     766                 :            : };
     767                 :            : 
     768                 :            : static struct xfrm6_protocol ah6_protocol = {
     769                 :            :         .handler        =       xfrm6_rcv,
     770                 :            :         .cb_handler     =       ah6_rcv_cb,
     771                 :            :         .err_handler    =       ah6_err,
     772                 :            :         .priority       =       0,
     773                 :            : };
     774                 :            : 
     775                 :         13 : static int __init ah6_init(void)
     776                 :            : {
     777         [ -  + ]:         13 :         if (xfrm_register_type(&ah6_type, AF_INET6) < 0) {
     778                 :          0 :                 pr_info("%s: can't add xfrm type\n", __func__);
     779                 :          0 :                 return -EAGAIN;
     780                 :            :         }
     781                 :            : 
     782         [ -  + ]:         13 :         if (xfrm6_protocol_register(&ah6_protocol, IPPROTO_AH) < 0) {
     783                 :          0 :                 pr_info("%s: can't add protocol\n", __func__);
     784                 :          0 :                 xfrm_unregister_type(&ah6_type, AF_INET6);
     785                 :          0 :                 return -EAGAIN;
     786                 :            :         }
     787                 :            : 
     788                 :            :         return 0;
     789                 :            : }
     790                 :            : 
     791                 :          0 : static void __exit ah6_fini(void)
     792                 :            : {
     793         [ #  # ]:          0 :         if (xfrm6_protocol_deregister(&ah6_protocol, IPPROTO_AH) < 0)
     794                 :          0 :                 pr_info("%s: can't remove protocol\n", __func__);
     795                 :            : 
     796                 :          0 :         xfrm_unregister_type(&ah6_type, AF_INET6);
     797                 :          0 : }
     798                 :            : 
     799                 :            : module_init(ah6_init);
     800                 :            : module_exit(ah6_fini);
     801                 :            : 
     802                 :            : MODULE_LICENSE("GPL");
     803                 :            : MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_AH);

Generated by: LCOV version 1.14