diff options
Diffstat (limited to 'net/ipv6/ah6.c')
| -rw-r--r-- | net/ipv6/ah6.c | 125 |
1 files changed, 106 insertions, 19 deletions
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index d31c0d6c0448..b0d83e8e4252 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c | |||
| @@ -24,7 +24,6 @@ | |||
| 24 | * This file is derived from net/ipv4/ah.c. | 24 | * This file is derived from net/ipv4/ah.c. |
| 25 | */ | 25 | */ |
| 26 | 26 | ||
| 27 | #include <linux/config.h> | ||
| 28 | #include <linux/module.h> | 27 | #include <linux/module.h> |
| 29 | #include <net/ip.h> | 28 | #include <net/ip.h> |
| 30 | #include <net/ah.h> | 29 | #include <net/ah.h> |
| @@ -75,6 +74,66 @@ bad: | |||
| 75 | return 0; | 74 | return 0; |
| 76 | } | 75 | } |
| 77 | 76 | ||
| 77 | #ifdef CONFIG_IPV6_MIP6 | ||
| 78 | /** | ||
| 79 | * ipv6_rearrange_destopt - rearrange IPv6 destination options header | ||
| 80 | * @iph: IPv6 header | ||
| 81 | * @destopt: destionation options header | ||
| 82 | */ | ||
| 83 | static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt) | ||
| 84 | { | ||
| 85 | u8 *opt = (u8 *)destopt; | ||
| 86 | int len = ipv6_optlen(destopt); | ||
| 87 | int off = 0; | ||
| 88 | int optlen = 0; | ||
| 89 | |||
| 90 | off += 2; | ||
| 91 | len -= 2; | ||
| 92 | |||
| 93 | while (len > 0) { | ||
| 94 | |||
| 95 | switch (opt[off]) { | ||
| 96 | |||
| 97 | case IPV6_TLV_PAD0: | ||
| 98 | optlen = 1; | ||
| 99 | break; | ||
| 100 | default: | ||
| 101 | if (len < 2) | ||
| 102 | goto bad; | ||
| 103 | optlen = opt[off+1]+2; | ||
| 104 | if (len < optlen) | ||
| 105 | goto bad; | ||
| 106 | |||
| 107 | /* Rearrange the source address in @iph and the | ||
| 108 | * addresses in home address option for final source. | ||
| 109 | * See 11.3.2 of RFC 3775 for details. | ||
| 110 | */ | ||
| 111 | if (opt[off] == IPV6_TLV_HAO) { | ||
| 112 | struct in6_addr final_addr; | ||
| 113 | struct ipv6_destopt_hao *hao; | ||
| 114 | |||
| 115 | hao = (struct ipv6_destopt_hao *)&opt[off]; | ||
| 116 | if (hao->length != sizeof(hao->addr)) { | ||
| 117 | if (net_ratelimit()) | ||
| 118 | printk(KERN_WARNING "destopt hao: invalid header length: %u\n", hao->length); | ||
| 119 | goto bad; | ||
| 120 | } | ||
| 121 | ipv6_addr_copy(&final_addr, &hao->addr); | ||
| 122 | ipv6_addr_copy(&hao->addr, &iph->saddr); | ||
| 123 | ipv6_addr_copy(&iph->saddr, &final_addr); | ||
| 124 | } | ||
| 125 | break; | ||
| 126 | } | ||
| 127 | |||
| 128 | off += optlen; | ||
| 129 | len -= optlen; | ||
| 130 | } | ||
| 131 | /* Note: ok if len == 0 */ | ||
| 132 | bad: | ||
| 133 | return; | ||
| 134 | } | ||
| 135 | #endif | ||
| 136 | |||
| 78 | /** | 137 | /** |
| 79 | * ipv6_rearrange_rthdr - rearrange IPv6 routing header | 138 | * ipv6_rearrange_rthdr - rearrange IPv6 routing header |
| 80 | * @iph: IPv6 header | 139 | * @iph: IPv6 header |
| @@ -114,7 +173,7 @@ static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) | |||
| 114 | ipv6_addr_copy(&iph->daddr, &final_addr); | 173 | ipv6_addr_copy(&iph->daddr, &final_addr); |
| 115 | } | 174 | } |
| 116 | 175 | ||
| 117 | static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) | 176 | static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) |
| 118 | { | 177 | { |
| 119 | union { | 178 | union { |
| 120 | struct ipv6hdr *iph; | 179 | struct ipv6hdr *iph; |
| @@ -129,8 +188,12 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) | |||
| 129 | 188 | ||
| 130 | while (exthdr.raw < end) { | 189 | while (exthdr.raw < end) { |
| 131 | switch (nexthdr) { | 190 | switch (nexthdr) { |
| 132 | case NEXTHDR_HOP: | ||
| 133 | case NEXTHDR_DEST: | 191 | case NEXTHDR_DEST: |
| 192 | #ifdef CONFIG_IPV6_MIP6 | ||
| 193 | if (dir == XFRM_POLICY_OUT) | ||
| 194 | ipv6_rearrange_destopt(iph, exthdr.opth); | ||
| 195 | #endif | ||
| 196 | case NEXTHDR_HOP: | ||
| 134 | if (!zero_out_mutable_opts(exthdr.opth)) { | 197 | if (!zero_out_mutable_opts(exthdr.opth)) { |
| 135 | LIMIT_NETDEBUG( | 198 | LIMIT_NETDEBUG( |
| 136 | KERN_WARNING "overrun %sopts\n", | 199 | KERN_WARNING "overrun %sopts\n", |
| @@ -165,6 +228,9 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
| 165 | u8 nexthdr; | 228 | u8 nexthdr; |
| 166 | char tmp_base[8]; | 229 | char tmp_base[8]; |
| 167 | struct { | 230 | struct { |
| 231 | #ifdef CONFIG_IPV6_MIP6 | ||
| 232 | struct in6_addr saddr; | ||
| 233 | #endif | ||
| 168 | struct in6_addr daddr; | 234 | struct in6_addr daddr; |
| 169 | char hdrs[0]; | 235 | char hdrs[0]; |
| 170 | } *tmp_ext; | 236 | } *tmp_ext; |
| @@ -189,10 +255,15 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
| 189 | err = -ENOMEM; | 255 | err = -ENOMEM; |
| 190 | goto error; | 256 | goto error; |
| 191 | } | 257 | } |
| 258 | #ifdef CONFIG_IPV6_MIP6 | ||
| 259 | memcpy(tmp_ext, &top_iph->saddr, extlen); | ||
| 260 | #else | ||
| 192 | memcpy(tmp_ext, &top_iph->daddr, extlen); | 261 | memcpy(tmp_ext, &top_iph->daddr, extlen); |
| 262 | #endif | ||
| 193 | err = ipv6_clear_mutable_options(top_iph, | 263 | err = ipv6_clear_mutable_options(top_iph, |
| 194 | extlen - sizeof(*tmp_ext) + | 264 | extlen - sizeof(*tmp_ext) + |
| 195 | sizeof(*top_iph)); | 265 | sizeof(*top_iph), |
| 266 | XFRM_POLICY_OUT); | ||
| 196 | if (err) | 267 | if (err) |
| 197 | goto error_free_iph; | 268 | goto error_free_iph; |
| 198 | } | 269 | } |
| @@ -214,13 +285,20 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
| 214 | ah->spi = x->id.spi; | 285 | ah->spi = x->id.spi; |
| 215 | ah->seq_no = htonl(++x->replay.oseq); | 286 | ah->seq_no = htonl(++x->replay.oseq); |
| 216 | xfrm_aevent_doreplay(x); | 287 | xfrm_aevent_doreplay(x); |
| 217 | ahp->icv(ahp, skb, ah->auth_data); | 288 | err = ah_mac_digest(ahp, skb, ah->auth_data); |
| 289 | if (err) | ||
| 290 | goto error_free_iph; | ||
| 291 | memcpy(ah->auth_data, ahp->work_icv, ahp->icv_trunc_len); | ||
| 218 | 292 | ||
| 219 | err = 0; | 293 | err = 0; |
| 220 | 294 | ||
| 221 | memcpy(top_iph, tmp_base, sizeof(tmp_base)); | 295 | memcpy(top_iph, tmp_base, sizeof(tmp_base)); |
| 222 | if (tmp_ext) { | 296 | if (tmp_ext) { |
| 297 | #ifdef CONFIG_IPV6_MIP6 | ||
| 298 | memcpy(&top_iph->saddr, tmp_ext, extlen); | ||
| 299 | #else | ||
| 223 | memcpy(&top_iph->daddr, tmp_ext, extlen); | 300 | memcpy(&top_iph->daddr, tmp_ext, extlen); |
| 301 | #endif | ||
| 224 | error_free_iph: | 302 | error_free_iph: |
| 225 | kfree(tmp_ext); | 303 | kfree(tmp_ext); |
| 226 | } | 304 | } |
| @@ -252,6 +330,7 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
| 252 | u16 hdr_len; | 330 | u16 hdr_len; |
| 253 | u16 ah_hlen; | 331 | u16 ah_hlen; |
| 254 | int nexthdr; | 332 | int nexthdr; |
| 333 | int err = -EINVAL; | ||
| 255 | 334 | ||
| 256 | if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr))) | 335 | if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr))) |
| 257 | goto out; | 336 | goto out; |
| @@ -279,7 +358,7 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
| 279 | if (!tmp_hdr) | 358 | if (!tmp_hdr) |
| 280 | goto out; | 359 | goto out; |
| 281 | memcpy(tmp_hdr, skb->nh.raw, hdr_len); | 360 | memcpy(tmp_hdr, skb->nh.raw, hdr_len); |
| 282 | if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len)) | 361 | if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len, XFRM_POLICY_IN)) |
| 283 | goto free_out; | 362 | goto free_out; |
| 284 | skb->nh.ipv6h->priority = 0; | 363 | skb->nh.ipv6h->priority = 0; |
| 285 | skb->nh.ipv6h->flow_lbl[0] = 0; | 364 | skb->nh.ipv6h->flow_lbl[0] = 0; |
| @@ -293,8 +372,11 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
| 293 | memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); | 372 | memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); |
| 294 | memset(ah->auth_data, 0, ahp->icv_trunc_len); | 373 | memset(ah->auth_data, 0, ahp->icv_trunc_len); |
| 295 | skb_push(skb, hdr_len); | 374 | skb_push(skb, hdr_len); |
| 296 | ahp->icv(ahp, skb, ah->auth_data); | 375 | err = ah_mac_digest(ahp, skb, ah->auth_data); |
| 297 | if (memcmp(ah->auth_data, auth_data, ahp->icv_trunc_len)) { | 376 | if (err) |
| 377 | goto free_out; | ||
| 378 | err = -EINVAL; | ||
| 379 | if (memcmp(ahp->work_icv, auth_data, ahp->icv_trunc_len)) { | ||
| 298 | LIMIT_NETDEBUG(KERN_WARNING "ipsec ah authentication error\n"); | 380 | LIMIT_NETDEBUG(KERN_WARNING "ipsec ah authentication error\n"); |
| 299 | x->stats.integrity_failed++; | 381 | x->stats.integrity_failed++; |
| 300 | goto free_out; | 382 | goto free_out; |
| @@ -311,7 +393,7 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
| 311 | free_out: | 393 | free_out: |
| 312 | kfree(tmp_hdr); | 394 | kfree(tmp_hdr); |
| 313 | out: | 395 | out: |
| 314 | return -EINVAL; | 396 | return err; |
| 315 | } | 397 | } |
| 316 | 398 | ||
| 317 | static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | 399 | static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
| @@ -339,6 +421,7 @@ static int ah6_init_state(struct xfrm_state *x) | |||
| 339 | { | 421 | { |
| 340 | struct ah_data *ahp = NULL; | 422 | struct ah_data *ahp = NULL; |
| 341 | struct xfrm_algo_desc *aalg_desc; | 423 | struct xfrm_algo_desc *aalg_desc; |
| 424 | struct crypto_hash *tfm; | ||
| 342 | 425 | ||
| 343 | if (!x->aalg) | 426 | if (!x->aalg) |
| 344 | goto error; | 427 | goto error; |
| @@ -356,24 +439,27 @@ static int ah6_init_state(struct xfrm_state *x) | |||
| 356 | 439 | ||
| 357 | ahp->key = x->aalg->alg_key; | 440 | ahp->key = x->aalg->alg_key; |
| 358 | ahp->key_len = (x->aalg->alg_key_len+7)/8; | 441 | ahp->key_len = (x->aalg->alg_key_len+7)/8; |
| 359 | ahp->tfm = crypto_alloc_tfm(x->aalg->alg_name, 0); | 442 | tfm = crypto_alloc_hash(x->aalg->alg_name, 0, CRYPTO_ALG_ASYNC); |
| 360 | if (!ahp->tfm) | 443 | if (IS_ERR(tfm)) |
| 444 | goto error; | ||
| 445 | |||
| 446 | ahp->tfm = tfm; | ||
| 447 | if (crypto_hash_setkey(tfm, ahp->key, ahp->key_len)) | ||
| 361 | goto error; | 448 | goto error; |
| 362 | ahp->icv = ah_hmac_digest; | ||
| 363 | 449 | ||
| 364 | /* | 450 | /* |
| 365 | * Lookup the algorithm description maintained by xfrm_algo, | 451 | * Lookup the algorithm description maintained by xfrm_algo, |
| 366 | * verify crypto transform properties, and store information | 452 | * verify crypto transform properties, and store information |
| 367 | * we need for AH processing. This lookup cannot fail here | 453 | * we need for AH processing. This lookup cannot fail here |
| 368 | * after a successful crypto_alloc_tfm(). | 454 | * after a successful crypto_alloc_hash(). |
| 369 | */ | 455 | */ |
| 370 | aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0); | 456 | aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0); |
| 371 | BUG_ON(!aalg_desc); | 457 | BUG_ON(!aalg_desc); |
| 372 | 458 | ||
| 373 | if (aalg_desc->uinfo.auth.icv_fullbits/8 != | 459 | if (aalg_desc->uinfo.auth.icv_fullbits/8 != |
| 374 | crypto_tfm_alg_digestsize(ahp->tfm)) { | 460 | crypto_hash_digestsize(tfm)) { |
| 375 | printk(KERN_INFO "AH: %s digestsize %u != %hu\n", | 461 | printk(KERN_INFO "AH: %s digestsize %u != %hu\n", |
| 376 | x->aalg->alg_name, crypto_tfm_alg_digestsize(ahp->tfm), | 462 | x->aalg->alg_name, crypto_hash_digestsize(tfm), |
| 377 | aalg_desc->uinfo.auth.icv_fullbits/8); | 463 | aalg_desc->uinfo.auth.icv_fullbits/8); |
| 378 | goto error; | 464 | goto error; |
| 379 | } | 465 | } |
| @@ -388,7 +474,7 @@ static int ah6_init_state(struct xfrm_state *x) | |||
| 388 | goto error; | 474 | goto error; |
| 389 | 475 | ||
| 390 | x->props.header_len = XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + ahp->icv_trunc_len); | 476 | x->props.header_len = XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + ahp->icv_trunc_len); |
| 391 | if (x->props.mode) | 477 | if (x->props.mode == XFRM_MODE_TUNNEL) |
| 392 | x->props.header_len += sizeof(struct ipv6hdr); | 478 | x->props.header_len += sizeof(struct ipv6hdr); |
| 393 | x->data = ahp; | 479 | x->data = ahp; |
| 394 | 480 | ||
| @@ -397,7 +483,7 @@ static int ah6_init_state(struct xfrm_state *x) | |||
| 397 | error: | 483 | error: |
| 398 | if (ahp) { | 484 | if (ahp) { |
| 399 | kfree(ahp->work_icv); | 485 | kfree(ahp->work_icv); |
| 400 | crypto_free_tfm(ahp->tfm); | 486 | crypto_free_hash(ahp->tfm); |
| 401 | kfree(ahp); | 487 | kfree(ahp); |
| 402 | } | 488 | } |
| 403 | return -EINVAL; | 489 | return -EINVAL; |
| @@ -412,7 +498,7 @@ static void ah6_destroy(struct xfrm_state *x) | |||
| 412 | 498 | ||
| 413 | kfree(ahp->work_icv); | 499 | kfree(ahp->work_icv); |
| 414 | ahp->work_icv = NULL; | 500 | ahp->work_icv = NULL; |
| 415 | crypto_free_tfm(ahp->tfm); | 501 | crypto_free_hash(ahp->tfm); |
| 416 | ahp->tfm = NULL; | 502 | ahp->tfm = NULL; |
| 417 | kfree(ahp); | 503 | kfree(ahp); |
| 418 | } | 504 | } |
| @@ -425,7 +511,8 @@ static struct xfrm_type ah6_type = | |||
| 425 | .init_state = ah6_init_state, | 511 | .init_state = ah6_init_state, |
| 426 | .destructor = ah6_destroy, | 512 | .destructor = ah6_destroy, |
| 427 | .input = ah6_input, | 513 | .input = ah6_input, |
| 428 | .output = ah6_output | 514 | .output = ah6_output, |
| 515 | .hdr_offset = xfrm6_find_1stfragopt, | ||
| 429 | }; | 516 | }; |
| 430 | 517 | ||
| 431 | static struct inet6_protocol ah6_protocol = { | 518 | static struct inet6_protocol ah6_protocol = { |
