/* * drivers/misc/tegra-profiler/auth.c * * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include "auth.h" #include "quadd.h" #include "debug.h" #define QUADD_SECURITY_MAGIC_REQUEST 0x11112222 #define QUADD_SECURITY_MAGIC_RESPONSE 0x33334444 #define QUADD_TIMEOUT 1000 /* msec */ enum { QUADD_SECURITY_RESPONSE_ERROR = 0, QUADD_SECURITY_RESPONSE_DEBUG_FLAG_ON = 1, QUADD_SECURITY_RESPONSE_DEBUG_FLAG_OFF = 2, QUADD_SECURITY_RESPONSE_PACKAGE_NOT_FOUND = 3, }; enum { QUADD_SECURITY_REQUEST_CMD_TEST_DEBUG_FLAG = 1, QUADD_SECURITY_RESPONSE_CMD_TEST_DEBUG_FLAG = 2, }; struct quadd_auth_data { char package_name[QUADD_MAX_PACKAGE_NAME]; uid_t debug_app_uid; int response_value; }; static struct quadd_auth_context { struct miscdevice misc_dev; atomic_t opened; wait_queue_head_t request_wait; wait_queue_head_t response_wait; int request_ready; int response_ready; struct quadd_auth_data data; struct mutex lock; unsigned int msg_id; struct quadd_ctx *quadd_ctx; } auth_ctx; static inline void response_ready(void) { auth_ctx.response_ready = 1; wake_up_interruptible(&auth_ctx.response_wait); } static inline void request_ready(void) { auth_ctx.request_ready = 1; wake_up_interruptible(&auth_ctx.request_wait); } static int auth_open(struct inode *inode, struct file *file) { struct quadd_auth_data *data = &auth_ctx.data; if (atomic_cmpxchg(&auth_ctx.opened, 0, 1)) { pr_err("Error: auth file is already opened\n"); return -EBUSY; } pr_info("auth is opened\n"); auth_ctx.request_ready = 0; auth_ctx.response_ready = 0; mutex_lock(&auth_ctx.lock); data->package_name[0] = '\0'; data->debug_app_uid = 0; data->response_value = 0; mutex_unlock(&auth_ctx.lock); return 0; } static int auth_release(struct inode *inode, struct file *file) { pr_info("auth is released\n"); atomic_set(&auth_ctx.opened, 0); return 0; } static ssize_t auth_read(struct file *filp, char __user *user_buf, size_t length, loff_t *offset) { char buf[QUADD_MAX_PACKAGE_NAME + 4 * sizeof(u32)]; int msg_length, err; struct quadd_auth_data *data = &auth_ctx.data; wait_event_interruptible(auth_ctx.request_wait, auth_ctx.request_ready); mutex_lock(&auth_ctx.lock); ((u32 *)buf)[0] = QUADD_SECURITY_MAGIC_REQUEST; ((u32 *)buf)[1] = ++auth_ctx.msg_id; ((u32 *)buf)[2] = QUADD_SECURITY_REQUEST_CMD_TEST_DEBUG_FLAG; ((u32 *)buf)[3] = strlen(data->package_name); strlcpy(buf + 4 * sizeof(u32), data->package_name, QUADD_MAX_PACKAGE_NAME); msg_length = strlen(data->package_name) + 4 * sizeof(u32); mutex_unlock(&auth_ctx.lock); err = copy_to_user(user_buf, buf, msg_length); if (err != 0) { pr_err("Error: copy to user: %d\n", err); return err; } pr_info("auth read, msg_length: %d\n", msg_length); return msg_length; } static ssize_t auth_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { int err; char buf[5 * sizeof(u32)]; u32 magic, response_cmd, response_value, length, uid, msg_id; struct quadd_auth_data *data = &auth_ctx.data; pr_info("auth read, count: %zu\n", count); mutex_lock(&auth_ctx.lock); data->response_value = QUADD_SECURITY_RESPONSE_ERROR; data->debug_app_uid = 0; mutex_unlock(&auth_ctx.lock); if (count < 5 * sizeof(u32)) { pr_err("Error count: %zu\n", count); response_ready(); return -E2BIG; } err = copy_from_user(buf, user_buf, 5 * sizeof(u32)); if (err) { pr_err("Error: copy from user: %d\n", err); response_ready(); return err; } magic = ((u32 *)buf)[0]; if (magic != QUADD_SECURITY_MAGIC_RESPONSE) { pr_err("Error magic: %#x\n", magic); response_ready(); return -EINVAL; } msg_id = ((u32 *)buf)[1]; if (msg_id != auth_ctx.msg_id) { pr_err("Error message id: %u\n", msg_id); response_ready(); return -EINVAL; } response_cmd = ((u32 *)buf)[2]; response_value = ((u32 *)buf)[3]; length = ((u32 *)buf)[4]; switch (response_cmd) { case QUADD_SECURITY_RESPONSE_CMD_TEST_DEBUG_FLAG: if (length != 4) { pr_err("Error: too long data: %u\n", length); response_ready(); return -E2BIG; } err = get_user(uid, (u32 __user *)user_buf + 5); if (err) { pr_err("Error: copy from user: %d\n", err); response_ready(); return err; } mutex_lock(&auth_ctx.lock); data->response_value = response_value; data->debug_app_uid = uid; mutex_unlock(&auth_ctx.lock); pr_info("uid: %u, response_value: %u\n", uid, response_value); break; default: pr_err("Error: invalid response command: %u\n", response_cmd); response_ready(); return -EINVAL; } response_ready(); return count; } static const struct file_operations auth_fops = { .read = auth_read, .write = auth_write, .open = auth_open, .release = auth_release, }; int quadd_auth_is_debuggable(const char *package_name, uid_t *uid) { uid_t uid_value; int response_value; struct quadd_auth_data *data = &auth_ctx.data; int pkg_name_length; if (!package_name) return -EINVAL; pkg_name_length = strlen(package_name); if (pkg_name_length == 0 || pkg_name_length > QUADD_MAX_PACKAGE_NAME) return -EINVAL; if (atomic_read(&auth_ctx.opened) == 0) return -EIO; mutex_lock(&auth_ctx.lock); data->debug_app_uid = 0; data->response_value = 0; strlcpy(data->package_name, package_name, QUADD_MAX_PACKAGE_NAME); mutex_unlock(&auth_ctx.lock); request_ready(); wait_event_interruptible_timeout(auth_ctx.response_wait, auth_ctx.response_ready, msecs_to_jiffies(QUADD_TIMEOUT)); if (!auth_ctx.response_ready) { pr_err("Error: Tegra profiler service did not answer\n"); return -ETIMEDOUT; } mutex_lock(&auth_ctx.lock); uid_value = data->debug_app_uid; response_value = data->response_value; mutex_unlock(&auth_ctx.lock); switch (response_value) { case QUADD_SECURITY_RESPONSE_DEBUG_FLAG_ON: if (uid_value > 0) { pr_info("package \"%s\" is debuggable, uid: %u\n", package_name, (unsigned int)uid_value); *uid = uid_value; return 0; } else { return -EACCES; } case QUADD_SECURITY_RESPONSE_DEBUG_FLAG_OFF: pr_info("package \"%s\" is not debuggable\n", package_name); return -EACCES; case QUADD_SECURITY_RESPONSE_PACKAGE_NOT_FOUND: pr_err("Error: package \"%s\" not found\n", package_name); return -ESRCH; case QUADD_SECURITY_RESPONSE_ERROR: default: pr_err("Error: invalid response\n"); return -EBADMSG; } } int quadd_auth_is_auth_open(void) { return atomic_read(&auth_ctx.opened) != 0; } int quadd_auth_init(struct quadd_ctx *quadd_ctx) { int err; struct miscdevice *misc_dev = &auth_ctx.misc_dev; pr_info("auth: init\n"); misc_dev->minor = MISC_DYNAMIC_MINOR; misc_dev->name = QUADD_AUTH_DEVICE_NAME; misc_dev->fops = &auth_fops; err = misc_register(misc_dev); if (err < 0) { pr_err("Error: misc_register %d\n", err); return err; } init_waitqueue_head(&auth_ctx.request_wait); init_waitqueue_head(&auth_ctx.response_wait); auth_ctx.request_ready = 0; auth_ctx.response_ready = 0; atomic_set(&auth_ctx.opened, 0); mutex_init(&auth_ctx.lock); auth_ctx.msg_id = 0; auth_ctx.quadd_ctx = quadd_ctx; return 0; } void quadd_auth_deinit(void) { struct miscdevice *misc_dev = &auth_ctx.misc_dev; pr_info("auth: deinit\n"); misc_deregister(misc_dev); }