// SPDX-License-Identifier: GPL-2.0 /* * drivers/auth_ctl/auth_ctrl.c * * Copyright (c) 2022 Huawei Device Co., Ltd. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "auth_ctrl.h" #ifdef CONFIG_QOS_CTRL #include "qos_ctrl.h" #endif typedef long (*auth_ctrl_func)(int abi, void __user *arg); static long ctrl_auth_basic_operation(int abi, void __user *uarg); static auth_ctrl_func g_func_array[AUTH_CTRL_MAX_NR] = { NULL, /* reserved */ ctrl_auth_basic_operation, }; /* * uid-based authority idr table */ static struct idr *ua_idr; struct idr *get_auth_ctrl_idr(void) { return ua_idr; } static DEFINE_MUTEX(ua_idr_mutex); struct mutex *get_auth_idr_mutex(void) { return &ua_idr_mutex; } /* * change auth's status to SYSTEM and enable all feature access */ static void change_to_super(struct auth_struct *auth) { #ifdef CONFIG_RTG_AUTHORITY auth->rtg_auth_flag = AF_RTG_ALL; #endif #ifdef CONFIG_QOS_AUTHORITY auth->qos_auth_flag = AF_QOS_ALL; #endif auth->status = AUTH_STATUS_SYSTEM_SERVER; } static void init_authority_record(struct auth_struct *auth) { #ifdef CONFIG_QOS_AUTHORITY int i; #endif #ifdef CONFIG_RTG_AUTHORITY auth->rtg_auth_flag = 0; #endif #ifdef CONFIG_QOS_AUTHORITY auth->qos_auth_flag = 0; #endif auth->status = AUTH_STATUS_DISABLED; mutex_init(&auth->mutex); refcount_set(&auth->usage, 1); #ifdef CONFIG_QOS_CTRL for (i = QOS_POLICY_MIN_LEVEL; i < NR_QOS; ++i) { INIT_LIST_HEAD(&auth->tasks[i]); auth->num[i] = 0; } #endif } void get_auth_struct(struct auth_struct *auth) { refcount_inc(&auth->usage); } static void __put_auth_struct(struct auth_struct *auth) { WARN_ON(auth->status != AUTH_STATUS_DEAD); WARN_ON(refcount_read(&auth->usage)); #ifdef CONFIG_QOS_CTRL /* refcount is zero here, no contend, no lock. */ remove_qos_tasks(auth); #endif kfree(auth); } void put_auth_struct(struct auth_struct *auth) { if (refcount_dec_and_test(&auth->usage)) __put_auth_struct(auth); } static int init_ua_idr(void) { ua_idr = kzalloc(sizeof(*ua_idr), GFP_ATOMIC); if (ua_idr == NULL) { pr_err("[AUTH_CTRL] auth idr init failed, no memory!\n"); return -ENOMEM; } idr_init(ua_idr); return 0; } static int init_super_authority(unsigned int auth_tgid) { int ret; struct auth_struct *auth_super; auth_super = kzalloc(sizeof(*auth_super), GFP_ATOMIC); if(auth_super == NULL) { pr_err("[AUTH_CTRL] auth struct alloc failed\n"); return -ENOMEM; } init_authority_record(auth_super); change_to_super(auth_super); ret = idr_alloc(ua_idr, auth_super, auth_tgid, auth_tgid + 1, GFP_ATOMIC); if(ret != auth_tgid) { pr_err("[AUTH_CTRL] authority for super init failed! ret=%d\n", ret); kfree(auth_super); return ret; } return 0; } int authority_remove_handler(int id, void *p, void *para) { struct auth_struct *auth = (struct auth_struct *)p; mutex_lock(&auth->mutex); #ifdef CONFIG_QOS_CTRL qos_switch(auth, AUTH_STATUS_DISABLED); #endif auth->status = AUTH_STATUS_DEAD; mutex_unlock(&auth->mutex); put_auth_struct(auth); return 0; } void remove_authority_control(void) { int ret; mutex_lock(&ua_idr_mutex); ret = idr_for_each(ua_idr, authority_remove_handler, NULL); if (ret < 0) pr_err("[AUTH_CTRL] authority item remove failed\n"); idr_destroy(ua_idr); kfree(ua_idr); mutex_unlock(&ua_idr_mutex); } /* * constrain user assigned auth_flag to kernel accepted auth_flag */ static int generic_auth_trim(unsigned int orig_flag, unsigned int constrain) { return orig_flag & constrain; } static inline void set_auth_flag(struct auth_ctrl_data *data, struct auth_struct *auth_to_enable) { #ifdef CONFIG_RTG_AUTHORITY auth_to_enable->rtg_auth_flag = generic_auth_trim(data->rtg_ua_flag, AF_RTG_DELEGATED); #endif #ifdef CONFIG_QOS_AUTHORITY auth_to_enable->qos_auth_flag = generic_auth_trim(data->qos_ua_flag, AF_QOS_ALL); #endif } static int auth_enable(struct auth_ctrl_data *data) { struct auth_struct *auth_to_enable; unsigned int tgid = data->pid; int status = data->status; int ret; mutex_lock(&ua_idr_mutex); auth_to_enable = idr_find(ua_idr, tgid); /* auth exist, just resume the task's qos request */ if (auth_to_enable) { get_auth_struct(auth_to_enable); mutex_unlock(&ua_idr_mutex); mutex_lock(&auth_to_enable->mutex); if (auth_to_enable->status == AUTH_STATUS_DEAD) { mutex_unlock(&auth_to_enable->mutex); put_auth_struct(auth_to_enable); return -INVALID_AUTH; } set_auth_flag(data, auth_to_enable); #ifdef CONFIG_QOS_CTRL qos_switch(auth_to_enable, status); #endif auth_to_enable->status = status; mutex_unlock(&auth_to_enable->mutex); ret = 0; put_auth_struct(auth_to_enable); goto out; } /* auth not exist, build a new auth, then insert to idr */ auth_to_enable = kzalloc(sizeof(*auth_to_enable), GFP_ATOMIC); if (!auth_to_enable) { mutex_unlock(&ua_idr_mutex); pr_err("[AUTH_CTRL] alloc auth data failed, no memory!\n"); ret = -ENOMEM; goto out; } init_authority_record(auth_to_enable); /* no one could get the auth from idr now, no need to lock */ set_auth_flag(data, auth_to_enable); auth_to_enable->status = status; ret = idr_alloc(ua_idr, auth_to_enable, tgid, tgid + 1, GFP_ATOMIC); if (ret < 0) { pr_err("[AUTH_CTRL] add auth to idr failed, no memory!\n"); kfree(auth_to_enable); } mutex_unlock(&ua_idr_mutex); out: return ret; } static int auth_delete(struct auth_ctrl_data *data) { struct auth_struct *auth_to_delete; unsigned int tgid = data->pid; mutex_lock(&ua_idr_mutex); auth_to_delete = (struct auth_struct *)idr_remove(ua_idr, tgid); if (!auth_to_delete) { mutex_unlock(&ua_idr_mutex); pr_err("[AUTH_CTRL] no auth data for this pid=%d, delete failed\n", tgid); return -PID_NOT_FOUND; } mutex_unlock(&ua_idr_mutex); mutex_lock(&auth_to_delete->mutex); #ifdef CONFIG_QOS_CTRL qos_switch(auth_to_delete, AUTH_STATUS_DISABLED); #endif auth_to_delete->status = AUTH_STATUS_DEAD; mutex_unlock(&auth_to_delete->mutex); put_auth_struct(auth_to_delete); return 0; } static int auth_get(struct auth_ctrl_data *data) { struct auth_struct *auth_to_get; unsigned int tgid = data->pid; mutex_lock(&ua_idr_mutex); auth_to_get = idr_find(ua_idr, tgid); if (!auth_to_get) { mutex_unlock(&ua_idr_mutex); pr_err("[AUTH_CTRL] no auth data for this pid=%d to get\n", tgid); return -PID_NOT_FOUND; } get_auth_struct(auth_to_get); mutex_unlock(&ua_idr_mutex); mutex_lock(&auth_to_get->mutex); if (auth_to_get->status == AUTH_STATUS_DEAD) { mutex_unlock(&auth_to_get->mutex); put_auth_struct(auth_to_get); return -INVALID_AUTH; } #ifdef CONFIG_RTG_AUTHORITY data->rtg_ua_flag = auth_to_get->rtg_auth_flag; #endif #ifdef CONFIG_QOS_AUTHORITY data->qos_ua_flag = auth_to_get->qos_auth_flag; #endif data->status = auth_to_get->status; mutex_unlock(&auth_to_get->mutex); put_auth_struct(auth_to_get); return 0; } static int auth_switch(struct auth_ctrl_data *data) { struct auth_struct *auth; unsigned int tgid = data->pid; unsigned int status = data->status; if (status == 0 || status >= AUTH_STATUS_MAX_NR) { pr_err("[AUTH_CTRL] not valied status %d\n", status); return -ARG_INVALID; } mutex_lock(&ua_idr_mutex); auth = idr_find(ua_idr, tgid); if (!auth) { mutex_unlock(&ua_idr_mutex); pr_err("[AUTH_CTRL] no auth data for this pid=%d to switch\n", tgid); return -PID_NOT_FOUND; } get_auth_struct(auth); mutex_unlock(&ua_idr_mutex); mutex_lock(&auth->mutex); if (auth->status == AUTH_STATUS_DEAD) { mutex_unlock(&auth->mutex); put_auth_struct(auth); return -INVALID_AUTH; } set_auth_flag(data, auth); #ifdef CONFIG_QOS_CTRL qos_switch(auth, status); #endif auth->status = status; mutex_unlock(&auth->mutex); put_auth_struct(auth); return 0; } typedef int (*auth_manipulate_func)(struct auth_ctrl_data *data); static auth_manipulate_func auth_func_array[AUTH_MAX_NR] = { /* * auth_enable: Start authority control for specific tgid. * auth_delte: End authroity control, remove statistic datas. * auth_get: Get auth info, deprecated. * auth_switch: Change authority flag and status for specific tgid. */ NULL, auth_enable, auth_delete, auth_get, auth_switch, }; static long do_auth_manipulate(struct auth_ctrl_data *data) { long ret = 0; unsigned int type = data->type; if (type >= AUTH_MAX_NR) { pr_err("[AUTH_CTRL] BASIC_AUTH_CTRL_OPERATION type not valid\n"); return -ARG_INVALID; } if (auth_func_array[type]) ret = (long)(*auth_func_array[type])(data); return ret; } static long ctrl_auth_basic_operation(int abi, void __user *uarg) { struct auth_ctrl_data auth_data; long ret = -1; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpointer-to-int-cast" switch (abi) { case AUTH_IOCTL_ABI_ARM32: ret = copy_from_user(&auth_data, (void __user *)compat_ptr((compat_uptr_t)uarg), sizeof(struct auth_ctrl_data)); break; case AUTH_IOCTL_ABI_AARCH64: ret = copy_from_user(&auth_data, uarg, sizeof(struct auth_ctrl_data)); break; default: pr_err("[AUTH_CTRL] abi format error\n"); break; } #pragma GCC diagnostic pop if (ret) { pr_err("[AUTH_RTG] %s copy user data failed\n", __func__); return ret; } ret = do_auth_manipulate(&auth_data); if (ret < 0) { pr_err("[AUTH_CTRL] BASIC_AUTH_CTRL_OPERATION failed\n"); return ret; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpointer-to-int-cast" switch (abi) { case AUTH_IOCTL_ABI_ARM32: ret = copy_to_user((void __user *)compat_ptr((compat_uptr_t)uarg), &auth_data, sizeof(struct auth_ctrl_data)); break; case AUTH_IOCTL_ABI_AARCH64: ret = copy_to_user(uarg, &auth_data, sizeof(struct auth_ctrl_data)); break; default: pr_err("[AUTH_CTRL] abi format error\n"); break; } #pragma GCC diagnostic pop if (ret) { pr_err("[AUTH_RTG] %s copy user data failed\n", __func__); return ret; } return 0; } long do_auth_ctrl_ioctl(int abi, 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) != AUTH_CTRL_IPC_MAGIG) { pr_err("%s: authority ctrl magic fail, TYPE=%d\n", __func__, _IOC_TYPE(cmd)); return -EINVAL; } if (func_cmd >= AUTH_CTRL_MAX_NR) { pr_err("%s: authority ctrl cmd error, cmd:%d\n", __func__, _IOC_TYPE(cmd)); return -EINVAL; } if (g_func_array[func_cmd]) return (*g_func_array[func_cmd])(abi, uarg); return -EINVAL; } #define get_authority_flag(func_id) (1 << (func_id - 1)) static inline unsigned int get_true_uid(struct task_struct *p) { if (!p) return get_uid(current_user())->uid.val; return task_uid(p).val; } /* * Return 1000 for both SYSTEM and ROOT * Return current's uid if p is NULL */ static inline unsigned int get_authority_uid(struct task_struct *p) { unsigned int uid = get_true_uid(p); if (super_uid(uid)) uid = SUPER_UID; return uid; } static unsigned int auth_flag(struct auth_struct *auth, unsigned int type) { switch (type) { #ifdef CONFIG_RTG_AUTHORITY case RTG_AUTH_FLAG: return auth->rtg_auth_flag; #endif #ifdef CONFIG_QOS_AUTHORITY case QOS_AUTH_FLAG: return auth->qos_auth_flag; #endif default: pr_err("[AUTH_CTRL] not valid auth type\n"); return INVALIED_AUTH_FLAG; } } bool check_authorized(unsigned int func_id, unsigned int type) { bool authorized = false; struct auth_struct *auth; unsigned int af = get_authority_flag(func_id); unsigned int uid = get_authority_uid(NULL); unsigned int tgid = task_tgid_nr(current); mutex_lock(&ua_idr_mutex); if (!ua_idr) { mutex_unlock(&ua_idr_mutex); pr_err("[AUTH_CTRL] authority idr table missed, auth failed\n"); return authorized; } auth = (struct auth_struct *)idr_find(ua_idr, tgid); if (!auth) { if (uid != SUPER_UID) { mutex_unlock(&ua_idr_mutex); pr_err("[AUTH_CTRL] no auth data for this pid = %d\n, tgid"); return authorized; } else if (init_super_authority(tgid)) { mutex_unlock(&ua_idr_mutex); pr_err("[AUTH_CTRL] init super authority failed\n"); return authorized; } //the auth must exist auth = (struct auth_struct *)idr_find(ua_idr, tgid); if (!auth) return authorized; } get_auth_struct(auth); mutex_unlock(&ua_idr_mutex); mutex_lock(&auth->mutex); if (auth->status == AUTH_STATUS_DEAD) { mutex_unlock(&auth->mutex); pr_info("[AUTH_CTRL] not valid auth for pid %d\n", tgid); put_auth_struct(auth); return authorized; } if (auth && (auth_flag(auth, type) & af)) authorized = true; mutex_unlock(&auth->mutex); put_auth_struct(auth); return authorized; } /* * Return authority info for given task * return current's auth if p is NULL * refcount will inc if this call return the valid auth * make sure to call put_auth_struct before the calling end */ struct auth_struct *get_authority(struct task_struct *p) { unsigned int tgid; struct auth_struct *auth; tgid = (p == NULL ? current->tgid : p->tgid); mutex_lock(&ua_idr_mutex); auth = idr_find(ua_idr, tgid); if (auth) get_auth_struct(auth); mutex_unlock(&ua_idr_mutex); return auth; } long proc_auth_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return do_auth_ctrl_ioctl(AUTH_IOCTL_ABI_AARCH64, file, cmd, arg); } #ifdef CONFIG_COMPAT long proc_auth_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return do_auth_ctrl_ioctl(AUTH_IOCTL_ABI_ARM32, file, cmd, (unsigned long)(compat_ptr((compat_uptr_t)arg))); } #endif static const struct file_operations auth_ctrl_fops = { .owner = THIS_MODULE, .unlocked_ioctl = proc_auth_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = proc_auth_compat_ioctl, #endif }; static struct miscdevice auth_ctrl_device = { .minor = MISC_DYNAMIC_MINOR, .name = "auth_ctrl", .fops = &auth_ctrl_fops, }; static __init int auth_ctrl_init_module(void) { int err; err = misc_register(&auth_ctrl_device); if (err < 0) { pr_err("auth_ctrl register failed\n"); return err; } pr_info("auth_ctrl init success\n"); BUG_ON(init_ua_idr()); #ifdef CONFIG_QOS_CTRL init_qos_ctrl(); #endif init_sched_auth_debug_procfs(); return 0; } static void auth_ctrl_exit_module(void) { remove_authority_control(); misc_deregister(&auth_ctrl_device); } /* module entry points */ module_init(auth_ctrl_init_module); module_exit(auth_ctrl_exit_module); MODULE_LICENSE("GPL v2");