| // SPDX-License-Identifier: GPL-2.0 | 
 | /* | 
 |  * security/tomoyo/mount.c | 
 |  * | 
 |  * Copyright (C) 2005-2011  NTT DATA CORPORATION | 
 |  */ | 
 |  | 
 | #include <linux/slab.h> | 
 | #include <uapi/linux/mount.h> | 
 | #include "common.h" | 
 |  | 
 | /* String table for special mount operations. */ | 
 | static const char * const tomoyo_mounts[TOMOYO_MAX_SPECIAL_MOUNT] = { | 
 | 	[TOMOYO_MOUNT_BIND]            = "--bind", | 
 | 	[TOMOYO_MOUNT_MOVE]            = "--move", | 
 | 	[TOMOYO_MOUNT_REMOUNT]         = "--remount", | 
 | 	[TOMOYO_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable", | 
 | 	[TOMOYO_MOUNT_MAKE_PRIVATE]    = "--make-private", | 
 | 	[TOMOYO_MOUNT_MAKE_SLAVE]      = "--make-slave", | 
 | 	[TOMOYO_MOUNT_MAKE_SHARED]     = "--make-shared", | 
 | }; | 
 |  | 
 | /** | 
 |  * tomoyo_audit_mount_log - Audit mount log. | 
 |  * | 
 |  * @r: Pointer to "struct tomoyo_request_info". | 
 |  * | 
 |  * Returns 0 on success, negative value otherwise. | 
 |  */ | 
 | static int tomoyo_audit_mount_log(struct tomoyo_request_info *r) | 
 | { | 
 | 	return tomoyo_supervisor(r, "file mount %s %s %s 0x%lX\n", | 
 | 				 r->param.mount.dev->name, | 
 | 				 r->param.mount.dir->name, | 
 | 				 r->param.mount.type->name, | 
 | 				 r->param.mount.flags); | 
 | } | 
 |  | 
 | /** | 
 |  * tomoyo_check_mount_acl - Check permission for path path path number operation. | 
 |  * | 
 |  * @r:   Pointer to "struct tomoyo_request_info". | 
 |  * @ptr: Pointer to "struct tomoyo_acl_info". | 
 |  * | 
 |  * Returns true if granted, false otherwise. | 
 |  */ | 
 | static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r, | 
 | 				   const struct tomoyo_acl_info *ptr) | 
 | { | 
 | 	const struct tomoyo_mount_acl *acl = | 
 | 		container_of(ptr, typeof(*acl), head); | 
 |  | 
 | 	return tomoyo_compare_number_union(r->param.mount.flags, | 
 | 					   &acl->flags) && | 
 | 		tomoyo_compare_name_union(r->param.mount.type, | 
 | 					  &acl->fs_type) && | 
 | 		tomoyo_compare_name_union(r->param.mount.dir, | 
 | 					  &acl->dir_name) && | 
 | 		(!r->param.mount.need_dev || | 
 | 		 tomoyo_compare_name_union(r->param.mount.dev, | 
 | 					   &acl->dev_name)); | 
 | } | 
 |  | 
 | /** | 
 |  * tomoyo_mount_acl - Check permission for mount() operation. | 
 |  * | 
 |  * @r:        Pointer to "struct tomoyo_request_info". | 
 |  * @dev_name: Name of device file. Maybe NULL. | 
 |  * @dir:      Pointer to "struct path". | 
 |  * @type:     Name of filesystem type. | 
 |  * @flags:    Mount options. | 
 |  * | 
 |  * Returns 0 on success, negative value otherwise. | 
 |  * | 
 |  * Caller holds tomoyo_read_lock(). | 
 |  */ | 
 | static int tomoyo_mount_acl(struct tomoyo_request_info *r, | 
 | 			    const char *dev_name, | 
 | 			    const struct path *dir, const char *type, | 
 | 			    unsigned long flags) | 
 | { | 
 | 	struct tomoyo_obj_info obj = { }; | 
 | 	struct path path; | 
 | 	struct file_system_type *fstype = NULL; | 
 | 	const char *requested_type = NULL; | 
 | 	const char *requested_dir_name = NULL; | 
 | 	const char *requested_dev_name = NULL; | 
 | 	struct tomoyo_path_info rtype; | 
 | 	struct tomoyo_path_info rdev; | 
 | 	struct tomoyo_path_info rdir; | 
 | 	int need_dev = 0; | 
 | 	int error = -ENOMEM; | 
 |  | 
 | 	r->obj = &obj; | 
 |  | 
 | 	/* Get fstype. */ | 
 | 	requested_type = tomoyo_encode(type); | 
 | 	if (!requested_type) | 
 | 		goto out; | 
 | 	rtype.name = requested_type; | 
 | 	tomoyo_fill_path_info(&rtype); | 
 |  | 
 | 	/* Get mount point. */ | 
 | 	obj.path2 = *dir; | 
 | 	requested_dir_name = tomoyo_realpath_from_path(dir); | 
 | 	if (!requested_dir_name) { | 
 | 		error = -ENOMEM; | 
 | 		goto out; | 
 | 	} | 
 | 	rdir.name = requested_dir_name; | 
 | 	tomoyo_fill_path_info(&rdir); | 
 |  | 
 | 	/* Compare fs name. */ | 
 | 	if (type == tomoyo_mounts[TOMOYO_MOUNT_REMOUNT]) { | 
 | 		/* dev_name is ignored. */ | 
 | 	} else if (type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE] || | 
 | 		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE] || | 
 | 		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE] || | 
 | 		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]) { | 
 | 		/* dev_name is ignored. */ | 
 | 	} else if (type == tomoyo_mounts[TOMOYO_MOUNT_BIND] || | 
 | 		   type == tomoyo_mounts[TOMOYO_MOUNT_MOVE]) { | 
 | 		need_dev = -1; /* dev_name is a directory */ | 
 | 	} else { | 
 | 		fstype = get_fs_type(type); | 
 | 		if (!fstype) { | 
 | 			error = -ENODEV; | 
 | 			goto out; | 
 | 		} | 
 | 		if (fstype->fs_flags & FS_REQUIRES_DEV) | 
 | 			/* dev_name is a block device file. */ | 
 | 			need_dev = 1; | 
 | 	} | 
 | 	if (need_dev) { | 
 | 		/* Get mount point or device file. */ | 
 | 		if (!dev_name || kern_path(dev_name, LOOKUP_FOLLOW, &path)) { | 
 | 			error = -ENOENT; | 
 | 			goto out; | 
 | 		} | 
 | 		obj.path1 = path; | 
 | 		requested_dev_name = tomoyo_realpath_from_path(&path); | 
 | 		if (!requested_dev_name) { | 
 | 			error = -ENOENT; | 
 | 			goto out; | 
 | 		} | 
 | 	} else { | 
 | 		/* Map dev_name to "<NULL>" if no dev_name given. */ | 
 | 		if (!dev_name) | 
 | 			dev_name = "<NULL>"; | 
 | 		requested_dev_name = tomoyo_encode(dev_name); | 
 | 		if (!requested_dev_name) { | 
 | 			error = -ENOMEM; | 
 | 			goto out; | 
 | 		} | 
 | 	} | 
 | 	rdev.name = requested_dev_name; | 
 | 	tomoyo_fill_path_info(&rdev); | 
 | 	r->param_type = TOMOYO_TYPE_MOUNT_ACL; | 
 | 	r->param.mount.need_dev = need_dev; | 
 | 	r->param.mount.dev = &rdev; | 
 | 	r->param.mount.dir = &rdir; | 
 | 	r->param.mount.type = &rtype; | 
 | 	r->param.mount.flags = flags; | 
 | 	do { | 
 | 		tomoyo_check_acl(r, tomoyo_check_mount_acl); | 
 | 		error = tomoyo_audit_mount_log(r); | 
 | 	} while (error == TOMOYO_RETRY_REQUEST); | 
 |  out: | 
 | 	kfree(requested_dev_name); | 
 | 	kfree(requested_dir_name); | 
 | 	if (fstype) | 
 | 		put_filesystem(fstype); | 
 | 	kfree(requested_type); | 
 | 	/* Drop refcount obtained by kern_path(). */ | 
 | 	if (obj.path1.dentry) | 
 | 		path_put(&obj.path1); | 
 | 	return error; | 
 | } | 
 |  | 
 | /** | 
 |  * tomoyo_mount_permission - Check permission for mount() operation. | 
 |  * | 
 |  * @dev_name:  Name of device file. Maybe NULL. | 
 |  * @path:      Pointer to "struct path". | 
 |  * @type:      Name of filesystem type. Maybe NULL. | 
 |  * @flags:     Mount options. | 
 |  * @data_page: Optional data. Maybe NULL. | 
 |  * | 
 |  * Returns 0 on success, negative value otherwise. | 
 |  */ | 
 | int tomoyo_mount_permission(const char *dev_name, const struct path *path, | 
 | 			    const char *type, unsigned long flags, | 
 | 			    void *data_page) | 
 | { | 
 | 	struct tomoyo_request_info r; | 
 | 	int error; | 
 | 	int idx; | 
 |  | 
 | 	if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT) | 
 | 	    == TOMOYO_CONFIG_DISABLED) | 
 | 		return 0; | 
 | 	if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | 
 | 		flags &= ~MS_MGC_MSK; | 
 | 	if (flags & MS_REMOUNT) { | 
 | 		type = tomoyo_mounts[TOMOYO_MOUNT_REMOUNT]; | 
 | 		flags &= ~MS_REMOUNT; | 
 | 	} else if (flags & MS_BIND) { | 
 | 		type = tomoyo_mounts[TOMOYO_MOUNT_BIND]; | 
 | 		flags &= ~MS_BIND; | 
 | 	} else if (flags & MS_SHARED) { | 
 | 		if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) | 
 | 			return -EINVAL; | 
 | 		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]; | 
 | 		flags &= ~MS_SHARED; | 
 | 	} else if (flags & MS_PRIVATE) { | 
 | 		if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE)) | 
 | 			return -EINVAL; | 
 | 		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE]; | 
 | 		flags &= ~MS_PRIVATE; | 
 | 	} else if (flags & MS_SLAVE) { | 
 | 		if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE)) | 
 | 			return -EINVAL; | 
 | 		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE]; | 
 | 		flags &= ~MS_SLAVE; | 
 | 	} else if (flags & MS_UNBINDABLE) { | 
 | 		if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE)) | 
 | 			return -EINVAL; | 
 | 		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE]; | 
 | 		flags &= ~MS_UNBINDABLE; | 
 | 	} else if (flags & MS_MOVE) { | 
 | 		type = tomoyo_mounts[TOMOYO_MOUNT_MOVE]; | 
 | 		flags &= ~MS_MOVE; | 
 | 	} | 
 | 	if (!type) | 
 | 		type = "<NULL>"; | 
 | 	idx = tomoyo_read_lock(); | 
 | 	error = tomoyo_mount_acl(&r, dev_name, path, type, flags); | 
 | 	tomoyo_read_unlock(idx); | 
 | 	return error; | 
 | } |