18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * kernel userspace event delivery 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Red Hat, Inc. All rights reserved. 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 Novell, Inc. All rights reserved. 78c2ecf20Sopenharmony_ci * Copyright (C) 2004 IBM, Inc. All rights reserved. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Authors: 108c2ecf20Sopenharmony_ci * Robert Love <rml@novell.com> 118c2ecf20Sopenharmony_ci * Kay Sievers <kay.sievers@vrfy.org> 128c2ecf20Sopenharmony_ci * Arjan van de Ven <arjanv@redhat.com> 138c2ecf20Sopenharmony_ci * Greg Kroah-Hartman <greg@kroah.com> 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 178c2ecf20Sopenharmony_ci#include <linux/string.h> 188c2ecf20Sopenharmony_ci#include <linux/kobject.h> 198c2ecf20Sopenharmony_ci#include <linux/export.h> 208c2ecf20Sopenharmony_ci#include <linux/kmod.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/socket.h> 238c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 248c2ecf20Sopenharmony_ci#include <linux/netlink.h> 258c2ecf20Sopenharmony_ci#include <linux/uidgid.h> 268c2ecf20Sopenharmony_ci#include <linux/uuid.h> 278c2ecf20Sopenharmony_ci#include <linux/ctype.h> 288c2ecf20Sopenharmony_ci#include <net/sock.h> 298c2ecf20Sopenharmony_ci#include <net/netlink.h> 308c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciu64 uevent_seqnum; 348c2ecf20Sopenharmony_ci#ifdef CONFIG_UEVENT_HELPER 358c2ecf20Sopenharmony_cichar uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; 368c2ecf20Sopenharmony_ci#endif 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct uevent_sock { 398c2ecf20Sopenharmony_ci struct list_head list; 408c2ecf20Sopenharmony_ci struct sock *sk; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#ifdef CONFIG_NET 448c2ecf20Sopenharmony_cistatic LIST_HEAD(uevent_sock_list); 458c2ecf20Sopenharmony_ci#endif 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* This lock protects uevent_seqnum and uevent_sock_list */ 488c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(uevent_sock_mutex); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* the strings here must match the enum in include/linux/kobject.h */ 518c2ecf20Sopenharmony_cistatic const char *kobject_actions[] = { 528c2ecf20Sopenharmony_ci [KOBJ_ADD] = "add", 538c2ecf20Sopenharmony_ci [KOBJ_REMOVE] = "remove", 548c2ecf20Sopenharmony_ci [KOBJ_CHANGE] = "change", 558c2ecf20Sopenharmony_ci [KOBJ_MOVE] = "move", 568c2ecf20Sopenharmony_ci [KOBJ_ONLINE] = "online", 578c2ecf20Sopenharmony_ci [KOBJ_OFFLINE] = "offline", 588c2ecf20Sopenharmony_ci [KOBJ_BIND] = "bind", 598c2ecf20Sopenharmony_ci [KOBJ_UNBIND] = "unbind", 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int kobject_action_type(const char *buf, size_t count, 638c2ecf20Sopenharmony_ci enum kobject_action *type, 648c2ecf20Sopenharmony_ci const char **args) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci enum kobject_action action; 678c2ecf20Sopenharmony_ci size_t count_first; 688c2ecf20Sopenharmony_ci const char *args_start; 698c2ecf20Sopenharmony_ci int ret = -EINVAL; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (count && (buf[count-1] == '\n' || buf[count-1] == '\0')) 728c2ecf20Sopenharmony_ci count--; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (!count) 758c2ecf20Sopenharmony_ci goto out; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci args_start = strnchr(buf, count, ' '); 788c2ecf20Sopenharmony_ci if (args_start) { 798c2ecf20Sopenharmony_ci count_first = args_start - buf; 808c2ecf20Sopenharmony_ci args_start = args_start + 1; 818c2ecf20Sopenharmony_ci } else 828c2ecf20Sopenharmony_ci count_first = count; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) { 858c2ecf20Sopenharmony_ci if (strncmp(kobject_actions[action], buf, count_first) != 0) 868c2ecf20Sopenharmony_ci continue; 878c2ecf20Sopenharmony_ci if (kobject_actions[action][count_first] != '\0') 888c2ecf20Sopenharmony_ci continue; 898c2ecf20Sopenharmony_ci if (args) 908c2ecf20Sopenharmony_ci *args = args_start; 918c2ecf20Sopenharmony_ci *type = action; 928c2ecf20Sopenharmony_ci ret = 0; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ciout: 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const char *action_arg_word_end(const char *buf, const char *buf_end, 1008c2ecf20Sopenharmony_ci char delim) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci const char *next = buf; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci while (next <= buf_end && *next != delim) 1058c2ecf20Sopenharmony_ci if (!isalnum(*next++)) 1068c2ecf20Sopenharmony_ci return NULL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (next == buf) 1098c2ecf20Sopenharmony_ci return NULL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return next; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int kobject_action_args(const char *buf, size_t count, 1158c2ecf20Sopenharmony_ci struct kobj_uevent_env **ret_env) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct kobj_uevent_env *env = NULL; 1188c2ecf20Sopenharmony_ci const char *next, *buf_end, *key; 1198c2ecf20Sopenharmony_ci int key_len; 1208c2ecf20Sopenharmony_ci int r = -EINVAL; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (count && (buf[count - 1] == '\n' || buf[count - 1] == '\0')) 1238c2ecf20Sopenharmony_ci count--; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (!count) 1268c2ecf20Sopenharmony_ci return -EINVAL; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci env = kzalloc(sizeof(*env), GFP_KERNEL); 1298c2ecf20Sopenharmony_ci if (!env) 1308c2ecf20Sopenharmony_ci return -ENOMEM; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* first arg is UUID */ 1338c2ecf20Sopenharmony_ci if (count < UUID_STRING_LEN || !uuid_is_valid(buf) || 1348c2ecf20Sopenharmony_ci add_uevent_var(env, "SYNTH_UUID=%.*s", UUID_STRING_LEN, buf)) 1358c2ecf20Sopenharmony_ci goto out; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * the rest are custom environment variables in KEY=VALUE 1398c2ecf20Sopenharmony_ci * format with ' ' delimiter between each KEY=VALUE pair 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci next = buf + UUID_STRING_LEN; 1428c2ecf20Sopenharmony_ci buf_end = buf + count - 1; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci while (next <= buf_end) { 1458c2ecf20Sopenharmony_ci if (*next != ' ') 1468c2ecf20Sopenharmony_ci goto out; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* skip the ' ', key must follow */ 1498c2ecf20Sopenharmony_ci key = ++next; 1508c2ecf20Sopenharmony_ci if (key > buf_end) 1518c2ecf20Sopenharmony_ci goto out; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci buf = next; 1548c2ecf20Sopenharmony_ci next = action_arg_word_end(buf, buf_end, '='); 1558c2ecf20Sopenharmony_ci if (!next || next > buf_end || *next != '=') 1568c2ecf20Sopenharmony_ci goto out; 1578c2ecf20Sopenharmony_ci key_len = next - buf; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* skip the '=', value must follow */ 1608c2ecf20Sopenharmony_ci if (++next > buf_end) 1618c2ecf20Sopenharmony_ci goto out; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci buf = next; 1648c2ecf20Sopenharmony_ci next = action_arg_word_end(buf, buf_end, ' '); 1658c2ecf20Sopenharmony_ci if (!next) 1668c2ecf20Sopenharmony_ci goto out; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (add_uevent_var(env, "SYNTH_ARG_%.*s=%.*s", 1698c2ecf20Sopenharmony_ci key_len, key, (int) (next - buf), buf)) 1708c2ecf20Sopenharmony_ci goto out; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci r = 0; 1748c2ecf20Sopenharmony_ciout: 1758c2ecf20Sopenharmony_ci if (r) 1768c2ecf20Sopenharmony_ci kfree(env); 1778c2ecf20Sopenharmony_ci else 1788c2ecf20Sopenharmony_ci *ret_env = env; 1798c2ecf20Sopenharmony_ci return r; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/** 1838c2ecf20Sopenharmony_ci * kobject_synth_uevent - send synthetic uevent with arguments 1848c2ecf20Sopenharmony_ci * 1858c2ecf20Sopenharmony_ci * @kobj: struct kobject for which synthetic uevent is to be generated 1868c2ecf20Sopenharmony_ci * @buf: buffer containing action type and action args, newline is ignored 1878c2ecf20Sopenharmony_ci * @count: length of buffer 1888c2ecf20Sopenharmony_ci * 1898c2ecf20Sopenharmony_ci * Returns 0 if kobject_synthetic_uevent() is completed with success or the 1908c2ecf20Sopenharmony_ci * corresponding error when it fails. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ciint kobject_synth_uevent(struct kobject *kobj, const char *buf, size_t count) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci char *no_uuid_envp[] = { "SYNTH_UUID=0", NULL }; 1958c2ecf20Sopenharmony_ci enum kobject_action action; 1968c2ecf20Sopenharmony_ci const char *action_args; 1978c2ecf20Sopenharmony_ci struct kobj_uevent_env *env; 1988c2ecf20Sopenharmony_ci const char *msg = NULL, *devpath; 1998c2ecf20Sopenharmony_ci int r; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci r = kobject_action_type(buf, count, &action, &action_args); 2028c2ecf20Sopenharmony_ci if (r) { 2038c2ecf20Sopenharmony_ci msg = "unknown uevent action string"; 2048c2ecf20Sopenharmony_ci goto out; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (!action_args) { 2088c2ecf20Sopenharmony_ci r = kobject_uevent_env(kobj, action, no_uuid_envp); 2098c2ecf20Sopenharmony_ci goto out; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci r = kobject_action_args(action_args, 2138c2ecf20Sopenharmony_ci count - (action_args - buf), &env); 2148c2ecf20Sopenharmony_ci if (r == -EINVAL) { 2158c2ecf20Sopenharmony_ci msg = "incorrect uevent action arguments"; 2168c2ecf20Sopenharmony_ci goto out; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (r) 2208c2ecf20Sopenharmony_ci goto out; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci r = kobject_uevent_env(kobj, action, env->envp); 2238c2ecf20Sopenharmony_ci kfree(env); 2248c2ecf20Sopenharmony_ciout: 2258c2ecf20Sopenharmony_ci if (r) { 2268c2ecf20Sopenharmony_ci devpath = kobject_get_path(kobj, GFP_KERNEL); 2278c2ecf20Sopenharmony_ci pr_warn("synth uevent: %s: %s\n", 2288c2ecf20Sopenharmony_ci devpath ?: "unknown device", 2298c2ecf20Sopenharmony_ci msg ?: "failed to send uevent"); 2308c2ecf20Sopenharmony_ci kfree(devpath); 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci return r; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci#ifdef CONFIG_UEVENT_HELPER 2368c2ecf20Sopenharmony_cistatic int kobj_usermode_filter(struct kobject *kobj) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci const struct kobj_ns_type_operations *ops; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci ops = kobj_ns_ops(kobj); 2418c2ecf20Sopenharmony_ci if (ops) { 2428c2ecf20Sopenharmony_ci const void *init_ns, *ns; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ns = kobj->ktype->namespace(kobj); 2458c2ecf20Sopenharmony_ci init_ns = ops->initial_ns(); 2468c2ecf20Sopenharmony_ci return ns != init_ns; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci int buffer_size = sizeof(env->buf) - env->buflen; 2558c2ecf20Sopenharmony_ci int len; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci len = strlcpy(&env->buf[env->buflen], subsystem, buffer_size); 2588c2ecf20Sopenharmony_ci if (len >= buffer_size) { 2598c2ecf20Sopenharmony_ci pr_warn("init_uevent_argv: buffer size of %d too small, needed %d\n", 2608c2ecf20Sopenharmony_ci buffer_size, len); 2618c2ecf20Sopenharmony_ci return -ENOMEM; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci env->argv[0] = uevent_helper; 2658c2ecf20Sopenharmony_ci env->argv[1] = &env->buf[env->buflen]; 2668c2ecf20Sopenharmony_ci env->argv[2] = NULL; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci env->buflen += len + 1; 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void cleanup_uevent_env(struct subprocess_info *info) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci kfree(info->data); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci#endif 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci#ifdef CONFIG_NET 2798c2ecf20Sopenharmony_cistatic struct sk_buff *alloc_uevent_skb(struct kobj_uevent_env *env, 2808c2ecf20Sopenharmony_ci const char *action_string, 2818c2ecf20Sopenharmony_ci const char *devpath) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct netlink_skb_parms *parms; 2848c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 2858c2ecf20Sopenharmony_ci char *scratch; 2868c2ecf20Sopenharmony_ci size_t len; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* allocate message with maximum possible size */ 2898c2ecf20Sopenharmony_ci len = strlen(action_string) + strlen(devpath) + 2; 2908c2ecf20Sopenharmony_ci skb = alloc_skb(len + env->buflen, GFP_KERNEL); 2918c2ecf20Sopenharmony_ci if (!skb) 2928c2ecf20Sopenharmony_ci return NULL; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* add header */ 2958c2ecf20Sopenharmony_ci scratch = skb_put(skb, len); 2968c2ecf20Sopenharmony_ci sprintf(scratch, "%s@%s", action_string, devpath); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci skb_put_data(skb, env->buf, env->buflen); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci parms = &NETLINK_CB(skb); 3018c2ecf20Sopenharmony_ci parms->creds.uid = GLOBAL_ROOT_UID; 3028c2ecf20Sopenharmony_ci parms->creds.gid = GLOBAL_ROOT_GID; 3038c2ecf20Sopenharmony_ci parms->dst_group = 1; 3048c2ecf20Sopenharmony_ci parms->portid = 0; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return skb; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int uevent_net_broadcast_untagged(struct kobj_uevent_env *env, 3108c2ecf20Sopenharmony_ci const char *action_string, 3118c2ecf20Sopenharmony_ci const char *devpath) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 3148c2ecf20Sopenharmony_ci struct uevent_sock *ue_sk; 3158c2ecf20Sopenharmony_ci int retval = 0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* send netlink message */ 3188c2ecf20Sopenharmony_ci list_for_each_entry(ue_sk, &uevent_sock_list, list) { 3198c2ecf20Sopenharmony_ci struct sock *uevent_sock = ue_sk->sk; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (!netlink_has_listeners(uevent_sock, 1)) 3228c2ecf20Sopenharmony_ci continue; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (!skb) { 3258c2ecf20Sopenharmony_ci retval = -ENOMEM; 3268c2ecf20Sopenharmony_ci skb = alloc_uevent_skb(env, action_string, devpath); 3278c2ecf20Sopenharmony_ci if (!skb) 3288c2ecf20Sopenharmony_ci continue; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci retval = netlink_broadcast(uevent_sock, skb_get(skb), 0, 1, 3328c2ecf20Sopenharmony_ci GFP_KERNEL); 3338c2ecf20Sopenharmony_ci /* ENOBUFS should be handled in userspace */ 3348c2ecf20Sopenharmony_ci if (retval == -ENOBUFS || retval == -ESRCH) 3358c2ecf20Sopenharmony_ci retval = 0; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci consume_skb(skb); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return retval; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int uevent_net_broadcast_tagged(struct sock *usk, 3438c2ecf20Sopenharmony_ci struct kobj_uevent_env *env, 3448c2ecf20Sopenharmony_ci const char *action_string, 3458c2ecf20Sopenharmony_ci const char *devpath) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct user_namespace *owning_user_ns = sock_net(usk)->user_ns; 3488c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 3498c2ecf20Sopenharmony_ci int ret = 0; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci skb = alloc_uevent_skb(env, action_string, devpath); 3528c2ecf20Sopenharmony_ci if (!skb) 3538c2ecf20Sopenharmony_ci return -ENOMEM; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* fix credentials */ 3568c2ecf20Sopenharmony_ci if (owning_user_ns != &init_user_ns) { 3578c2ecf20Sopenharmony_ci struct netlink_skb_parms *parms = &NETLINK_CB(skb); 3588c2ecf20Sopenharmony_ci kuid_t root_uid; 3598c2ecf20Sopenharmony_ci kgid_t root_gid; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* fix uid */ 3628c2ecf20Sopenharmony_ci root_uid = make_kuid(owning_user_ns, 0); 3638c2ecf20Sopenharmony_ci if (uid_valid(root_uid)) 3648c2ecf20Sopenharmony_ci parms->creds.uid = root_uid; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* fix gid */ 3678c2ecf20Sopenharmony_ci root_gid = make_kgid(owning_user_ns, 0); 3688c2ecf20Sopenharmony_ci if (gid_valid(root_gid)) 3698c2ecf20Sopenharmony_ci parms->creds.gid = root_gid; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ret = netlink_broadcast(usk, skb, 0, 1, GFP_KERNEL); 3738c2ecf20Sopenharmony_ci /* ENOBUFS should be handled in userspace */ 3748c2ecf20Sopenharmony_ci if (ret == -ENOBUFS || ret == -ESRCH) 3758c2ecf20Sopenharmony_ci ret = 0; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci#endif 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int kobject_uevent_net_broadcast(struct kobject *kobj, 3828c2ecf20Sopenharmony_ci struct kobj_uevent_env *env, 3838c2ecf20Sopenharmony_ci const char *action_string, 3848c2ecf20Sopenharmony_ci const char *devpath) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci int ret = 0; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci#ifdef CONFIG_NET 3898c2ecf20Sopenharmony_ci const struct kobj_ns_type_operations *ops; 3908c2ecf20Sopenharmony_ci const struct net *net = NULL; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ops = kobj_ns_ops(kobj); 3938c2ecf20Sopenharmony_ci if (!ops && kobj->kset) { 3948c2ecf20Sopenharmony_ci struct kobject *ksobj = &kobj->kset->kobj; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (ksobj->parent != NULL) 3978c2ecf20Sopenharmony_ci ops = kobj_ns_ops(ksobj->parent); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* kobjects currently only carry network namespace tags and they 4018c2ecf20Sopenharmony_ci * are the only tag relevant here since we want to decide which 4028c2ecf20Sopenharmony_ci * network namespaces to broadcast the uevent into. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_ci if (ops && ops->netlink_ns && kobj->ktype->namespace) 4058c2ecf20Sopenharmony_ci if (ops->type == KOBJ_NS_TYPE_NET) 4068c2ecf20Sopenharmony_ci net = kobj->ktype->namespace(kobj); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (!net) 4098c2ecf20Sopenharmony_ci ret = uevent_net_broadcast_untagged(env, action_string, 4108c2ecf20Sopenharmony_ci devpath); 4118c2ecf20Sopenharmony_ci else 4128c2ecf20Sopenharmony_ci ret = uevent_net_broadcast_tagged(net->uevent_sock->sk, env, 4138c2ecf20Sopenharmony_ci action_string, devpath); 4148c2ecf20Sopenharmony_ci#endif 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return ret; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic void zap_modalias_env(struct kobj_uevent_env *env) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci static const char modalias_prefix[] = "MODALIAS="; 4228c2ecf20Sopenharmony_ci size_t len; 4238c2ecf20Sopenharmony_ci int i, j; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci for (i = 0; i < env->envp_idx;) { 4268c2ecf20Sopenharmony_ci if (strncmp(env->envp[i], modalias_prefix, 4278c2ecf20Sopenharmony_ci sizeof(modalias_prefix) - 1)) { 4288c2ecf20Sopenharmony_ci i++; 4298c2ecf20Sopenharmony_ci continue; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci len = strlen(env->envp[i]) + 1; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (i != env->envp_idx - 1) { 4358c2ecf20Sopenharmony_ci /* @env->envp[] contains pointers to @env->buf[] 4368c2ecf20Sopenharmony_ci * with @env->buflen chars, and we are removing 4378c2ecf20Sopenharmony_ci * variable MODALIAS here pointed by @env->envp[i] 4388c2ecf20Sopenharmony_ci * with length @len as shown below: 4398c2ecf20Sopenharmony_ci * 4408c2ecf20Sopenharmony_ci * 0 @env->buf[] @env->buflen 4418c2ecf20Sopenharmony_ci * --------------------------------------------- 4428c2ecf20Sopenharmony_ci * ^ ^ ^ ^ 4438c2ecf20Sopenharmony_ci * | |-> @len <-| target block | 4448c2ecf20Sopenharmony_ci * @env->envp[0] @env->envp[i] @env->envp[i + 1] 4458c2ecf20Sopenharmony_ci * 4468c2ecf20Sopenharmony_ci * so the "target block" indicated above is moved 4478c2ecf20Sopenharmony_ci * backward by @len, and its right size is 4488c2ecf20Sopenharmony_ci * @env->buflen - (@env->envp[i + 1] - @env->envp[0]). 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci memmove(env->envp[i], env->envp[i + 1], 4518c2ecf20Sopenharmony_ci env->buflen - (env->envp[i + 1] - env->envp[0])); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci for (j = i; j < env->envp_idx - 1; j++) 4548c2ecf20Sopenharmony_ci env->envp[j] = env->envp[j + 1] - len; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci env->envp_idx--; 4588c2ecf20Sopenharmony_ci env->buflen -= len; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci/** 4638c2ecf20Sopenharmony_ci * kobject_uevent_env - send an uevent with environmental data 4648c2ecf20Sopenharmony_ci * 4658c2ecf20Sopenharmony_ci * @kobj: struct kobject that the action is happening to 4668c2ecf20Sopenharmony_ci * @action: action that is happening 4678c2ecf20Sopenharmony_ci * @envp_ext: pointer to environmental data 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * Returns 0 if kobject_uevent_env() is completed with success or the 4708c2ecf20Sopenharmony_ci * corresponding error when it fails. 4718c2ecf20Sopenharmony_ci */ 4728c2ecf20Sopenharmony_ciint kobject_uevent_env(struct kobject *kobj, enum kobject_action action, 4738c2ecf20Sopenharmony_ci char *envp_ext[]) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct kobj_uevent_env *env; 4768c2ecf20Sopenharmony_ci const char *action_string = kobject_actions[action]; 4778c2ecf20Sopenharmony_ci const char *devpath = NULL; 4788c2ecf20Sopenharmony_ci const char *subsystem; 4798c2ecf20Sopenharmony_ci struct kobject *top_kobj; 4808c2ecf20Sopenharmony_ci struct kset *kset; 4818c2ecf20Sopenharmony_ci const struct kset_uevent_ops *uevent_ops; 4828c2ecf20Sopenharmony_ci int i = 0; 4838c2ecf20Sopenharmony_ci int retval = 0; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* 4868c2ecf20Sopenharmony_ci * Mark "remove" event done regardless of result, for some subsystems 4878c2ecf20Sopenharmony_ci * do not want to re-trigger "remove" event via automatic cleanup. 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci if (action == KOBJ_REMOVE) 4908c2ecf20Sopenharmony_ci kobj->state_remove_uevent_sent = 1; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci pr_debug("kobject: '%s' (%p): %s\n", 4938c2ecf20Sopenharmony_ci kobject_name(kobj), kobj, __func__); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* search the kset we belong to */ 4968c2ecf20Sopenharmony_ci top_kobj = kobj; 4978c2ecf20Sopenharmony_ci while (!top_kobj->kset && top_kobj->parent) 4988c2ecf20Sopenharmony_ci top_kobj = top_kobj->parent; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (!top_kobj->kset) { 5018c2ecf20Sopenharmony_ci pr_debug("kobject: '%s' (%p): %s: attempted to send uevent " 5028c2ecf20Sopenharmony_ci "without kset!\n", kobject_name(kobj), kobj, 5038c2ecf20Sopenharmony_ci __func__); 5048c2ecf20Sopenharmony_ci return -EINVAL; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci kset = top_kobj->kset; 5088c2ecf20Sopenharmony_ci uevent_ops = kset->uevent_ops; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* skip the event, if uevent_suppress is set*/ 5118c2ecf20Sopenharmony_ci if (kobj->uevent_suppress) { 5128c2ecf20Sopenharmony_ci pr_debug("kobject: '%s' (%p): %s: uevent_suppress " 5138c2ecf20Sopenharmony_ci "caused the event to drop!\n", 5148c2ecf20Sopenharmony_ci kobject_name(kobj), kobj, __func__); 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci /* skip the event, if the filter returns zero. */ 5188c2ecf20Sopenharmony_ci if (uevent_ops && uevent_ops->filter) 5198c2ecf20Sopenharmony_ci if (!uevent_ops->filter(kset, kobj)) { 5208c2ecf20Sopenharmony_ci pr_debug("kobject: '%s' (%p): %s: filter function " 5218c2ecf20Sopenharmony_ci "caused the event to drop!\n", 5228c2ecf20Sopenharmony_ci kobject_name(kobj), kobj, __func__); 5238c2ecf20Sopenharmony_ci return 0; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* originating subsystem */ 5278c2ecf20Sopenharmony_ci if (uevent_ops && uevent_ops->name) 5288c2ecf20Sopenharmony_ci subsystem = uevent_ops->name(kset, kobj); 5298c2ecf20Sopenharmony_ci else 5308c2ecf20Sopenharmony_ci subsystem = kobject_name(&kset->kobj); 5318c2ecf20Sopenharmony_ci if (!subsystem) { 5328c2ecf20Sopenharmony_ci pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the " 5338c2ecf20Sopenharmony_ci "event to drop!\n", kobject_name(kobj), kobj, 5348c2ecf20Sopenharmony_ci __func__); 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* environment buffer */ 5398c2ecf20Sopenharmony_ci env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL); 5408c2ecf20Sopenharmony_ci if (!env) 5418c2ecf20Sopenharmony_ci return -ENOMEM; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* complete object path */ 5448c2ecf20Sopenharmony_ci devpath = kobject_get_path(kobj, GFP_KERNEL); 5458c2ecf20Sopenharmony_ci if (!devpath) { 5468c2ecf20Sopenharmony_ci retval = -ENOENT; 5478c2ecf20Sopenharmony_ci goto exit; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* default keys */ 5518c2ecf20Sopenharmony_ci retval = add_uevent_var(env, "ACTION=%s", action_string); 5528c2ecf20Sopenharmony_ci if (retval) 5538c2ecf20Sopenharmony_ci goto exit; 5548c2ecf20Sopenharmony_ci retval = add_uevent_var(env, "DEVPATH=%s", devpath); 5558c2ecf20Sopenharmony_ci if (retval) 5568c2ecf20Sopenharmony_ci goto exit; 5578c2ecf20Sopenharmony_ci retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem); 5588c2ecf20Sopenharmony_ci if (retval) 5598c2ecf20Sopenharmony_ci goto exit; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* keys passed in from the caller */ 5628c2ecf20Sopenharmony_ci if (envp_ext) { 5638c2ecf20Sopenharmony_ci for (i = 0; envp_ext[i]; i++) { 5648c2ecf20Sopenharmony_ci retval = add_uevent_var(env, "%s", envp_ext[i]); 5658c2ecf20Sopenharmony_ci if (retval) 5668c2ecf20Sopenharmony_ci goto exit; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* let the kset specific function add its stuff */ 5718c2ecf20Sopenharmony_ci if (uevent_ops && uevent_ops->uevent) { 5728c2ecf20Sopenharmony_ci retval = uevent_ops->uevent(kset, kobj, env); 5738c2ecf20Sopenharmony_ci if (retval) { 5748c2ecf20Sopenharmony_ci pr_debug("kobject: '%s' (%p): %s: uevent() returned " 5758c2ecf20Sopenharmony_ci "%d\n", kobject_name(kobj), kobj, 5768c2ecf20Sopenharmony_ci __func__, retval); 5778c2ecf20Sopenharmony_ci goto exit; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci switch (action) { 5828c2ecf20Sopenharmony_ci case KOBJ_ADD: 5838c2ecf20Sopenharmony_ci /* 5848c2ecf20Sopenharmony_ci * Mark "add" event so we can make sure we deliver "remove" 5858c2ecf20Sopenharmony_ci * event to userspace during automatic cleanup. If 5868c2ecf20Sopenharmony_ci * the object did send an "add" event, "remove" will 5878c2ecf20Sopenharmony_ci * automatically generated by the core, if not already done 5888c2ecf20Sopenharmony_ci * by the caller. 5898c2ecf20Sopenharmony_ci */ 5908c2ecf20Sopenharmony_ci kobj->state_add_uevent_sent = 1; 5918c2ecf20Sopenharmony_ci break; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci case KOBJ_UNBIND: 5948c2ecf20Sopenharmony_ci zap_modalias_env(env); 5958c2ecf20Sopenharmony_ci break; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci default: 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci mutex_lock(&uevent_sock_mutex); 6028c2ecf20Sopenharmony_ci /* we will send an event, so request a new sequence number */ 6038c2ecf20Sopenharmony_ci retval = add_uevent_var(env, "SEQNUM=%llu", ++uevent_seqnum); 6048c2ecf20Sopenharmony_ci if (retval) { 6058c2ecf20Sopenharmony_ci mutex_unlock(&uevent_sock_mutex); 6068c2ecf20Sopenharmony_ci goto exit; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci retval = kobject_uevent_net_broadcast(kobj, env, action_string, 6098c2ecf20Sopenharmony_ci devpath); 6108c2ecf20Sopenharmony_ci mutex_unlock(&uevent_sock_mutex); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci#ifdef CONFIG_UEVENT_HELPER 6138c2ecf20Sopenharmony_ci /* call uevent_helper, usually only enabled during early boot */ 6148c2ecf20Sopenharmony_ci if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { 6158c2ecf20Sopenharmony_ci struct subprocess_info *info; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci retval = add_uevent_var(env, "HOME=/"); 6188c2ecf20Sopenharmony_ci if (retval) 6198c2ecf20Sopenharmony_ci goto exit; 6208c2ecf20Sopenharmony_ci retval = add_uevent_var(env, 6218c2ecf20Sopenharmony_ci "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); 6228c2ecf20Sopenharmony_ci if (retval) 6238c2ecf20Sopenharmony_ci goto exit; 6248c2ecf20Sopenharmony_ci retval = init_uevent_argv(env, subsystem); 6258c2ecf20Sopenharmony_ci if (retval) 6268c2ecf20Sopenharmony_ci goto exit; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci retval = -ENOMEM; 6298c2ecf20Sopenharmony_ci info = call_usermodehelper_setup(env->argv[0], env->argv, 6308c2ecf20Sopenharmony_ci env->envp, GFP_KERNEL, 6318c2ecf20Sopenharmony_ci NULL, cleanup_uevent_env, env); 6328c2ecf20Sopenharmony_ci if (info) { 6338c2ecf20Sopenharmony_ci retval = call_usermodehelper_exec(info, UMH_NO_WAIT); 6348c2ecf20Sopenharmony_ci env = NULL; /* freed by cleanup_uevent_env */ 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci#endif 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ciexit: 6408c2ecf20Sopenharmony_ci kfree(devpath); 6418c2ecf20Sopenharmony_ci kfree(env); 6428c2ecf20Sopenharmony_ci return retval; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(kobject_uevent_env); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci/** 6478c2ecf20Sopenharmony_ci * kobject_uevent - notify userspace by sending an uevent 6488c2ecf20Sopenharmony_ci * 6498c2ecf20Sopenharmony_ci * @kobj: struct kobject that the action is happening to 6508c2ecf20Sopenharmony_ci * @action: action that is happening 6518c2ecf20Sopenharmony_ci * 6528c2ecf20Sopenharmony_ci * Returns 0 if kobject_uevent() is completed with success or the 6538c2ecf20Sopenharmony_ci * corresponding error when it fails. 6548c2ecf20Sopenharmony_ci */ 6558c2ecf20Sopenharmony_ciint kobject_uevent(struct kobject *kobj, enum kobject_action action) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci return kobject_uevent_env(kobj, action, NULL); 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(kobject_uevent); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci/** 6628c2ecf20Sopenharmony_ci * add_uevent_var - add key value string to the environment buffer 6638c2ecf20Sopenharmony_ci * @env: environment buffer structure 6648c2ecf20Sopenharmony_ci * @format: printf format for the key=value pair 6658c2ecf20Sopenharmony_ci * 6668c2ecf20Sopenharmony_ci * Returns 0 if environment variable was added successfully or -ENOMEM 6678c2ecf20Sopenharmony_ci * if no space was available. 6688c2ecf20Sopenharmony_ci */ 6698c2ecf20Sopenharmony_ciint add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci va_list args; 6728c2ecf20Sopenharmony_ci int len; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (env->envp_idx >= ARRAY_SIZE(env->envp)) { 6758c2ecf20Sopenharmony_ci WARN(1, KERN_ERR "add_uevent_var: too many keys\n"); 6768c2ecf20Sopenharmony_ci return -ENOMEM; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci va_start(args, format); 6808c2ecf20Sopenharmony_ci len = vsnprintf(&env->buf[env->buflen], 6818c2ecf20Sopenharmony_ci sizeof(env->buf) - env->buflen, 6828c2ecf20Sopenharmony_ci format, args); 6838c2ecf20Sopenharmony_ci va_end(args); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (len >= (sizeof(env->buf) - env->buflen)) { 6868c2ecf20Sopenharmony_ci WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n"); 6878c2ecf20Sopenharmony_ci return -ENOMEM; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci env->envp[env->envp_idx++] = &env->buf[env->buflen]; 6918c2ecf20Sopenharmony_ci env->buflen += len + 1; 6928c2ecf20Sopenharmony_ci return 0; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(add_uevent_var); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci#if defined(CONFIG_NET) 6978c2ecf20Sopenharmony_cistatic int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb, 6988c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci /* u64 to chars: 2^64 - 1 = 21 chars */ 7018c2ecf20Sopenharmony_ci char buf[sizeof("SEQNUM=") + 21]; 7028c2ecf20Sopenharmony_ci struct sk_buff *skbc; 7038c2ecf20Sopenharmony_ci int ret; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* bump and prepare sequence number */ 7068c2ecf20Sopenharmony_ci ret = snprintf(buf, sizeof(buf), "SEQNUM=%llu", ++uevent_seqnum); 7078c2ecf20Sopenharmony_ci if (ret < 0 || (size_t)ret >= sizeof(buf)) 7088c2ecf20Sopenharmony_ci return -ENOMEM; 7098c2ecf20Sopenharmony_ci ret++; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* verify message does not overflow */ 7128c2ecf20Sopenharmony_ci if ((skb->len + ret) > UEVENT_BUFFER_SIZE) { 7138c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "uevent message too big"); 7148c2ecf20Sopenharmony_ci return -EINVAL; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci /* copy skb and extend to accommodate sequence number */ 7188c2ecf20Sopenharmony_ci skbc = skb_copy_expand(skb, 0, ret, GFP_KERNEL); 7198c2ecf20Sopenharmony_ci if (!skbc) 7208c2ecf20Sopenharmony_ci return -ENOMEM; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* append sequence number */ 7238c2ecf20Sopenharmony_ci skb_put_data(skbc, buf, ret); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* remove msg header */ 7268c2ecf20Sopenharmony_ci skb_pull(skbc, NLMSG_HDRLEN); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* set portid 0 to inform userspace message comes from kernel */ 7298c2ecf20Sopenharmony_ci NETLINK_CB(skbc).portid = 0; 7308c2ecf20Sopenharmony_ci NETLINK_CB(skbc).dst_group = 1; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci ret = netlink_broadcast(usk, skbc, 0, 1, GFP_KERNEL); 7338c2ecf20Sopenharmony_ci /* ENOBUFS should be handled in userspace */ 7348c2ecf20Sopenharmony_ci if (ret == -ENOBUFS || ret == -ESRCH) 7358c2ecf20Sopenharmony_ci ret = 0; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci return ret; 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic int uevent_net_rcv_skb(struct sk_buff *skb, struct nlmsghdr *nlh, 7418c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct net *net; 7448c2ecf20Sopenharmony_ci int ret; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (!nlmsg_data(nlh)) 7478c2ecf20Sopenharmony_ci return -EINVAL; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* 7508c2ecf20Sopenharmony_ci * Verify that we are allowed to send messages to the target 7518c2ecf20Sopenharmony_ci * network namespace. The caller must have CAP_SYS_ADMIN in the 7528c2ecf20Sopenharmony_ci * owning user namespace of the target network namespace. 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_ci net = sock_net(NETLINK_CB(skb).sk); 7558c2ecf20Sopenharmony_ci if (!netlink_ns_capable(skb, net->user_ns, CAP_SYS_ADMIN)) { 7568c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "missing CAP_SYS_ADMIN capability"); 7578c2ecf20Sopenharmony_ci return -EPERM; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci mutex_lock(&uevent_sock_mutex); 7618c2ecf20Sopenharmony_ci ret = uevent_net_broadcast(net->uevent_sock->sk, skb, extack); 7628c2ecf20Sopenharmony_ci mutex_unlock(&uevent_sock_mutex); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return ret; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic void uevent_net_rcv(struct sk_buff *skb) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci netlink_rcv_skb(skb, &uevent_net_rcv_skb); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic int uevent_net_init(struct net *net) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci struct uevent_sock *ue_sk; 7758c2ecf20Sopenharmony_ci struct netlink_kernel_cfg cfg = { 7768c2ecf20Sopenharmony_ci .groups = 1, 7778c2ecf20Sopenharmony_ci .input = uevent_net_rcv, 7788c2ecf20Sopenharmony_ci .flags = NL_CFG_F_NONROOT_RECV 7798c2ecf20Sopenharmony_ci }; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL); 7828c2ecf20Sopenharmony_ci if (!ue_sk) 7838c2ecf20Sopenharmony_ci return -ENOMEM; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, &cfg); 7868c2ecf20Sopenharmony_ci if (!ue_sk->sk) { 7878c2ecf20Sopenharmony_ci pr_err("kobject_uevent: unable to create netlink socket!\n"); 7888c2ecf20Sopenharmony_ci kfree(ue_sk); 7898c2ecf20Sopenharmony_ci return -ENODEV; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci net->uevent_sock = ue_sk; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* Restrict uevents to initial user namespace. */ 7958c2ecf20Sopenharmony_ci if (sock_net(ue_sk->sk)->user_ns == &init_user_ns) { 7968c2ecf20Sopenharmony_ci mutex_lock(&uevent_sock_mutex); 7978c2ecf20Sopenharmony_ci list_add_tail(&ue_sk->list, &uevent_sock_list); 7988c2ecf20Sopenharmony_ci mutex_unlock(&uevent_sock_mutex); 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci return 0; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic void uevent_net_exit(struct net *net) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci struct uevent_sock *ue_sk = net->uevent_sock; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (sock_net(ue_sk->sk)->user_ns == &init_user_ns) { 8098c2ecf20Sopenharmony_ci mutex_lock(&uevent_sock_mutex); 8108c2ecf20Sopenharmony_ci list_del(&ue_sk->list); 8118c2ecf20Sopenharmony_ci mutex_unlock(&uevent_sock_mutex); 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci netlink_kernel_release(ue_sk->sk); 8158c2ecf20Sopenharmony_ci kfree(ue_sk); 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic struct pernet_operations uevent_net_ops = { 8198c2ecf20Sopenharmony_ci .init = uevent_net_init, 8208c2ecf20Sopenharmony_ci .exit = uevent_net_exit, 8218c2ecf20Sopenharmony_ci}; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic int __init kobject_uevent_init(void) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci return register_pernet_subsys(&uevent_net_ops); 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cipostcore_initcall(kobject_uevent_init); 8308c2ecf20Sopenharmony_ci#endif 831