3d157be8创建于 2023年12月27日历史提交
/*
 * auth_base_impl.c
 *
 * function for base hash operation
 *
 * Copyright (C) 2022 Huawei Technologies Co., Ltd.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that 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.
 */
#include "auth_base_impl.h"
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/rwsem.h>
#include <linux/path.h>
#include <linux/file.h>
#include <linux/fs.h>

#include <linux/mm.h>
#include <linux/dcache.h>
#include <linux/mm_types.h>
#include <linux/highmem.h>
#include <linux/cred.h>
#include <linux/slab.h>
#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE)
#include <linux/sched/mm.h>
#endif
#if defined (CONFIG_SELINUX_AUTH_ENABLE) && defined (CONFIG_SECURITY_SELINUX)
#include <linux/security.h>
#endif
#include <securec.h>
#include "tc_ns_log.h"
#include "tc_ns_client.h"
#include "agent.h" /* for get_proc_dpath */
#include "ko_adapt.h"

/* for crypto */
struct crypto_shash *g_shash_handle;
bool g_shash_handle_state = false;
struct mutex g_shash_handle_lock;

void init_crypto_hash_lock(void)
{
	mutex_init(&g_shash_handle_lock);
}

void mutex_crypto_hash_lock(void)
{
	mutex_lock(&g_shash_handle_lock);
}

void mutex_crypto_hash_unlock(void)
{
	mutex_unlock(&g_shash_handle_lock);
}

/* begin: prepare crypto context */
struct crypto_shash *get_shash_handle(void)
{
	return g_shash_handle;
}

void free_shash_handle(void)
{
	if (g_shash_handle) {
		crypto_free_shash(g_shash_handle);
		g_shash_handle_state = false;
		g_shash_handle = NULL;
	}
}

int tee_init_shash_handle(char *hash_type)
{
	long rc;

	if (!hash_type) {
		tloge("tee init crypto: error input parameter\n");
		return -EFAULT;
	}

	mutex_crypto_hash_lock();
	if (g_shash_handle_state) {
		mutex_crypto_hash_unlock();
		return 0;
	}

	g_shash_handle = crypto_alloc_shash(hash_type, 0, 0);
	if (IS_ERR_OR_NULL(g_shash_handle)) {
		rc = PTR_ERR(g_shash_handle);
		tloge("Can not allocate %s reason: %ld\n", hash_type, rc);
		mutex_crypto_hash_unlock();
		return rc;
	}
	g_shash_handle_state = true;

	mutex_crypto_hash_unlock();
	return 0;
}
/* end: prepare crypto context */

/* begin: Calculate the SHA256 file digest */
static int prepare_desc(struct sdesc **desc)
{
	size_t size;
	size_t shash_size;

	shash_size = crypto_shash_descsize(g_shash_handle);
	size = sizeof((*desc)->shash) + shash_size;
	if (size < sizeof((*desc)->shash) || size < shash_size) {
		tloge("size flow\n");
		return -ENOMEM;
	}

	*desc = kzalloc(size, GFP_KERNEL);
	if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)(*desc))) {
		tloge("alloc desc failed\n");
		return -ENOMEM;
	}

	return EOK;
}

#define PINED_PAGE_NUMBER 1
static int get_proc_user_pages(struct mm_struct *mm, unsigned long start_code,
	struct page **ptr_page, struct task_struct *cur_struct)
{
#if (KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE)
	(void)cur_struct;
	return get_user_pages_remote(mm, start_code,
		(unsigned long)PINED_PAGE_NUMBER, FOLL_FORCE, ptr_page, NULL, NULL);
#elif (KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE)
	return get_user_pages_remote(cur_struct, mm, start_code,
		(unsigned long)PINED_PAGE_NUMBER, FOLL_FORCE, ptr_page, NULL, NULL);
#elif (KERNEL_VERSION(4, 4, 197) == LINUX_VERSION_CODE)
	return get_user_pages_locked(cur_struct, mm, start_code,
		(unsigned long)PINED_PAGE_NUMBER, FOLL_FORCE, ptr_page, NULL);
#else
	return get_user_pages_locked(cur_struct, mm, start_code,
		(unsigned long)PINED_PAGE_NUMBER, 0, 1, ptr_page, NULL);
#endif
}

static int update_task_hash(struct mm_struct *mm,
	struct task_struct *cur_struct, struct shash_desc *shash)
{
	int rc = -1;
	unsigned long in_size;
	struct page *ptr_page = NULL;
	void *ptr_base = NULL;

	unsigned long start_code = mm->start_code;
	unsigned long end_code = mm->end_code;
	unsigned long code_size = end_code - start_code;
	if (code_size == 0) {
		tloge("bad code size\n");
		return -EINVAL;
	}

	while (start_code < end_code) {
		/* Get a handle of the page we want to read */
		rc = get_proc_user_pages(mm, start_code, &ptr_page, cur_struct);
		if (rc != PINED_PAGE_NUMBER) {
			tloge("get user pages error[0x%x]\n", rc);
			rc = -EFAULT;
			break;
		}

		ptr_base = kmap_atomic(ptr_page);
		if (!ptr_base) {
			rc = -EFAULT;
			put_page(ptr_page);
			break;
		}

		in_size = (code_size > PAGE_SIZE) ? PAGE_SIZE : code_size;
		rc = crypto_shash_update(shash, ptr_base, in_size);
		if (rc) {
			kunmap_atomic(ptr_base);
			put_page(ptr_page);
			break;
		}

		kunmap_atomic(ptr_base);
		put_page(ptr_page);
		start_code += in_size;
		code_size = end_code - start_code;
	}
	return rc;
}

int calc_task_hash(unsigned char *digest, uint32_t dig_len,
	struct task_struct *cur_struct, uint32_t pub_key_len)
{
	struct mm_struct *mm = NULL;
	struct sdesc *desc = NULL;
	bool check_value = false;
	int rc;

	check_value = (!cur_struct || !digest ||
		dig_len != SHA256_DIGEST_LENTH);
	if (check_value) {
		tloge("tee hash: input param is error\n");
		return -EFAULT;
	}

	mm = get_task_mm(cur_struct);
	if (!mm) {
		if (memset_s(digest, dig_len, 0, MAX_SHA_256_SZ))
			return -EFAULT;
		tloge("kernel proc need not check\n");
		return EOK;
	}

	if (pub_key_len != sizeof(uint32_t)) {
		tloge("apk need not check\n");
		mmput(mm);
		return EOK;
	}

	if (prepare_desc(&desc) != EOK) {
		mmput(mm);
		tloge("prepare desc failed\n");
		return -ENOMEM;
	}

	desc->shash.tfm = g_shash_handle;
	if (crypto_shash_init(&desc->shash)) {
		tloge("shash init failed\n");
		rc = -ENOMEM;
		goto free_res;
	}

	down_read(&mm_sem_lock(mm));
	if (update_task_hash(mm, cur_struct, &desc->shash)) {
		up_read(&mm_sem_lock(mm));
		rc = -ENOMEM;
		goto free_res;
	}
	up_read(&mm_sem_lock(mm));

	rc = crypto_shash_final(&desc->shash, digest);
free_res:
	mmput(mm);
	kfree(desc);
	return rc;
}
/* end: Calculate the SHA256 file digest */

#if defined(CONFIG_SELINUX_AUTH_ENABLE) && defined (CONFIG_SECURITY_SELINUX)
static int check_proc_selinux_access(const char * s_ctx)
{
	if (s_ctx == NULL) {
		tloge("bad params\n");
		return CHECK_ACCESS_FAIL;
	}

	int rc;
	u32 sid;
	u32 tid;
	u32 s_ctx_len = strnlen(s_ctx, MAX_SCTX_LEN);
	if (s_ctx_len == 0 || s_ctx_len >= MAX_SCTX_LEN) {
		tloge("invalid selinux ctx\n");
		return CHECK_ACCESS_FAIL;
	}

	security_task_getsecid(current, &sid);
	rc = security_secctx_to_secid(s_ctx, s_ctx_len, &tid);
	if (rc != 0) {
		tloge("secctx to sid failed, rc %d", rc);
		return CHECK_ACCESS_FAIL;
	}
	if (sid != tid) {
		tloge("check selinux label failed\n");
		return CHECK_ACCESS_FAIL;
	}

	return EOK;
}
#else
static int check_proc_selinux_access(const char * s_ctx)
{
	(void)s_ctx;
	return 0;
}
#endif

static int get_proc_uid(uid_t *proc_uid)
{
#ifdef CONFIG_LIBLINUX
	if (current->cred == NULL) {
		tloge("cred is NULL\n");
		return CHECK_ACCESS_FAIL;
	}
	*proc_uid = current->cred->uid.val;
#else
	const struct cred *cred = NULL;
	get_task_struct(current);
	cred = koadpt_get_task_cred(current);
	if (cred == NULL) {
		tloge("cred is NULL\n");
		put_task_struct(current);
		return CHECK_ACCESS_FAIL;
	}

	*proc_uid = cred->uid.val;
	put_cred(cred);
	put_task_struct(current);
#endif
	return CHECK_ACCESS_SUCC;
}

static int check_proc_uid_path(const char *auth_ctx)
{
	int ret = 0;
	char str_path_uid[MAX_PATH_SIZE] = { 0 };
	char *pro_dpath = NULL;
	char *k_path = NULL;
	u32 auth_ctx_len;
	uid_t proc_uid;

	if (auth_ctx == NULL) {
		tloge("bad params\n");
		return CHECK_ACCESS_FAIL;
	}

	auth_ctx_len = (u32)strnlen(auth_ctx, MAX_PATH_SIZE);
	if (auth_ctx_len == 0 || auth_ctx_len >= MAX_PATH_SIZE) {
		tloge("invalid uid path\n");
		return CHECK_ACCESS_FAIL;
	}

	k_path = kmalloc(MAX_PATH_SIZE, GFP_KERNEL);
	if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)k_path)) {
		tloge("path kmalloc fail\n");
		return CHECK_ACCESS_FAIL;
	}

	pro_dpath = get_proc_dpath(k_path, MAX_PATH_SIZE);
	if (IS_ERR_OR_NULL(pro_dpath)) {
		kfree(k_path);
		return CHECK_ACCESS_FAIL;
	}

	ret = get_proc_uid(&proc_uid);
	if (ret != CHECK_ACCESS_SUCC) {
		tloge("get proc uid failed\n");
		goto clean;
	}

	if (snprintf_s(str_path_uid, MAX_PATH_SIZE, MAX_PATH_SIZE - 1, "%s:%u",
		pro_dpath, (unsigned int)proc_uid) < 0) {
		tloge("snprintf_s path uid failed, ret %d\n", ret);
		ret = CHECK_ACCESS_FAIL;
		goto clean;
	}

	if (strnlen(str_path_uid, MAX_PATH_SIZE) != auth_ctx_len || strncmp(str_path_uid, auth_ctx, auth_ctx_len) != 0)
		ret = ENTER_BYPASS_CHANNEL;
	else
		ret = CHECK_ACCESS_SUCC;

clean:
	kfree(k_path);
	return ret;
}

#ifdef CONFIG_CADAEMON_AUTH
int check_cadaemon_auth(void)
{
	int ret = check_proc_uid_path(CADAEMON_PATH_UID_AUTH_CTX);
	if (ret != 0) {
		tloge("check cadaemon path failed, ret %d\n", ret);
		return ret;
	}
	ret = check_proc_selinux_access(SELINUX_CADAEMON_LABEL);
	if (ret != 0) {
		tloge("check cadaemon selinux label failed!, ret %d\n", ret);
		return -EACCES;
	}
	return 0;
}
#endif

int check_hidl_auth(void)
{
	int ret = check_proc_uid_path(CA_HIDL_PATH_UID_AUTH_CTX);
	if (ret != CHECK_ACCESS_SUCC)
		return ret;

#if defined(CONFIG_SELINUX_AUTH_ENABLE) && defined (CONFIG_SECURITY_SELINUX)
	ret = check_proc_selinux_access(SELINUX_CA_HIDL_LABEL);
	if (ret != EOK) {
		tloge("check hidl selinux label failed, ret %d\n", ret);
		return CHECK_SECLABEL_FAIL;
	}
#endif

	return CHECK_ACCESS_SUCC;
}

#ifdef CONFIG_TEECD_AUTH
int check_teecd_auth(void)
{
	int ret = check_proc_uid_path(TEECD_PATH_UID_AUTH_CTX);
	if (ret != 0) {
		tloge("check teecd path failed, ret %d\n", ret);
		return ret;
	}

#if defined(CONFIG_SELINUX_AUTH_ENABLE) && defined (CONFIG_SECURITY_SELINUX)
	ret = check_proc_selinux_access(SELINUX_TEECD_LABEL);
	if (ret != 0) {
		tloge("check teecd selinux label failed!, ret %d\n", ret);
		return -EACCES;
	}
#endif
	return CHECK_ACCESS_SUCC;
}
#endif