1579 lines · 38.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (c) 2015-2016, Linaro Limited */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include <linux/cdev.h>#include <linux/cred.h>#include <linux/fs.h>#include <linux/idr.h>#include <linux/module.h>#include <linux/overflow.h>#include <linux/slab.h>#include <linux/tee_core.h>#include <linux/uaccess.h>#include <crypto/sha1.h>#include "tee_private.h" #define TEE_NUM_DEVICES	32 #define TEE_IOCTL_PARAM_SIZE(x) (size_mul(sizeof(struct tee_param), (x))) #define TEE_UUID_NS_NAME_SIZE	128 /* * TEE Client UUID name space identifier (UUIDv4) * * Value here is random UUID that is allocated as name space identifier for * forming Client UUID's for TEE environment using UUIDv5 scheme. */static const uuid_t tee_client_uuid_ns = UUID_INIT(0x58ac9ca0, 0x2086, 0x4683,						   0xa1, 0xb8, 0xec, 0x4b,						   0xc0, 0x8e, 0x01, 0xb6); /* * Unprivileged devices in the lower half range and privileged devices in * the upper half range. */static DECLARE_BITMAP(dev_mask, TEE_NUM_DEVICES);static DEFINE_SPINLOCK(driver_lock); static const struct class tee_class;static dev_t tee_devt; struct tee_context *teedev_open(struct tee_device *teedev){	int rc;	struct tee_context *ctx; 	if (!tee_device_get(teedev))		return ERR_PTR(-EINVAL); 	ctx = kzalloc_obj(*ctx);	if (!ctx) {		rc = -ENOMEM;		goto err;	} 	kref_init(&ctx->refcount);	ctx->teedev = teedev;	rc = teedev->desc->ops->open(ctx);	if (rc)		goto err; 	return ctx;err:	kfree(ctx);	tee_device_put(teedev);	return ERR_PTR(rc); }EXPORT_SYMBOL_GPL(teedev_open); void teedev_ctx_get(struct tee_context *ctx){	if (ctx->releasing)		return; 	kref_get(&ctx->refcount);}EXPORT_SYMBOL_GPL(teedev_ctx_get); static void teedev_ctx_release(struct kref *ref){	struct tee_context *ctx = container_of(ref, struct tee_context,					       refcount);	ctx->releasing = true;	ctx->teedev->desc->ops->release(ctx);	kfree(ctx);} void teedev_ctx_put(struct tee_context *ctx){	if (ctx->releasing)		return; 	kref_put(&ctx->refcount, teedev_ctx_release);}EXPORT_SYMBOL_GPL(teedev_ctx_put); void teedev_close_context(struct tee_context *ctx){	struct tee_device *teedev = ctx->teedev; 	if (teedev->desc->ops->close_context)		teedev->desc->ops->close_context(ctx); 	teedev_ctx_put(ctx);	tee_device_put(teedev);}EXPORT_SYMBOL_GPL(teedev_close_context); static int tee_open(struct inode *inode, struct file *filp){	struct tee_context *ctx; 	ctx = teedev_open(container_of(inode->i_cdev, struct tee_device, cdev));	if (IS_ERR(ctx))		return PTR_ERR(ctx); 	/*	 * Default user-space behaviour is to wait for tee-supplicant	 * if not present for any requests in this context.	 */	ctx->supp_nowait = false;	filp->private_data = ctx;	return 0;} static int tee_release(struct inode *inode, struct file *filp){	teedev_close_context(filp->private_data);	return 0;} /** * uuid_v5() - Calculate UUIDv5 * @uuid: Resulting UUID * @ns: Name space ID for UUIDv5 function * @name: Name for UUIDv5 function * @size: Size of name * * UUIDv5 is specific in RFC 4122. * * This implements section (for SHA-1): * 4.3.  Algorithm for Creating a Name-Based UUID */static void uuid_v5(uuid_t *uuid, const uuid_t *ns, const void *name,		    size_t size){	unsigned char hash[SHA1_DIGEST_SIZE];	struct sha1_ctx ctx; 	sha1_init(&ctx);	sha1_update(&ctx, (const u8 *)ns, sizeof(*ns));	sha1_update(&ctx, (const u8 *)name, size);	sha1_final(&ctx, hash); 	memcpy(uuid->b, hash, UUID_SIZE); 	/* Tag for version 5 */	uuid->b[6] = (hash[6] & 0x0F) | 0x50;	uuid->b[8] = (hash[8] & 0x3F) | 0x80;} int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method,				 const u8 connection_data[TEE_IOCTL_UUID_LEN]){	gid_t ns_grp = (gid_t)-1;	kgid_t grp = INVALID_GID;	char *name = NULL;	int name_len;	int rc = 0; 	if (connection_method == TEE_IOCTL_LOGIN_PUBLIC ||	    connection_method == TEE_IOCTL_LOGIN_REE_KERNEL) {		/* Nil UUID to be passed to TEE environment */		uuid_copy(uuid, &uuid_null);		return 0;	} 	/*	 * In Linux environment client UUID is based on UUIDv5.	 *	 * Determine client UUID with following semantics for 'name':	 *	 * For TEEC_LOGIN_USER:	 * uid=<uid>	 *	 * For TEEC_LOGIN_GROUP:	 * gid=<gid>	 *	 */ 	name = kzalloc(TEE_UUID_NS_NAME_SIZE, GFP_KERNEL);	if (!name)		return -ENOMEM; 	switch (connection_method) {	case TEE_IOCTL_LOGIN_USER:		name_len = snprintf(name, TEE_UUID_NS_NAME_SIZE, "uid=%x",				    current_euid().val);		if (name_len >= TEE_UUID_NS_NAME_SIZE) {			rc = -E2BIG;			goto out_free_name;		}		break; 	case TEE_IOCTL_LOGIN_GROUP:		memcpy(&ns_grp, connection_data, sizeof(gid_t));		grp = make_kgid(current_user_ns(), ns_grp);		if (!gid_valid(grp) || !in_egroup_p(grp)) {			rc = -EPERM;			goto out_free_name;		} 		name_len = snprintf(name, TEE_UUID_NS_NAME_SIZE, "gid=%x",				    grp.val);		if (name_len >= TEE_UUID_NS_NAME_SIZE) {			rc = -E2BIG;			goto out_free_name;		}		break; 	default:		rc = -EINVAL;		goto out_free_name;	} 	uuid_v5(uuid, &tee_client_uuid_ns, name, name_len);out_free_name:	kfree(name); 	return rc;}EXPORT_SYMBOL_GPL(tee_session_calc_client_uuid); static int tee_ioctl_version(struct tee_context *ctx,			     struct tee_ioctl_version_data __user *uvers){	struct tee_ioctl_version_data vers; 	ctx->teedev->desc->ops->get_version(ctx->teedev, &vers); 	if (ctx->teedev->desc->flags & TEE_DESC_PRIVILEGED)		vers.gen_caps |= TEE_GEN_CAP_PRIVILEGED; 	if (copy_to_user(uvers, &vers, sizeof(vers)))		return -EFAULT; 	return 0;} static int tee_ioctl_shm_alloc(struct tee_context *ctx,			       struct tee_ioctl_shm_alloc_data __user *udata){	long ret;	struct tee_ioctl_shm_alloc_data data;	struct tee_shm *shm; 	if (copy_from_user(&data, udata, sizeof(data)))		return -EFAULT; 	/* Currently no input flags are supported */	if (data.flags)		return -EINVAL; 	shm = tee_shm_alloc_user_buf(ctx, data.size);	if (IS_ERR(shm))		return PTR_ERR(shm); 	data.id = shm->id;	data.size = shm->size; 	if (copy_to_user(udata, &data, sizeof(data)))		ret = -EFAULT;	else		ret = tee_shm_get_fd(shm); 	/*	 * When user space closes the file descriptor the shared memory	 * should be freed or if tee_shm_get_fd() failed then it will	 * be freed immediately.	 */	tee_shm_put(shm);	return ret;} static inttee_ioctl_shm_register(struct tee_context *ctx,		       struct tee_ioctl_shm_register_data __user *udata){	long ret;	struct tee_ioctl_shm_register_data data;	struct tee_shm *shm; 	if (copy_from_user(&data, udata, sizeof(data)))		return -EFAULT; 	/* Currently no input flags are supported */	if (data.flags)		return -EINVAL; 	shm = tee_shm_register_user_buf(ctx, data.addr, data.length);	if (IS_ERR(shm))		return PTR_ERR(shm); 	data.id = shm->id;	data.length = shm->size; 	if (copy_to_user(udata, &data, sizeof(data)))		ret = -EFAULT;	else		ret = tee_shm_get_fd(shm);	/*	 * When user space closes the file descriptor the shared memory	 * should be freed or if tee_shm_get_fd() failed then it will	 * be freed immediately.	 */	tee_shm_put(shm);	return ret;} static inttee_ioctl_shm_register_fd(struct tee_context *ctx,			  struct tee_ioctl_shm_register_fd_data __user *udata){	struct tee_ioctl_shm_register_fd_data data;	struct tee_shm *shm;	long ret; 	if (copy_from_user(&data, udata, sizeof(data)))		return -EFAULT; 	/* Currently no input flags are supported */	if (data.flags)		return -EINVAL; 	shm = tee_shm_register_fd(ctx, data.fd);	if (IS_ERR(shm))		return -EINVAL; 	data.id = shm->id;	data.flags = shm->flags;	data.size = shm->size; 	if (copy_to_user(udata, &data, sizeof(data)))		ret = -EFAULT;	else		ret = tee_shm_get_fd(shm); 	/*	 * When user space closes the file descriptor the shared memory	 * should be freed or if tee_shm_get_fd() failed then it will	 * be freed immediately.	 */	tee_shm_put(shm);	return ret;} static int param_from_user_memref(struct tee_context *ctx,				  struct tee_param_memref *memref,				  struct tee_ioctl_param *ip){	struct tee_shm *shm;	size_t offs = 0; 	/*	 * If a NULL pointer is passed to a TA in the TEE,	 * the ip.c IOCTL parameters is set to TEE_MEMREF_NULL	 * indicating a NULL memory reference.	 */	if (ip->c != TEE_MEMREF_NULL) {		/*		 * If we fail to get a pointer to a shared		 * memory object (and increase the ref count)		 * from an identifier we return an error. All		 * pointers that has been added in params have		 * an increased ref count. It's the callers		 * responibility to do tee_shm_put() on all		 * resolved pointers.		 */		shm = tee_shm_get_from_id(ctx, ip->c);		if (IS_ERR(shm))			return PTR_ERR(shm); 		/*		 * Ensure offset + size does not overflow		 * offset and does not overflow the size of		 * the referred shared memory object.		 */		if ((ip->a + ip->b) < ip->a ||		    (ip->a + ip->b) > shm->size) {			tee_shm_put(shm);			return -EINVAL;		} 		if (shm->flags & TEE_SHM_DMA_BUF) {			struct tee_shm_dmabuf_ref *ref; 			ref = container_of(shm, struct tee_shm_dmabuf_ref, shm);			if (ref->parent_shm) {				/*				 * The shm already has one reference to				 * ref->parent_shm so we are clear of 0.				 * We're getting another reference since				 * this shm will be used in the parameter				 * list instead of the shm we got with				 * tee_shm_get_from_id() above.				 */				refcount_inc(&ref->parent_shm->refcount);				tee_shm_put(shm);				shm = ref->parent_shm;				offs = ref->offset;			}		}	} else if (ctx->cap_memref_null) {		/* Pass NULL pointer to OP-TEE */		shm = NULL;	} else {		return -EINVAL;	} 	memref->shm_offs = ip->a + offs;	memref->size = ip->b;	memref->shm = shm; 	return 0;} static int params_from_user(struct tee_context *ctx, struct tee_param *params,			    size_t num_params,			    struct tee_ioctl_param __user *uparams){	size_t n; 	for (n = 0; n < num_params; n++) {		struct tee_ioctl_param ip;		int rc; 		if (copy_from_user(&ip, uparams + n, sizeof(ip)))			return -EFAULT; 		/* All unused attribute bits has to be zero */		if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK)			return -EINVAL; 		params[n].attr = ip.attr;		switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:		case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT:			break;		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:			params[n].u.value.a = ip.a;			params[n].u.value.b = ip.b;			params[n].u.value.c = ip.c;			break;		case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT:		case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT:		case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT:			params[n].u.ubuf.uaddr = u64_to_user_ptr(ip.a);			params[n].u.ubuf.size = ip.b; 			if (!access_ok(params[n].u.ubuf.uaddr,				       params[n].u.ubuf.size))				return -EFAULT; 			break;		case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT:		case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT:			params[n].u.objref.id = ip.a;			params[n].u.objref.flags = ip.b;			break;		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:			rc = param_from_user_memref(ctx, &params[n].u.memref,						    &ip);			if (rc)				return rc;			break;		default:			/* Unknown attribute */			return -EINVAL;		}	}	return 0;} static int params_to_user(struct tee_ioctl_param __user *uparams,			  size_t num_params, struct tee_param *params){	size_t n; 	for (n = 0; n < num_params; n++) {		struct tee_ioctl_param __user *up = uparams + n;		struct tee_param *p = params + n;

Lines 1–500 of 1579.