diff --git a/drivers/Kconfig b/drivers/Kconfig index efb66e25fa2dd2304f1caf6d503aa04fa33d9c4a..5d687aa32ecad24322c0b3aa1318a11a98b1d9b9 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -243,4 +243,6 @@ source "drivers/hte/Kconfig" source "drivers/cdx/Kconfig" +source "drivers/accesstokenid/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 1bec7819a837ab86c0169ca4b6a06b2d37b5483c..0c8dc4e22dfbdc06308b11e04ddbd4c076485d6d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -199,3 +199,5 @@ obj-$(CONFIG_DRM_ACCEL) += accel/ obj-$(CONFIG_CDX_BUS) += cdx/ obj-$(CONFIG_S390) += s390/ + +obj-$(CONFIG_ACCESS_TOKENID) += accesstokenid/ diff --git a/drivers/accesstokenid/Kconfig b/drivers/accesstokenid/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..30d2957a18888070d259c62edf547f0527be6bf6 --- /dev/null +++ b/drivers/accesstokenid/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +config ACCESS_TOKENID + bool "Access task's token" + default n + diff --git a/drivers/accesstokenid/Makefile b/drivers/accesstokenid/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..738a550f86cf0be05547563525a323c44aee5d15 --- /dev/null +++ b/drivers/accesstokenid/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_ACCESS_TOKENID) += access_tokenid.o diff --git a/drivers/accesstokenid/access_tokenid.c b/drivers/accesstokenid/access_tokenid.c new file mode 100644 index 0000000000000000000000000000000000000000..33a61ef163b3e8833cf265892fc20b934150a9e3 --- /dev/null +++ b/drivers/accesstokenid/access_tokenid.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * access_tokenid.c + * + * Copyright (C) 2022-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + */ + +#define pr_fmt(fmt) "access_token_id: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include "access_tokenid.h" + +DEFINE_RWLOCK(token_rwlock); +#define ACCESS_TOKEN_UID KUIDT_INIT(3020) +#define MAX_NODE_NUM 500 +#define UINT32_T_BITS 32 + +static struct kmem_cache *g_cache = NULL; +static struct token_perm_node *g_token_perm_root = NULL; +static size_t g_total_node_num = 0; + +int access_tokenid_get_tokenid(struct file *file, void __user *uarg) +{ + return copy_to_user(uarg, ¤t->token, + sizeof(current->token)) ? -EFAULT : 0; +} + +static bool check_permission_for_set_tokenid(struct file *file, unsigned long long tokenid) +{ + kuid_t uid = current_uid(); + struct inode *inode = file->f_inode; + access_tokenid_inner *tokenid_inner = (access_tokenid_inner *)&tokenid; + + if (inode == NULL) { + pr_err("%s: file inode is null\n", __func__); + return false; + } + + if (uid_eq(uid, GLOBAL_ROOT_UID) || + uid_eq(uid, inode->i_uid)) { + return true; + } else if (uid_eq(uid, NWEBSPAWN_UID) && (tokenid_inner->render_flag == 1)) { + return true; + } + + return false; +} + +int access_tokenid_set_tokenid(struct file *file, void __user *uarg) +{ + unsigned long long tmp = 0; + + if (copy_from_user(&tmp, uarg, sizeof(tmp))) + return -EFAULT; + + if (!check_permission_for_set_tokenid(file, tmp)) + return -EPERM; + + current->token = tmp; + return 0; +} + +static bool check_permission_for_ftokenid(struct file *file) +{ + int i; + struct group_info *group_info; + kuid_t uid = current_uid(); + struct inode *inode = file->f_inode; + + if (inode == NULL) { + pr_err("%s: file inode is null\n", __func__); + return false; + } + + if (uid_eq(uid, GLOBAL_ROOT_UID)) + return true; + + group_info = get_current_groups(); + for (i = 0; i < group_info->ngroups; i++) { + kgid_t gid = group_info->gid[i]; + + if (gid_eq(gid, inode->i_gid)) { + put_group_info(group_info); + return true; + } + } + + put_group_info(group_info); + return false; +} + +int access_tokenid_get_ftokenid(struct file *file, void __user *uarg) +{ + if (!check_permission_for_ftokenid(file)) + return -EPERM; + + return copy_to_user(uarg, ¤t->ftoken, + sizeof(current->ftoken)) ? -EFAULT : 0; +} + +int access_tokenid_set_ftokenid(struct file *file, void __user *uarg) +{ + unsigned long long tmp = 0; + + if (!check_permission_for_ftokenid(file)) + return -EPERM; + + if (copy_from_user(&tmp, uarg, sizeof(tmp))) + return -EFAULT; + + current->ftoken = tmp; + return 0; +} + +static bool check_permission_for_set_token_permission(void) +{ + kuid_t uid = current_uid(); + return uid_eq(uid, ACCESS_TOKEN_UID); +} + +static void add_node_to_left_tree_tail(struct token_perm_node *root_node, struct token_perm_node *node) +{ + if ((root_node == NULL) || (node == NULL)) + return; + + struct token_perm_node *current_node = root_node; + while (true) { + if (current_node->left == NULL) { + current_node->left = node; + break; + } + current_node = current_node->left; + } +} + +static void find_node_by_token(struct token_perm_node *root_node, uint32_t token, + struct token_perm_node **target_node, struct token_perm_node **parent_node) +{ + *target_node = NULL; + *parent_node = NULL; + struct token_perm_node *current_node = root_node; + while (current_node != NULL) { + if (current_node->perm_data.token == token) { + *target_node = current_node; + break; + } + *parent_node = current_node; + if (current_node->perm_data.token > token) { + current_node = current_node->left; + } else { + current_node = current_node->right; + } + } +} + +static int add_node_to_tree(struct token_perm_node *root_node, struct token_perm_node *node) +{ + struct token_perm_node *target_node = NULL; + struct token_perm_node *parent_node = NULL; + find_node_by_token(root_node, node->perm_data.token, &target_node, &parent_node); + if (target_node != NULL) { + target_node->perm_data = node->perm_data; + return 0; + } + if (g_total_node_num >= MAX_NODE_NUM) { + pr_err("%s: the number of token nodes is exceeded.\n", __func__); + return -EDQUOT; + } + if (parent_node == NULL) { + g_token_perm_root = node; + } else if (parent_node->perm_data.token > node->perm_data.token) { + parent_node->left = node; + } else { + parent_node->right = node; + } + g_total_node_num++; + return 1; +} + +static struct token_perm_node *remove_node_by_token(struct token_perm_node *root_node, uint32_t token) +{ + struct token_perm_node *target_node = NULL; + struct token_perm_node *parent_node = NULL; + find_node_by_token(root_node, token, &target_node, &parent_node); + if (target_node == NULL) { + pr_err("%s: target token to be removed not found.\n", __func__); + return NULL; + } + + struct token_perm_node **new_node_addr = NULL; + if (parent_node == NULL) { + new_node_addr = &root_node; + } else if (parent_node->perm_data.token > token) { + new_node_addr = &(parent_node->left); + } else { + new_node_addr = &(parent_node->right); + } + if (target_node->right != NULL) { + *new_node_addr = target_node->right; + add_node_to_left_tree_tail(target_node->right, target_node->left); + } else { + *new_node_addr = target_node->left; + } + g_total_node_num--; + return target_node; +} + +int access_tokenid_add_permission(struct file *file, void __user *uarg) +{ + if (!check_permission_for_set_token_permission()) + return -EPERM; + + struct token_perm_node *node = kmem_cache_zalloc(g_cache, GFP_KERNEL); + if (node == NULL) + return -ENOMEM; + if (copy_from_user(&(node->perm_data), uarg, sizeof(ioctl_add_perm_data))) { + kmem_cache_free(g_cache, node); + return -EFAULT; + } + + write_lock(&token_rwlock); + int ret = add_node_to_tree(g_token_perm_root, node); + write_unlock(&token_rwlock); + if (ret <= 0) { + kmem_cache_free(g_cache, node); + return ret; + } + return 0; +} + +int access_tokenid_remove_permission(struct file *file, void __user *uarg) +{ + if (!check_permission_for_set_token_permission()) + return -EPERM; + + uint32_t token = 0; + if (copy_from_user(&token, uarg, sizeof(token))) + return -EFAULT; + + write_lock(&token_rwlock); + struct token_perm_node *target_node = remove_node_by_token(g_token_perm_root, token); + write_unlock(&token_rwlock); + + if (target_node != NULL) + kmem_cache_free(g_cache, target_node); + + return 0; +} + +int access_tokenid_set_permission(struct file *file, void __user *uarg) +{ + if (!check_permission_for_set_token_permission()) + return -EPERM; + + ioctl_set_get_perm_data set_perm_data; + if (copy_from_user(&set_perm_data, uarg, sizeof(set_perm_data))) + return -EFAULT; + + uint32_t idx = set_perm_data.op_code / UINT32_T_BITS; + if (idx >= MAX_PERM_GROUP_NUM) { + pr_err("%s: invalid op_code.\n", __func__); + return -EINVAL; + } + + struct token_perm_node *target_node = NULL; + struct token_perm_node *parent_node = NULL; + write_lock(&token_rwlock); + find_node_by_token(g_token_perm_root, set_perm_data.token, &target_node, &parent_node); + if (target_node == NULL) { + write_unlock(&token_rwlock); + pr_err("%s: token not found.\n", __func__); + return -ENODATA; + } + uint32_t bit_idx = set_perm_data.op_code % UINT32_T_BITS; + if (set_perm_data.is_granted) { + target_node->perm_data.perm[idx] |= (uint32_t)0x01 << bit_idx; + } else { + target_node->perm_data.perm[idx] &= ~((uint32_t)0x01 << bit_idx); + } + write_unlock(&token_rwlock); + return 0; +} + +int access_tokenid_get_permission(struct file *file, void __user *uarg) +{ + ioctl_set_get_perm_data get_perm_data; + if (copy_from_user(&get_perm_data, uarg, sizeof(get_perm_data))) + return -EFAULT; + + uint32_t idx = get_perm_data.op_code / UINT32_T_BITS; + if (idx >= MAX_PERM_GROUP_NUM) { + pr_err("%s: invalid op_code.\n", __func__); + return -EINVAL; + } + + struct token_perm_node *target_node = NULL; + struct token_perm_node *parent_node = NULL; + read_lock(&token_rwlock); + find_node_by_token(g_token_perm_root, get_perm_data.token, &target_node, &parent_node); + read_unlock(&token_rwlock); + if (target_node == NULL) + return -ENODATA; + + uint32_t bit_idx = get_perm_data.op_code % UINT32_T_BITS; + return (target_node->perm_data.perm[idx] & ((uint32_t)0x01 << bit_idx)) >> bit_idx; +} + +typedef int (*access_token_id_func)(struct file *file, void __user *arg); + +static access_token_id_func g_func_array[ACCESS_TOKENID_MAX_NR] = { + NULL, /* reserved */ + access_tokenid_get_tokenid, + access_tokenid_set_tokenid, + access_tokenid_get_ftokenid, + access_tokenid_set_ftokenid, + access_tokenid_add_permission, + access_tokenid_remove_permission, + access_tokenid_get_permission, + access_tokenid_set_permission, +}; + +static long access_tokenid_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *uarg = (void __user *)arg; + unsigned int func_cmd = _IOC_NR(cmd); + + if (uarg == NULL) { + pr_err("%s: invalid user uarg\n", __func__); + return -EINVAL; + } + + if (_IOC_TYPE(cmd) != ACCESS_TOKEN_ID_IOCTL_BASE) { + pr_err("%s: access tokenid magic fail, TYPE=%d\n", + __func__, _IOC_TYPE(cmd)); + return -EINVAL; + } + + if (func_cmd >= ACCESS_TOKENID_MAX_NR) { + pr_err("%s: access tokenid cmd error, cmd:%d\n", + __func__, func_cmd); + return -EINVAL; + } + + if (g_func_array[func_cmd]) + return (*g_func_array[func_cmd])(file, uarg); + + return -EINVAL; +} + +static const struct file_operations access_tokenid_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = access_tokenid_ioctl, + .compat_ioctl = access_tokenid_ioctl, +}; + +static struct miscdevice access_tokenid_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "access_token_id", + .fops = &access_tokenid_fops, +}; + +static int access_tokenid_init_module(void) +{ + int err; + + err = misc_register(&access_tokenid_device); + if (err < 0) { + pr_err("access_tokenid register failed\n"); + return err; + } + + g_cache = kmem_cache_create("access_token_node", sizeof(struct token_perm_node), 0, SLAB_HWCACHE_ALIGN, NULL); + if (g_cache == NULL) { + pr_err("access_tokenid kmem_cache create failed\n"); + return -ENOMEM; + } + pr_info("access_tokenid init success\n"); + return 0; +} + +static void access_tokenid_exit_module(void) +{ + kmem_cache_destroy(g_cache); + misc_deregister(&access_tokenid_device); +} + +/* module entry points */ +module_init(access_tokenid_init_module); +module_exit(access_tokenid_exit_module); diff --git a/drivers/accesstokenid/access_tokenid.h b/drivers/accesstokenid/access_tokenid.h new file mode 100644 index 0000000000000000000000000000000000000000..7eb3119efbf4a72b50fe466bdff379a52cebd5c9 --- /dev/null +++ b/drivers/accesstokenid/access_tokenid.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * access_tokenid.h + * + * Copyright (C) 2022-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + */ + +#ifndef _ACCESS_TOKEN_ID_H +#define _ACCESS_TOKEN_ID_H + +#include +#include + +#define ACCESS_TOKEN_ID_IOCTL_BASE 'A' +#define MAX_PERM_GROUP_NUM 64 + +enum { + GET_TOKEN_ID = 1, + SET_TOKEN_ID, + GET_FTOKEN_ID, + SET_FTOKEN_ID, + ADD_PERMISSIONS, + REMOVE_PERMISSIONS, + GET_PERMISSION, + SET_PERMISSION, + ACCESS_TOKENID_MAX_NR +}; + +typedef struct { + unsigned int token_uniqueid : 20; + unsigned int res : 5; + unsigned int render_flag : 1; + unsigned int dlp_flag : 1; + unsigned int type : 2; + unsigned int version : 3; +} access_tokenid_inner; + +typedef struct { + uint32_t token; + uint32_t op_code; + bool is_granted; +} ioctl_set_get_perm_data; + +typedef struct { + uint32_t token; + uint32_t perm[MAX_PERM_GROUP_NUM]; +} ioctl_add_perm_data; + +struct token_perm_node { + ioctl_add_perm_data perm_data; + struct token_perm_node *left; + struct token_perm_node *right; +}; + +#define ACCESS_TOKENID_GET_TOKENID \ + _IOR(ACCESS_TOKEN_ID_IOCTL_BASE, GET_TOKEN_ID, unsigned long long) +#define ACCESS_TOKENID_SET_TOKENID \ + _IOW(ACCESS_TOKEN_ID_IOCTL_BASE, SET_TOKEN_ID, unsigned long long) +#define ACCESS_TOKENID_GET_FTOKENID \ + _IOR(ACCESS_TOKEN_ID_IOCTL_BASE, GET_FTOKEN_ID, unsigned long long) +#define ACCESS_TOKENID_SET_FTOKENID \ + _IOW(ACCESS_TOKEN_ID_IOCTL_BASE, SET_FTOKEN_ID, unsigned long long) +#define ACCESS_TOKENID_ADD_PERMISSIONS \ + _IOW(ACCESS_TOKEN_ID_IOCTL_BASE, ADD_PERMISSIONS, ioctl_add_perm_data) +#define ACCESS_TOKENID_REMOVE_PERMISSIONS \ + _IOW(ACCESS_TOKEN_ID_IOCTL_BASE, REMOVE_PERMISSIONS, uint32_t) +#define ACCESS_TOKENID_GET_PERMISSION \ + _IOW(ACCESS_TOKEN_ID_IOCTL_BASE, GET_PERMISSION, ioctl_set_get_perm_data) +#define ACCESS_TOKENID_SET_PERMISSION \ + _IOW(ACCESS_TOKEN_ID_IOCTL_BASE, SET_PERMISSION, ioctl_set_get_perm_data) + +#endif /* _ACCESS_TOKEN_ID_H */ diff --git a/fs/proc/base.c b/fs/proc/base.c index ffd54617c35478e92a9f6bef67013e16e6cd3183..2b318f2161f04dc9776ed0e8b848c965dd281c2e 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3232,6 +3232,15 @@ static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns, } #endif /* CONFIG_STACKLEAK_METRICS */ +#ifdef CONFIG_ACCESS_TOKENID +static int proc_token_operations(struct seq_file *m, struct pid_namespace *ns, + struct pid *pid, struct task_struct *task) +{ + seq_printf(m, "%#llx %#llx\n", task->token, task->ftoken); + return 0; +} +#endif /* CONFIG_ACCESS_TOKENID */ + /* * Thread groups */ @@ -3345,6 +3354,9 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_PROC_PID_ARCH_STATUS ONE("arch_status", S_IRUGO, proc_pid_arch_status), #endif +#ifdef CONFIG_ACCESS_TOKENID + ONE("tokenid", S_IRUSR, proc_token_operations), +#endif #ifdef CONFIG_SECCOMP_CACHE_DEBUG ONE("seccomp_cache", S_IRUSR, proc_pid_seccomp_cache), #endif @@ -3684,6 +3696,9 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_PROC_PID_ARCH_STATUS ONE("arch_status", S_IRUGO, proc_pid_arch_status), #endif +#ifdef CONFIG_ACCESS_TOKENID + ONE("tokenid", S_IRUSR, proc_token_operations), +#endif #ifdef CONFIG_SECCOMP_CACHE_DEBUG ONE("seccomp_cache", S_IRUSR, proc_pid_seccomp_cache), #endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 77f01ac385f7a57695c2e9af5b826cb4f9b39f6a..d611b40fdef61db72ed55f5da05608e30166a0a5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1536,6 +1536,11 @@ struct task_struct { struct user_event_mm *user_event_mm; #endif +#ifdef CONFIG_ACCESS_TOKENID + u64 token; + u64 ftoken; +#endif + /* * New fields for task_struct should be added above here, so that * they are included in the randomized portion of task_struct. diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h index b0542cd11aeb09f5d0c979e1acf31ad8d550bb6d..c7ca50d59ad30fedfdd185590d6f0eddddc8effd 100644 --- a/include/linux/uidgid.h +++ b/include/linux/uidgid.h @@ -55,6 +55,10 @@ static inline gid_t __kgid_val(kgid_t gid) #define GLOBAL_ROOT_UID KUIDT_INIT(0) #define GLOBAL_ROOT_GID KGIDT_INIT(0) +#ifdef CONFIG_ACCESS_TOKENID +#define NWEBSPAWN_UID KUIDT_INIT(3081) +#endif + #define INVALID_UID KUIDT_INIT(-1) #define INVALID_GID KGIDT_INIT(-1) diff --git a/kernel/fork.c b/kernel/fork.c index 177ce7438db6b1ca4f309351c7364e15ca4e6ab6..486248a10b8b75497afdfe9cb3caca30450d3d7a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1115,6 +1115,11 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) if (err) goto free_tsk; +#ifdef CONFIG_ACCESS_TOKENID + tsk->token = orig->token; + tsk->ftoken = 0; +#endif + err = alloc_thread_stack_node(tsk, node); if (err) goto free_tsk;