diff options
Diffstat (limited to 'lib/kobject_uevent.c')
-rw-r--r-- | lib/kobject_uevent.c | 79 |
1 files changed, 78 insertions, 1 deletions
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 54cfbaeb3a4e..fa10ad8e9b17 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/uuid.h> | 25 | #include <linux/uuid.h> |
26 | #include <linux/ctype.h> | 26 | #include <linux/ctype.h> |
27 | #include <net/sock.h> | 27 | #include <net/sock.h> |
28 | #include <net/netlink.h> | ||
28 | #include <net/net_namespace.h> | 29 | #include <net/net_namespace.h> |
29 | 30 | ||
30 | 31 | ||
@@ -604,12 +605,88 @@ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) | |||
604 | EXPORT_SYMBOL_GPL(add_uevent_var); | 605 | EXPORT_SYMBOL_GPL(add_uevent_var); |
605 | 606 | ||
606 | #if defined(CONFIG_NET) | 607 | #if defined(CONFIG_NET) |
608 | static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb, | ||
609 | struct netlink_ext_ack *extack) | ||
610 | { | ||
611 | /* u64 to chars: 2^64 - 1 = 21 chars */ | ||
612 | char buf[sizeof("SEQNUM=") + 21]; | ||
613 | struct sk_buff *skbc; | ||
614 | int ret; | ||
615 | |||
616 | /* bump and prepare sequence number */ | ||
617 | ret = snprintf(buf, sizeof(buf), "SEQNUM=%llu", ++uevent_seqnum); | ||
618 | if (ret < 0 || (size_t)ret >= sizeof(buf)) | ||
619 | return -ENOMEM; | ||
620 | ret++; | ||
621 | |||
622 | /* verify message does not overflow */ | ||
623 | if ((skb->len + ret) > UEVENT_BUFFER_SIZE) { | ||
624 | NL_SET_ERR_MSG(extack, "uevent message too big"); | ||
625 | return -EINVAL; | ||
626 | } | ||
627 | |||
628 | /* copy skb and extend to accommodate sequence number */ | ||
629 | skbc = skb_copy_expand(skb, 0, ret, GFP_KERNEL); | ||
630 | if (!skbc) | ||
631 | return -ENOMEM; | ||
632 | |||
633 | /* append sequence number */ | ||
634 | skb_put_data(skbc, buf, ret); | ||
635 | |||
636 | /* remove msg header */ | ||
637 | skb_pull(skbc, NLMSG_HDRLEN); | ||
638 | |||
639 | /* set portid 0 to inform userspace message comes from kernel */ | ||
640 | NETLINK_CB(skbc).portid = 0; | ||
641 | NETLINK_CB(skbc).dst_group = 1; | ||
642 | |||
643 | ret = netlink_broadcast(usk, skbc, 0, 1, GFP_KERNEL); | ||
644 | /* ENOBUFS should be handled in userspace */ | ||
645 | if (ret == -ENOBUFS || ret == -ESRCH) | ||
646 | ret = 0; | ||
647 | |||
648 | return ret; | ||
649 | } | ||
650 | |||
651 | static int uevent_net_rcv_skb(struct sk_buff *skb, struct nlmsghdr *nlh, | ||
652 | struct netlink_ext_ack *extack) | ||
653 | { | ||
654 | struct net *net; | ||
655 | int ret; | ||
656 | |||
657 | if (!nlmsg_data(nlh)) | ||
658 | return -EINVAL; | ||
659 | |||
660 | /* | ||
661 | * Verify that we are allowed to send messages to the target | ||
662 | * network namespace. The caller must have CAP_SYS_ADMIN in the | ||
663 | * owning user namespace of the target network namespace. | ||
664 | */ | ||
665 | net = sock_net(NETLINK_CB(skb).sk); | ||
666 | if (!netlink_ns_capable(skb, net->user_ns, CAP_SYS_ADMIN)) { | ||
667 | NL_SET_ERR_MSG(extack, "missing CAP_SYS_ADMIN capability"); | ||
668 | return -EPERM; | ||
669 | } | ||
670 | |||
671 | mutex_lock(&uevent_sock_mutex); | ||
672 | ret = uevent_net_broadcast(net->uevent_sock->sk, skb, extack); | ||
673 | mutex_unlock(&uevent_sock_mutex); | ||
674 | |||
675 | return ret; | ||
676 | } | ||
677 | |||
678 | static void uevent_net_rcv(struct sk_buff *skb) | ||
679 | { | ||
680 | netlink_rcv_skb(skb, &uevent_net_rcv_skb); | ||
681 | } | ||
682 | |||
607 | static int uevent_net_init(struct net *net) | 683 | static int uevent_net_init(struct net *net) |
608 | { | 684 | { |
609 | struct uevent_sock *ue_sk; | 685 | struct uevent_sock *ue_sk; |
610 | struct netlink_kernel_cfg cfg = { | 686 | struct netlink_kernel_cfg cfg = { |
611 | .groups = 1, | 687 | .groups = 1, |
612 | .flags = NL_CFG_F_NONROOT_RECV, | 688 | .input = uevent_net_rcv, |
689 | .flags = NL_CFG_F_NONROOT_RECV | ||
613 | }; | 690 | }; |
614 | 691 | ||
615 | ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL); | 692 | ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL); |