diff options
Diffstat (limited to 'net/xfrm/xfrm_user.c')
-rw-r--r-- | net/xfrm/xfrm_user.c | 147 |
1 files changed, 131 insertions, 16 deletions
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index b95a2d64eb59..1ada6186933c 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c | |||
@@ -62,6 +62,22 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) | |||
62 | return 0; | 62 | return 0; |
63 | } | 63 | } |
64 | 64 | ||
65 | static int verify_auth_trunc(struct nlattr **attrs) | ||
66 | { | ||
67 | struct nlattr *rt = attrs[XFRMA_ALG_AUTH_TRUNC]; | ||
68 | struct xfrm_algo_auth *algp; | ||
69 | |||
70 | if (!rt) | ||
71 | return 0; | ||
72 | |||
73 | algp = nla_data(rt); | ||
74 | if (nla_len(rt) < xfrm_alg_auth_len(algp)) | ||
75 | return -EINVAL; | ||
76 | |||
77 | algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; | ||
78 | return 0; | ||
79 | } | ||
80 | |||
65 | static int verify_aead(struct nlattr **attrs) | 81 | static int verify_aead(struct nlattr **attrs) |
66 | { | 82 | { |
67 | struct nlattr *rt = attrs[XFRMA_ALG_AEAD]; | 83 | struct nlattr *rt = attrs[XFRMA_ALG_AEAD]; |
@@ -128,7 +144,8 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, | |||
128 | err = -EINVAL; | 144 | err = -EINVAL; |
129 | switch (p->id.proto) { | 145 | switch (p->id.proto) { |
130 | case IPPROTO_AH: | 146 | case IPPROTO_AH: |
131 | if (!attrs[XFRMA_ALG_AUTH] || | 147 | if ((!attrs[XFRMA_ALG_AUTH] && |
148 | !attrs[XFRMA_ALG_AUTH_TRUNC]) || | ||
132 | attrs[XFRMA_ALG_AEAD] || | 149 | attrs[XFRMA_ALG_AEAD] || |
133 | attrs[XFRMA_ALG_CRYPT] || | 150 | attrs[XFRMA_ALG_CRYPT] || |
134 | attrs[XFRMA_ALG_COMP]) | 151 | attrs[XFRMA_ALG_COMP]) |
@@ -139,10 +156,12 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, | |||
139 | if (attrs[XFRMA_ALG_COMP]) | 156 | if (attrs[XFRMA_ALG_COMP]) |
140 | goto out; | 157 | goto out; |
141 | if (!attrs[XFRMA_ALG_AUTH] && | 158 | if (!attrs[XFRMA_ALG_AUTH] && |
159 | !attrs[XFRMA_ALG_AUTH_TRUNC] && | ||
142 | !attrs[XFRMA_ALG_CRYPT] && | 160 | !attrs[XFRMA_ALG_CRYPT] && |
143 | !attrs[XFRMA_ALG_AEAD]) | 161 | !attrs[XFRMA_ALG_AEAD]) |
144 | goto out; | 162 | goto out; |
145 | if ((attrs[XFRMA_ALG_AUTH] || | 163 | if ((attrs[XFRMA_ALG_AUTH] || |
164 | attrs[XFRMA_ALG_AUTH_TRUNC] || | ||
146 | attrs[XFRMA_ALG_CRYPT]) && | 165 | attrs[XFRMA_ALG_CRYPT]) && |
147 | attrs[XFRMA_ALG_AEAD]) | 166 | attrs[XFRMA_ALG_AEAD]) |
148 | goto out; | 167 | goto out; |
@@ -152,6 +171,7 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, | |||
152 | if (!attrs[XFRMA_ALG_COMP] || | 171 | if (!attrs[XFRMA_ALG_COMP] || |
153 | attrs[XFRMA_ALG_AEAD] || | 172 | attrs[XFRMA_ALG_AEAD] || |
154 | attrs[XFRMA_ALG_AUTH] || | 173 | attrs[XFRMA_ALG_AUTH] || |
174 | attrs[XFRMA_ALG_AUTH_TRUNC] || | ||
155 | attrs[XFRMA_ALG_CRYPT]) | 175 | attrs[XFRMA_ALG_CRYPT]) |
156 | goto out; | 176 | goto out; |
157 | break; | 177 | break; |
@@ -161,6 +181,7 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, | |||
161 | case IPPROTO_ROUTING: | 181 | case IPPROTO_ROUTING: |
162 | if (attrs[XFRMA_ALG_COMP] || | 182 | if (attrs[XFRMA_ALG_COMP] || |
163 | attrs[XFRMA_ALG_AUTH] || | 183 | attrs[XFRMA_ALG_AUTH] || |
184 | attrs[XFRMA_ALG_AUTH_TRUNC] || | ||
164 | attrs[XFRMA_ALG_AEAD] || | 185 | attrs[XFRMA_ALG_AEAD] || |
165 | attrs[XFRMA_ALG_CRYPT] || | 186 | attrs[XFRMA_ALG_CRYPT] || |
166 | attrs[XFRMA_ENCAP] || | 187 | attrs[XFRMA_ENCAP] || |
@@ -176,6 +197,8 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, | |||
176 | 197 | ||
177 | if ((err = verify_aead(attrs))) | 198 | if ((err = verify_aead(attrs))) |
178 | goto out; | 199 | goto out; |
200 | if ((err = verify_auth_trunc(attrs))) | ||
201 | goto out; | ||
179 | if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) | 202 | if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) |
180 | goto out; | 203 | goto out; |
181 | if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) | 204 | if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) |
@@ -229,6 +252,66 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, | |||
229 | return 0; | 252 | return 0; |
230 | } | 253 | } |
231 | 254 | ||
255 | static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, | ||
256 | struct nlattr *rta) | ||
257 | { | ||
258 | struct xfrm_algo *ualg; | ||
259 | struct xfrm_algo_auth *p; | ||
260 | struct xfrm_algo_desc *algo; | ||
261 | |||
262 | if (!rta) | ||
263 | return 0; | ||
264 | |||
265 | ualg = nla_data(rta); | ||
266 | |||
267 | algo = xfrm_aalg_get_byname(ualg->alg_name, 1); | ||
268 | if (!algo) | ||
269 | return -ENOSYS; | ||
270 | *props = algo->desc.sadb_alg_id; | ||
271 | |||
272 | p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL); | ||
273 | if (!p) | ||
274 | return -ENOMEM; | ||
275 | |||
276 | strcpy(p->alg_name, algo->name); | ||
277 | p->alg_key_len = ualg->alg_key_len; | ||
278 | p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; | ||
279 | memcpy(p->alg_key, ualg->alg_key, (ualg->alg_key_len + 7) / 8); | ||
280 | |||
281 | *algpp = p; | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, | ||
286 | struct nlattr *rta) | ||
287 | { | ||
288 | struct xfrm_algo_auth *p, *ualg; | ||
289 | struct xfrm_algo_desc *algo; | ||
290 | |||
291 | if (!rta) | ||
292 | return 0; | ||
293 | |||
294 | ualg = nla_data(rta); | ||
295 | |||
296 | algo = xfrm_aalg_get_byname(ualg->alg_name, 1); | ||
297 | if (!algo) | ||
298 | return -ENOSYS; | ||
299 | if (ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) | ||
300 | return -EINVAL; | ||
301 | *props = algo->desc.sadb_alg_id; | ||
302 | |||
303 | p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL); | ||
304 | if (!p) | ||
305 | return -ENOMEM; | ||
306 | |||
307 | strcpy(p->alg_name, algo->name); | ||
308 | if (!p->alg_trunc_len) | ||
309 | p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; | ||
310 | |||
311 | *algpp = p; | ||
312 | return 0; | ||
313 | } | ||
314 | |||
232 | static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props, | 315 | static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props, |
233 | struct nlattr *rta) | 316 | struct nlattr *rta) |
234 | { | 317 | { |
@@ -332,10 +415,14 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, | |||
332 | if ((err = attach_aead(&x->aead, &x->props.ealgo, | 415 | if ((err = attach_aead(&x->aead, &x->props.ealgo, |
333 | attrs[XFRMA_ALG_AEAD]))) | 416 | attrs[XFRMA_ALG_AEAD]))) |
334 | goto error; | 417 | goto error; |
335 | if ((err = attach_one_algo(&x->aalg, &x->props.aalgo, | 418 | if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, |
336 | xfrm_aalg_get_byname, | 419 | attrs[XFRMA_ALG_AUTH_TRUNC]))) |
337 | attrs[XFRMA_ALG_AUTH]))) | ||
338 | goto error; | 420 | goto error; |
421 | if (!x->props.aalgo) { | ||
422 | if ((err = attach_auth(&x->aalg, &x->props.aalgo, | ||
423 | attrs[XFRMA_ALG_AUTH]))) | ||
424 | goto error; | ||
425 | } | ||
339 | if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, | 426 | if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, |
340 | xfrm_ealg_get_byname, | 427 | xfrm_ealg_get_byname, |
341 | attrs[XFRMA_ALG_CRYPT]))) | 428 | attrs[XFRMA_ALG_CRYPT]))) |
@@ -548,6 +635,24 @@ static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) | |||
548 | return 0; | 635 | return 0; |
549 | } | 636 | } |
550 | 637 | ||
638 | static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb) | ||
639 | { | ||
640 | struct xfrm_algo *algo; | ||
641 | struct nlattr *nla; | ||
642 | |||
643 | nla = nla_reserve(skb, XFRMA_ALG_AUTH, | ||
644 | sizeof(*algo) + (auth->alg_key_len + 7) / 8); | ||
645 | if (!nla) | ||
646 | return -EMSGSIZE; | ||
647 | |||
648 | algo = nla_data(nla); | ||
649 | strcpy(algo->alg_name, auth->alg_name); | ||
650 | memcpy(algo->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8); | ||
651 | algo->alg_key_len = auth->alg_key_len; | ||
652 | |||
653 | return 0; | ||
654 | } | ||
655 | |||
551 | /* Don't change this without updating xfrm_sa_len! */ | 656 | /* Don't change this without updating xfrm_sa_len! */ |
552 | static int copy_to_user_state_extra(struct xfrm_state *x, | 657 | static int copy_to_user_state_extra(struct xfrm_state *x, |
553 | struct xfrm_usersa_info *p, | 658 | struct xfrm_usersa_info *p, |
@@ -563,8 +668,13 @@ static int copy_to_user_state_extra(struct xfrm_state *x, | |||
563 | 668 | ||
564 | if (x->aead) | 669 | if (x->aead) |
565 | NLA_PUT(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead); | 670 | NLA_PUT(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead); |
566 | if (x->aalg) | 671 | if (x->aalg) { |
567 | NLA_PUT(skb, XFRMA_ALG_AUTH, xfrm_alg_len(x->aalg), x->aalg); | 672 | if (copy_to_user_auth(x->aalg, skb)) |
673 | goto nla_put_failure; | ||
674 | |||
675 | NLA_PUT(skb, XFRMA_ALG_AUTH_TRUNC, | ||
676 | xfrm_alg_auth_len(x->aalg), x->aalg); | ||
677 | } | ||
568 | if (x->ealg) | 678 | if (x->ealg) |
569 | NLA_PUT(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); | 679 | NLA_PUT(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); |
570 | if (x->calg) | 680 | if (x->calg) |
@@ -2117,8 +2227,11 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x) | |||
2117 | size_t l = 0; | 2227 | size_t l = 0; |
2118 | if (x->aead) | 2228 | if (x->aead) |
2119 | l += nla_total_size(aead_len(x->aead)); | 2229 | l += nla_total_size(aead_len(x->aead)); |
2120 | if (x->aalg) | 2230 | if (x->aalg) { |
2121 | l += nla_total_size(xfrm_alg_len(x->aalg)); | 2231 | l += nla_total_size(sizeof(struct xfrm_algo) + |
2232 | (x->aalg->alg_key_len + 7) / 8); | ||
2233 | l += nla_total_size(xfrm_alg_auth_len(x->aalg)); | ||
2234 | } | ||
2122 | if (x->ealg) | 2235 | if (x->ealg) |
2123 | l += nla_total_size(xfrm_alg_len(x->ealg)); | 2236 | l += nla_total_size(xfrm_alg_len(x->ealg)); |
2124 | if (x->calg) | 2237 | if (x->calg) |
@@ -2608,22 +2721,24 @@ static int __net_init xfrm_user_net_init(struct net *net) | |||
2608 | xfrm_netlink_rcv, NULL, THIS_MODULE); | 2721 | xfrm_netlink_rcv, NULL, THIS_MODULE); |
2609 | if (nlsk == NULL) | 2722 | if (nlsk == NULL) |
2610 | return -ENOMEM; | 2723 | return -ENOMEM; |
2724 | net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */ | ||
2611 | rcu_assign_pointer(net->xfrm.nlsk, nlsk); | 2725 | rcu_assign_pointer(net->xfrm.nlsk, nlsk); |
2612 | return 0; | 2726 | return 0; |
2613 | } | 2727 | } |
2614 | 2728 | ||
2615 | static void __net_exit xfrm_user_net_exit(struct net *net) | 2729 | static void __net_exit xfrm_user_net_exit(struct list_head *net_exit_list) |
2616 | { | 2730 | { |
2617 | struct sock *nlsk = net->xfrm.nlsk; | 2731 | struct net *net; |
2618 | 2732 | list_for_each_entry(net, net_exit_list, exit_list) | |
2619 | rcu_assign_pointer(net->xfrm.nlsk, NULL); | 2733 | rcu_assign_pointer(net->xfrm.nlsk, NULL); |
2620 | synchronize_rcu(); | 2734 | synchronize_net(); |
2621 | netlink_kernel_release(nlsk); | 2735 | list_for_each_entry(net, net_exit_list, exit_list) |
2736 | netlink_kernel_release(net->xfrm.nlsk_stash); | ||
2622 | } | 2737 | } |
2623 | 2738 | ||
2624 | static struct pernet_operations xfrm_user_net_ops = { | 2739 | static struct pernet_operations xfrm_user_net_ops = { |
2625 | .init = xfrm_user_net_init, | 2740 | .init = xfrm_user_net_init, |
2626 | .exit = xfrm_user_net_exit, | 2741 | .exit_batch = xfrm_user_net_exit, |
2627 | }; | 2742 | }; |
2628 | 2743 | ||
2629 | static int __init xfrm_user_init(void) | 2744 | static int __init xfrm_user_init(void) |