tegrakernel/kernel/nvidia/drivers/misc/tegra-profiler/auth.c

349 lines
7.7 KiB
C

/*
* 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 <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#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);
}