xref: /openbmc/linux/security/tomoyo/mount.c (revision 597473720f4dc69749542bfcfed4a927a43d935e)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
22106ccd9STetsuo Handa /*
32106ccd9STetsuo Handa  * security/tomoyo/mount.c
42106ccd9STetsuo Handa  *
50f2a55d5STetsuo Handa  * Copyright (C) 2005-2011  NTT DATA CORPORATION
62106ccd9STetsuo Handa  */
72106ccd9STetsuo Handa 
82106ccd9STetsuo Handa #include <linux/slab.h>
9e262e32dSDavid Howells #include <uapi/linux/mount.h>
102106ccd9STetsuo Handa #include "common.h"
112106ccd9STetsuo Handa 
12b5bc60b4STetsuo Handa /* String table for special mount operations. */
13b5bc60b4STetsuo Handa static const char * const tomoyo_mounts[TOMOYO_MAX_SPECIAL_MOUNT] = {
14b5bc60b4STetsuo Handa 	[TOMOYO_MOUNT_BIND]            = "--bind",
15b5bc60b4STetsuo Handa 	[TOMOYO_MOUNT_MOVE]            = "--move",
16b5bc60b4STetsuo Handa 	[TOMOYO_MOUNT_REMOUNT]         = "--remount",
17b5bc60b4STetsuo Handa 	[TOMOYO_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable",
18b5bc60b4STetsuo Handa 	[TOMOYO_MOUNT_MAKE_PRIVATE]    = "--make-private",
19b5bc60b4STetsuo Handa 	[TOMOYO_MOUNT_MAKE_SLAVE]      = "--make-slave",
20b5bc60b4STetsuo Handa 	[TOMOYO_MOUNT_MAKE_SHARED]     = "--make-shared",
21b5bc60b4STetsuo Handa };
222106ccd9STetsuo Handa 
232106ccd9STetsuo Handa /**
2499a85259STetsuo Handa  * tomoyo_audit_mount_log - Audit mount log.
2599a85259STetsuo Handa  *
2699a85259STetsuo Handa  * @r: Pointer to "struct tomoyo_request_info".
2799a85259STetsuo Handa  *
2899a85259STetsuo Handa  * Returns 0 on success, negative value otherwise.
2999a85259STetsuo Handa  */
tomoyo_audit_mount_log(struct tomoyo_request_info * r)3099a85259STetsuo Handa static int tomoyo_audit_mount_log(struct tomoyo_request_info *r)
3199a85259STetsuo Handa {
32eadd99ccSTetsuo Handa 	return tomoyo_supervisor(r, "file mount %s %s %s 0x%lX\n",
337c75964fSTetsuo Handa 				 r->param.mount.dev->name,
34eadd99ccSTetsuo Handa 				 r->param.mount.dir->name,
35eadd99ccSTetsuo Handa 				 r->param.mount.type->name,
36eadd99ccSTetsuo Handa 				 r->param.mount.flags);
3799a85259STetsuo Handa }
3899a85259STetsuo Handa 
390df7e8b8STetsuo Handa /**
400df7e8b8STetsuo Handa  * tomoyo_check_mount_acl - Check permission for path path path number operation.
410df7e8b8STetsuo Handa  *
420df7e8b8STetsuo Handa  * @r:   Pointer to "struct tomoyo_request_info".
430df7e8b8STetsuo Handa  * @ptr: Pointer to "struct tomoyo_acl_info".
440df7e8b8STetsuo Handa  *
450df7e8b8STetsuo Handa  * Returns true if granted, false otherwise.
460df7e8b8STetsuo Handa  */
tomoyo_check_mount_acl(struct tomoyo_request_info * r,const struct tomoyo_acl_info * ptr)47484ca79cSTetsuo Handa static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
4899a85259STetsuo Handa 				   const struct tomoyo_acl_info *ptr)
4999a85259STetsuo Handa {
5099a85259STetsuo Handa 	const struct tomoyo_mount_acl *acl =
5199a85259STetsuo Handa 		container_of(ptr, typeof(*acl), head);
52*cdcf6723STetsuo Handa 
530df7e8b8STetsuo Handa 	return tomoyo_compare_number_union(r->param.mount.flags,
540df7e8b8STetsuo Handa 					   &acl->flags) &&
550df7e8b8STetsuo Handa 		tomoyo_compare_name_union(r->param.mount.type,
560df7e8b8STetsuo Handa 					  &acl->fs_type) &&
570df7e8b8STetsuo Handa 		tomoyo_compare_name_union(r->param.mount.dir,
580df7e8b8STetsuo Handa 					  &acl->dir_name) &&
5999a85259STetsuo Handa 		(!r->param.mount.need_dev ||
600df7e8b8STetsuo Handa 		 tomoyo_compare_name_union(r->param.mount.dev,
610df7e8b8STetsuo Handa 					   &acl->dev_name));
6299a85259STetsuo Handa }
6399a85259STetsuo Handa 
6499a85259STetsuo Handa /**
65d795ef9eSTetsuo Handa  * tomoyo_mount_acl - Check permission for mount() operation.
662106ccd9STetsuo Handa  *
672106ccd9STetsuo Handa  * @r:        Pointer to "struct tomoyo_request_info".
680f2a55d5STetsuo Handa  * @dev_name: Name of device file. Maybe NULL.
692106ccd9STetsuo Handa  * @dir:      Pointer to "struct path".
702106ccd9STetsuo Handa  * @type:     Name of filesystem type.
712106ccd9STetsuo Handa  * @flags:    Mount options.
722106ccd9STetsuo Handa  *
732106ccd9STetsuo Handa  * Returns 0 on success, negative value otherwise.
742106ccd9STetsuo Handa  *
752106ccd9STetsuo Handa  * Caller holds tomoyo_read_lock().
762106ccd9STetsuo Handa  */
tomoyo_mount_acl(struct tomoyo_request_info * r,const char * dev_name,const struct path * dir,const char * type,unsigned long flags)77808d4e3cSAl Viro static int tomoyo_mount_acl(struct tomoyo_request_info *r,
78808d4e3cSAl Viro 			    const char *dev_name,
79e6641eddSAl Viro 			    const struct path *dir, const char *type,
80b5bc60b4STetsuo Handa 			    unsigned long flags)
812106ccd9STetsuo Handa {
8297fb35e4STetsuo Handa 	struct tomoyo_obj_info obj = { };
832106ccd9STetsuo Handa 	struct path path;
842106ccd9STetsuo Handa 	struct file_system_type *fstype = NULL;
852106ccd9STetsuo Handa 	const char *requested_type = NULL;
862106ccd9STetsuo Handa 	const char *requested_dir_name = NULL;
872106ccd9STetsuo Handa 	const char *requested_dev_name = NULL;
882106ccd9STetsuo Handa 	struct tomoyo_path_info rtype;
892106ccd9STetsuo Handa 	struct tomoyo_path_info rdev;
902106ccd9STetsuo Handa 	struct tomoyo_path_info rdir;
912106ccd9STetsuo Handa 	int need_dev = 0;
922106ccd9STetsuo Handa 	int error = -ENOMEM;
93*cdcf6723STetsuo Handa 
9497fb35e4STetsuo Handa 	r->obj = &obj;
952106ccd9STetsuo Handa 
962106ccd9STetsuo Handa 	/* Get fstype. */
97c8c57e84STetsuo Handa 	requested_type = tomoyo_encode(type);
982106ccd9STetsuo Handa 	if (!requested_type)
992106ccd9STetsuo Handa 		goto out;
1002106ccd9STetsuo Handa 	rtype.name = requested_type;
1012106ccd9STetsuo Handa 	tomoyo_fill_path_info(&rtype);
1022106ccd9STetsuo Handa 
1032106ccd9STetsuo Handa 	/* Get mount point. */
10497fb35e4STetsuo Handa 	obj.path2 = *dir;
1052106ccd9STetsuo Handa 	requested_dir_name = tomoyo_realpath_from_path(dir);
1062106ccd9STetsuo Handa 	if (!requested_dir_name) {
1072106ccd9STetsuo Handa 		error = -ENOMEM;
1082106ccd9STetsuo Handa 		goto out;
1092106ccd9STetsuo Handa 	}
1102106ccd9STetsuo Handa 	rdir.name = requested_dir_name;
1112106ccd9STetsuo Handa 	tomoyo_fill_path_info(&rdir);
1122106ccd9STetsuo Handa 
1132106ccd9STetsuo Handa 	/* Compare fs name. */
114b5bc60b4STetsuo Handa 	if (type == tomoyo_mounts[TOMOYO_MOUNT_REMOUNT]) {
1152106ccd9STetsuo Handa 		/* dev_name is ignored. */
116b5bc60b4STetsuo Handa 	} else if (type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE] ||
117b5bc60b4STetsuo Handa 		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE] ||
118b5bc60b4STetsuo Handa 		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE] ||
119b5bc60b4STetsuo Handa 		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]) {
1202106ccd9STetsuo Handa 		/* dev_name is ignored. */
121b5bc60b4STetsuo Handa 	} else if (type == tomoyo_mounts[TOMOYO_MOUNT_BIND] ||
122b5bc60b4STetsuo Handa 		   type == tomoyo_mounts[TOMOYO_MOUNT_MOVE]) {
1232106ccd9STetsuo Handa 		need_dev = -1; /* dev_name is a directory */
1242106ccd9STetsuo Handa 	} else {
1252106ccd9STetsuo Handa 		fstype = get_fs_type(type);
1262106ccd9STetsuo Handa 		if (!fstype) {
1272106ccd9STetsuo Handa 			error = -ENODEV;
1282106ccd9STetsuo Handa 			goto out;
1292106ccd9STetsuo Handa 		}
1302106ccd9STetsuo Handa 		if (fstype->fs_flags & FS_REQUIRES_DEV)
1312106ccd9STetsuo Handa 			/* dev_name is a block device file. */
1322106ccd9STetsuo Handa 			need_dev = 1;
1332106ccd9STetsuo Handa 	}
1342106ccd9STetsuo Handa 	if (need_dev) {
1352106ccd9STetsuo Handa 		/* Get mount point or device file. */
1364e78c724STetsuo Handa 		if (!dev_name || kern_path(dev_name, LOOKUP_FOLLOW, &path)) {
1372106ccd9STetsuo Handa 			error = -ENOENT;
1382106ccd9STetsuo Handa 			goto out;
1392106ccd9STetsuo Handa 		}
14097fb35e4STetsuo Handa 		obj.path1 = path;
1412106ccd9STetsuo Handa 		requested_dev_name = tomoyo_realpath_from_path(&path);
1422106ccd9STetsuo Handa 		if (!requested_dev_name) {
1432106ccd9STetsuo Handa 			error = -ENOENT;
1442106ccd9STetsuo Handa 			goto out;
1452106ccd9STetsuo Handa 		}
1462106ccd9STetsuo Handa 	} else {
1472106ccd9STetsuo Handa 		/* Map dev_name to "<NULL>" if no dev_name given. */
1482106ccd9STetsuo Handa 		if (!dev_name)
1492106ccd9STetsuo Handa 			dev_name = "<NULL>";
150c8c57e84STetsuo Handa 		requested_dev_name = tomoyo_encode(dev_name);
1512106ccd9STetsuo Handa 		if (!requested_dev_name) {
1522106ccd9STetsuo Handa 			error = -ENOMEM;
1532106ccd9STetsuo Handa 			goto out;
1542106ccd9STetsuo Handa 		}
1552106ccd9STetsuo Handa 	}
1562106ccd9STetsuo Handa 	rdev.name = requested_dev_name;
1572106ccd9STetsuo Handa 	tomoyo_fill_path_info(&rdev);
158cf6e9a64STetsuo Handa 	r->param_type = TOMOYO_TYPE_MOUNT_ACL;
159cf6e9a64STetsuo Handa 	r->param.mount.need_dev = need_dev;
160cf6e9a64STetsuo Handa 	r->param.mount.dev = &rdev;
161cf6e9a64STetsuo Handa 	r->param.mount.dir = &rdir;
162cf6e9a64STetsuo Handa 	r->param.mount.type = &rtype;
163cf6e9a64STetsuo Handa 	r->param.mount.flags = flags;
16499a85259STetsuo Handa 	do {
16599a85259STetsuo Handa 		tomoyo_check_acl(r, tomoyo_check_mount_acl);
16699a85259STetsuo Handa 		error = tomoyo_audit_mount_log(r);
16799a85259STetsuo Handa 	} while (error == TOMOYO_RETRY_REQUEST);
1682106ccd9STetsuo Handa  out:
1692106ccd9STetsuo Handa 	kfree(requested_dev_name);
1702106ccd9STetsuo Handa 	kfree(requested_dir_name);
1712106ccd9STetsuo Handa 	if (fstype)
1722106ccd9STetsuo Handa 		put_filesystem(fstype);
1732106ccd9STetsuo Handa 	kfree(requested_type);
17497fb35e4STetsuo Handa 	/* Drop refcount obtained by kern_path(). */
17597fb35e4STetsuo Handa 	if (obj.path1.dentry)
17697fb35e4STetsuo Handa 		path_put(&obj.path1);
1772106ccd9STetsuo Handa 	return error;
1782106ccd9STetsuo Handa }
1792106ccd9STetsuo Handa 
1802106ccd9STetsuo Handa /**
1812106ccd9STetsuo Handa  * tomoyo_mount_permission - Check permission for mount() operation.
1822106ccd9STetsuo Handa  *
1830f2a55d5STetsuo Handa  * @dev_name:  Name of device file. Maybe NULL.
1842106ccd9STetsuo Handa  * @path:      Pointer to "struct path".
1852106ccd9STetsuo Handa  * @type:      Name of filesystem type. Maybe NULL.
1862106ccd9STetsuo Handa  * @flags:     Mount options.
1872106ccd9STetsuo Handa  * @data_page: Optional data. Maybe NULL.
1882106ccd9STetsuo Handa  *
1892106ccd9STetsuo Handa  * Returns 0 on success, negative value otherwise.
1902106ccd9STetsuo Handa  */
tomoyo_mount_permission(const char * dev_name,const struct path * path,const char * type,unsigned long flags,void * data_page)191e6641eddSAl Viro int tomoyo_mount_permission(const char *dev_name, const struct path *path,
192b5bc60b4STetsuo Handa 			    const char *type, unsigned long flags,
193b5bc60b4STetsuo Handa 			    void *data_page)
1942106ccd9STetsuo Handa {
1952106ccd9STetsuo Handa 	struct tomoyo_request_info r;
1962106ccd9STetsuo Handa 	int error;
1972106ccd9STetsuo Handa 	int idx;
1982106ccd9STetsuo Handa 
19957c2590fSTetsuo Handa 	if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT)
20057c2590fSTetsuo Handa 	    == TOMOYO_CONFIG_DISABLED)
2012106ccd9STetsuo Handa 		return 0;
202d795ef9eSTetsuo Handa 	if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
203d795ef9eSTetsuo Handa 		flags &= ~MS_MGC_MSK;
204d795ef9eSTetsuo Handa 	if (flags & MS_REMOUNT) {
205b5bc60b4STetsuo Handa 		type = tomoyo_mounts[TOMOYO_MOUNT_REMOUNT];
206d795ef9eSTetsuo Handa 		flags &= ~MS_REMOUNT;
207df91e494STetsuo Handa 	} else if (flags & MS_BIND) {
208b5bc60b4STetsuo Handa 		type = tomoyo_mounts[TOMOYO_MOUNT_BIND];
209d795ef9eSTetsuo Handa 		flags &= ~MS_BIND;
210df91e494STetsuo Handa 	} else if (flags & MS_SHARED) {
211df91e494STetsuo Handa 		if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
212df91e494STetsuo Handa 			return -EINVAL;
213b5bc60b4STetsuo Handa 		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED];
214d795ef9eSTetsuo Handa 		flags &= ~MS_SHARED;
215df91e494STetsuo Handa 	} else if (flags & MS_PRIVATE) {
216df91e494STetsuo Handa 		if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE))
217df91e494STetsuo Handa 			return -EINVAL;
218df91e494STetsuo Handa 		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE];
219df91e494STetsuo Handa 		flags &= ~MS_PRIVATE;
220df91e494STetsuo Handa 	} else if (flags & MS_SLAVE) {
221df91e494STetsuo Handa 		if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE))
222df91e494STetsuo Handa 			return -EINVAL;
223df91e494STetsuo Handa 		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE];
224df91e494STetsuo Handa 		flags &= ~MS_SLAVE;
225df91e494STetsuo Handa 	} else if (flags & MS_UNBINDABLE) {
226df91e494STetsuo Handa 		if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE))
227df91e494STetsuo Handa 			return -EINVAL;
228df91e494STetsuo Handa 		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE];
229df91e494STetsuo Handa 		flags &= ~MS_UNBINDABLE;
230df91e494STetsuo Handa 	} else if (flags & MS_MOVE) {
231df91e494STetsuo Handa 		type = tomoyo_mounts[TOMOYO_MOUNT_MOVE];
232df91e494STetsuo Handa 		flags &= ~MS_MOVE;
233d795ef9eSTetsuo Handa 	}
2342106ccd9STetsuo Handa 	if (!type)
2352106ccd9STetsuo Handa 		type = "<NULL>";
2362106ccd9STetsuo Handa 	idx = tomoyo_read_lock();
2372106ccd9STetsuo Handa 	error = tomoyo_mount_acl(&r, dev_name, path, type, flags);
2382106ccd9STetsuo Handa 	tomoyo_read_unlock(idx);
2392106ccd9STetsuo Handa 	return error;
2402106ccd9STetsuo Handa }
241