diff options
Diffstat (limited to 'lib/kobject_uevent.c')
-rw-r--r-- | lib/kobject_uevent.c | 137 |
1 files changed, 95 insertions, 42 deletions
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 649bf60a9440..63d0816ab23b 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c | |||
@@ -232,30 +232,6 @@ out: | |||
232 | return r; | 232 | return r; |
233 | } | 233 | } |
234 | 234 | ||
235 | #ifdef CONFIG_NET | ||
236 | static int kobj_bcast_filter(struct sock *dsk, struct sk_buff *skb, void *data) | ||
237 | { | ||
238 | struct kobject *kobj = data, *ksobj; | ||
239 | const struct kobj_ns_type_operations *ops; | ||
240 | |||
241 | ops = kobj_ns_ops(kobj); | ||
242 | if (!ops && kobj->kset) { | ||
243 | ksobj = &kobj->kset->kobj; | ||
244 | if (ksobj->parent != NULL) | ||
245 | ops = kobj_ns_ops(ksobj->parent); | ||
246 | } | ||
247 | |||
248 | if (ops && ops->netlink_ns && kobj->ktype->namespace) { | ||
249 | const void *sock_ns, *ns; | ||
250 | ns = kobj->ktype->namespace(kobj); | ||
251 | sock_ns = ops->netlink_ns(dsk); | ||
252 | return sock_ns != ns; | ||
253 | } | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | #endif | ||
258 | |||
259 | #ifdef CONFIG_UEVENT_HELPER | 235 | #ifdef CONFIG_UEVENT_HELPER |
260 | static int kobj_usermode_filter(struct kobject *kobj) | 236 | static int kobj_usermode_filter(struct kobject *kobj) |
261 | { | 237 | { |
@@ -327,17 +303,14 @@ static struct sk_buff *alloc_uevent_skb(struct kobj_uevent_env *env, | |||
327 | 303 | ||
328 | return skb; | 304 | return skb; |
329 | } | 305 | } |
330 | #endif | ||
331 | 306 | ||
332 | static int kobject_uevent_net_broadcast(struct kobject *kobj, | 307 | static int uevent_net_broadcast_untagged(struct kobj_uevent_env *env, |
333 | struct kobj_uevent_env *env, | 308 | const char *action_string, |
334 | const char *action_string, | 309 | const char *devpath) |
335 | const char *devpath) | ||
336 | { | 310 | { |
337 | int retval = 0; | ||
338 | #if defined(CONFIG_NET) | ||
339 | struct sk_buff *skb = NULL; | 311 | struct sk_buff *skb = NULL; |
340 | struct uevent_sock *ue_sk; | 312 | struct uevent_sock *ue_sk; |
313 | int retval = 0; | ||
341 | 314 | ||
342 | /* send netlink message */ | 315 | /* send netlink message */ |
343 | list_for_each_entry(ue_sk, &uevent_sock_list, list) { | 316 | list_for_each_entry(ue_sk, &uevent_sock_list, list) { |
@@ -353,19 +326,93 @@ static int kobject_uevent_net_broadcast(struct kobject *kobj, | |||
353 | continue; | 326 | continue; |
354 | } | 327 | } |
355 | 328 | ||
356 | retval = netlink_broadcast_filtered(uevent_sock, skb_get(skb), | 329 | retval = netlink_broadcast(uevent_sock, skb_get(skb), 0, 1, |
357 | 0, 1, GFP_KERNEL, | 330 | GFP_KERNEL); |
358 | kobj_bcast_filter, | ||
359 | kobj); | ||
360 | /* ENOBUFS should be handled in userspace */ | 331 | /* ENOBUFS should be handled in userspace */ |
361 | if (retval == -ENOBUFS || retval == -ESRCH) | 332 | if (retval == -ENOBUFS || retval == -ESRCH) |
362 | retval = 0; | 333 | retval = 0; |
363 | } | 334 | } |
364 | consume_skb(skb); | 335 | consume_skb(skb); |
365 | #endif | 336 | |
366 | return retval; | 337 | return retval; |
367 | } | 338 | } |
368 | 339 | ||
340 | static int uevent_net_broadcast_tagged(struct sock *usk, | ||
341 | struct kobj_uevent_env *env, | ||
342 | const char *action_string, | ||
343 | const char *devpath) | ||
344 | { | ||
345 | struct user_namespace *owning_user_ns = sock_net(usk)->user_ns; | ||
346 | struct sk_buff *skb = NULL; | ||
347 | int ret = 0; | ||
348 | |||
349 | skb = alloc_uevent_skb(env, action_string, devpath); | ||
350 | if (!skb) | ||
351 | return -ENOMEM; | ||
352 | |||
353 | /* fix credentials */ | ||
354 | if (owning_user_ns != &init_user_ns) { | ||
355 | struct netlink_skb_parms *parms = &NETLINK_CB(skb); | ||
356 | kuid_t root_uid; | ||
357 | kgid_t root_gid; | ||
358 | |||
359 | /* fix uid */ | ||
360 | root_uid = make_kuid(owning_user_ns, 0); | ||
361 | if (uid_valid(root_uid)) | ||
362 | parms->creds.uid = root_uid; | ||
363 | |||
364 | /* fix gid */ | ||
365 | root_gid = make_kgid(owning_user_ns, 0); | ||
366 | if (gid_valid(root_gid)) | ||
367 | parms->creds.gid = root_gid; | ||
368 | } | ||
369 | |||
370 | ret = netlink_broadcast(usk, skb, 0, 1, GFP_KERNEL); | ||
371 | /* ENOBUFS should be handled in userspace */ | ||
372 | if (ret == -ENOBUFS || ret == -ESRCH) | ||
373 | ret = 0; | ||
374 | |||
375 | return ret; | ||
376 | } | ||
377 | #endif | ||
378 | |||
379 | static int kobject_uevent_net_broadcast(struct kobject *kobj, | ||
380 | struct kobj_uevent_env *env, | ||
381 | const char *action_string, | ||
382 | const char *devpath) | ||
383 | { | ||
384 | int ret = 0; | ||
385 | |||
386 | #ifdef CONFIG_NET | ||
387 | const struct kobj_ns_type_operations *ops; | ||
388 | const struct net *net = NULL; | ||
389 | |||
390 | ops = kobj_ns_ops(kobj); | ||
391 | if (!ops && kobj->kset) { | ||
392 | struct kobject *ksobj = &kobj->kset->kobj; | ||
393 | if (ksobj->parent != NULL) | ||
394 | ops = kobj_ns_ops(ksobj->parent); | ||
395 | } | ||
396 | |||
397 | /* kobjects currently only carry network namespace tags and they | ||
398 | * are the only tag relevant here since we want to decide which | ||
399 | * network namespaces to broadcast the uevent into. | ||
400 | */ | ||
401 | if (ops && ops->netlink_ns && kobj->ktype->namespace) | ||
402 | if (ops->type == KOBJ_NS_TYPE_NET) | ||
403 | net = kobj->ktype->namespace(kobj); | ||
404 | |||
405 | if (!net) | ||
406 | ret = uevent_net_broadcast_untagged(env, action_string, | ||
407 | devpath); | ||
408 | else | ||
409 | ret = uevent_net_broadcast_tagged(net->uevent_sock->sk, env, | ||
410 | action_string, devpath); | ||
411 | #endif | ||
412 | |||
413 | return ret; | ||
414 | } | ||
415 | |||
369 | static void zap_modalias_env(struct kobj_uevent_env *env) | 416 | static void zap_modalias_env(struct kobj_uevent_env *env) |
370 | { | 417 | { |
371 | static const char modalias_prefix[] = "MODALIAS="; | 418 | static const char modalias_prefix[] = "MODALIAS="; |
@@ -724,9 +771,13 @@ static int uevent_net_init(struct net *net) | |||
724 | 771 | ||
725 | net->uevent_sock = ue_sk; | 772 | net->uevent_sock = ue_sk; |
726 | 773 | ||
727 | mutex_lock(&uevent_sock_mutex); | 774 | /* Restrict uevents to initial user namespace. */ |
728 | list_add_tail(&ue_sk->list, &uevent_sock_list); | 775 | if (sock_net(ue_sk->sk)->user_ns == &init_user_ns) { |
729 | mutex_unlock(&uevent_sock_mutex); | 776 | mutex_lock(&uevent_sock_mutex); |
777 | list_add_tail(&ue_sk->list, &uevent_sock_list); | ||
778 | mutex_unlock(&uevent_sock_mutex); | ||
779 | } | ||
780 | |||
730 | return 0; | 781 | return 0; |
731 | } | 782 | } |
732 | 783 | ||
@@ -734,9 +785,11 @@ static void uevent_net_exit(struct net *net) | |||
734 | { | 785 | { |
735 | struct uevent_sock *ue_sk = net->uevent_sock; | 786 | struct uevent_sock *ue_sk = net->uevent_sock; |
736 | 787 | ||
737 | mutex_lock(&uevent_sock_mutex); | 788 | if (sock_net(ue_sk->sk)->user_ns == &init_user_ns) { |
738 | list_del(&ue_sk->list); | 789 | mutex_lock(&uevent_sock_mutex); |
739 | mutex_unlock(&uevent_sock_mutex); | 790 | list_del(&ue_sk->list); |
791 | mutex_unlock(&uevent_sock_mutex); | ||
792 | } | ||
740 | 793 | ||
741 | netlink_kernel_release(ue_sk->sk); | 794 | netlink_kernel_release(ue_sk->sk); |
742 | kfree(ue_sk); | 795 | kfree(ue_sk); |