xref: /openbmc/linux/security/tomoyo/realpath.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2c73bd6d4SKentaro Takeda /*
3c73bd6d4SKentaro Takeda  * security/tomoyo/realpath.c
4c73bd6d4SKentaro Takeda  *
50f2a55d5STetsuo Handa  * Copyright (C) 2005-2011  NTT DATA CORPORATION
6c73bd6d4SKentaro Takeda  */
7c73bd6d4SKentaro Takeda 
8c73bd6d4SKentaro Takeda #include "common.h"
9d10577a8SAl Viro #include <linux/magic.h>
10c59f415aSAlexey Gladkov #include <linux/proc_fs.h>
11c73bd6d4SKentaro Takeda 
12c73bd6d4SKentaro Takeda /**
13059d84dbSTetsuo Handa  * tomoyo_encode2 - Encode binary string to ascii string.
14c73bd6d4SKentaro Takeda  *
15c8c57e84STetsuo Handa  * @str:     String in binary format.
16059d84dbSTetsuo Handa  * @str_len: Size of @str in byte.
17c73bd6d4SKentaro Takeda  *
18c8c57e84STetsuo Handa  * Returns pointer to @str in ascii format on success, NULL otherwise.
19c8c57e84STetsuo Handa  *
20c8c57e84STetsuo Handa  * This function uses kzalloc(), so caller must kfree() if this function
21c8c57e84STetsuo Handa  * didn't return NULL.
22c73bd6d4SKentaro Takeda  */
tomoyo_encode2(const char * str,int str_len)23059d84dbSTetsuo Handa char *tomoyo_encode2(const char *str, int str_len)
24c73bd6d4SKentaro Takeda {
25059d84dbSTetsuo Handa 	int i;
26c8c57e84STetsuo Handa 	int len = 0;
27c8c57e84STetsuo Handa 	const char *p = str;
28c8c57e84STetsuo Handa 	char *cp;
29c8c57e84STetsuo Handa 	char *cp0;
30c73bd6d4SKentaro Takeda 
31c8c57e84STetsuo Handa 	if (!p)
32c8c57e84STetsuo Handa 		return NULL;
33059d84dbSTetsuo Handa 	for (i = 0; i < str_len; i++) {
34059d84dbSTetsuo Handa 		const unsigned char c = p[i];
35059d84dbSTetsuo Handa 
36c8c57e84STetsuo Handa 		if (c == '\\')
37c8c57e84STetsuo Handa 			len += 2;
38c8c57e84STetsuo Handa 		else if (c > ' ' && c < 127)
39c8c57e84STetsuo Handa 			len++;
40a4054b6bSEric W. Biederman 		else
41c8c57e84STetsuo Handa 			len += 4;
42a4054b6bSEric W. Biederman 	}
43c8c57e84STetsuo Handa 	len++;
44c8c57e84STetsuo Handa 	/* Reserve space for appending "/". */
45c8c57e84STetsuo Handa 	cp = kzalloc(len + 10, GFP_NOFS);
46c8c57e84STetsuo Handa 	if (!cp)
47c8c57e84STetsuo Handa 		return NULL;
48c8c57e84STetsuo Handa 	cp0 = cp;
49c8c57e84STetsuo Handa 	p = str;
50059d84dbSTetsuo Handa 	for (i = 0; i < str_len; i++) {
51059d84dbSTetsuo Handa 		const unsigned char c = p[i];
52c8c57e84STetsuo Handa 
53c8c57e84STetsuo Handa 		if (c == '\\') {
54c8c57e84STetsuo Handa 			*cp++ = '\\';
55c8c57e84STetsuo Handa 			*cp++ = '\\';
56c8c57e84STetsuo Handa 		} else if (c > ' ' && c < 127) {
57c8c57e84STetsuo Handa 			*cp++ = c;
58c73bd6d4SKentaro Takeda 		} else {
59c8c57e84STetsuo Handa 			*cp++ = '\\';
60c8c57e84STetsuo Handa 			*cp++ = (c >> 6) + '0';
61c8c57e84STetsuo Handa 			*cp++ = ((c >> 3) & 7) + '0';
62c8c57e84STetsuo Handa 			*cp++ = (c & 7) + '0';
63c73bd6d4SKentaro Takeda 		}
64c73bd6d4SKentaro Takeda 	}
65c8c57e84STetsuo Handa 	return cp0;
66c73bd6d4SKentaro Takeda }
67c73bd6d4SKentaro Takeda 
68c73bd6d4SKentaro Takeda /**
69059d84dbSTetsuo Handa  * tomoyo_encode - Encode binary string to ascii string.
70059d84dbSTetsuo Handa  *
71059d84dbSTetsuo Handa  * @str: String in binary format.
72059d84dbSTetsuo Handa  *
73059d84dbSTetsuo Handa  * Returns pointer to @str in ascii format on success, NULL otherwise.
74059d84dbSTetsuo Handa  *
75059d84dbSTetsuo Handa  * This function uses kzalloc(), so caller must kfree() if this function
76059d84dbSTetsuo Handa  * didn't return NULL.
77059d84dbSTetsuo Handa  */
tomoyo_encode(const char * str)78059d84dbSTetsuo Handa char *tomoyo_encode(const char *str)
79059d84dbSTetsuo Handa {
80059d84dbSTetsuo Handa 	return str ? tomoyo_encode2(str, strlen(str)) : NULL;
81059d84dbSTetsuo Handa }
82059d84dbSTetsuo Handa 
83059d84dbSTetsuo Handa /**
845625f2e3STetsuo Handa  * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
855625f2e3STetsuo Handa  *
865625f2e3STetsuo Handa  * @path:   Pointer to "struct path".
875625f2e3STetsuo Handa  * @buffer: Pointer to buffer to return value in.
885625f2e3STetsuo Handa  * @buflen: Sizeof @buffer.
895625f2e3STetsuo Handa  *
905625f2e3STetsuo Handa  * Returns the buffer on success, an error code otherwise.
915625f2e3STetsuo Handa  *
925625f2e3STetsuo Handa  * If dentry is a directory, trailing '/' is appended.
935625f2e3STetsuo Handa  */
tomoyo_get_absolute_path(const struct path * path,char * const buffer,const int buflen)9422473862SAl Viro static char *tomoyo_get_absolute_path(const struct path *path, char * const buffer,
955625f2e3STetsuo Handa 				      const int buflen)
965625f2e3STetsuo Handa {
975625f2e3STetsuo Handa 	char *pos = ERR_PTR(-ENOMEM);
98cdcf6723STetsuo Handa 
995625f2e3STetsuo Handa 	if (buflen >= 256) {
1005625f2e3STetsuo Handa 		/* go to whatever namespace root we are under */
10102125a82SAl Viro 		pos = d_absolute_path(path, buffer, buflen - 1);
1025625f2e3STetsuo Handa 		if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
103c6f493d6SDavid Howells 			struct inode *inode = d_backing_inode(path->dentry);
104cdcf6723STetsuo Handa 
1055625f2e3STetsuo Handa 			if (inode && S_ISDIR(inode->i_mode)) {
1065625f2e3STetsuo Handa 				buffer[buflen - 2] = '/';
1075625f2e3STetsuo Handa 				buffer[buflen - 1] = '\0';
1085625f2e3STetsuo Handa 			}
1095625f2e3STetsuo Handa 		}
1105625f2e3STetsuo Handa 	}
1115625f2e3STetsuo Handa 	return pos;
1125625f2e3STetsuo Handa }
1135625f2e3STetsuo Handa 
1145625f2e3STetsuo Handa /**
1155625f2e3STetsuo Handa  * tomoyo_get_dentry_path - Get the path of a dentry.
1165625f2e3STetsuo Handa  *
1175625f2e3STetsuo Handa  * @dentry: Pointer to "struct dentry".
1185625f2e3STetsuo Handa  * @buffer: Pointer to buffer to return value in.
1195625f2e3STetsuo Handa  * @buflen: Sizeof @buffer.
1205625f2e3STetsuo Handa  *
1215625f2e3STetsuo Handa  * Returns the buffer on success, an error code otherwise.
1225625f2e3STetsuo Handa  *
1235625f2e3STetsuo Handa  * If dentry is a directory, trailing '/' is appended.
1245625f2e3STetsuo Handa  */
tomoyo_get_dentry_path(struct dentry * dentry,char * const buffer,const int buflen)1255625f2e3STetsuo Handa static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer,
1265625f2e3STetsuo Handa 				    const int buflen)
1275625f2e3STetsuo Handa {
1285625f2e3STetsuo Handa 	char *pos = ERR_PTR(-ENOMEM);
129cdcf6723STetsuo Handa 
1305625f2e3STetsuo Handa 	if (buflen >= 256) {
1315625f2e3STetsuo Handa 		pos = dentry_path_raw(dentry, buffer, buflen - 1);
1325625f2e3STetsuo Handa 		if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
133c6f493d6SDavid Howells 			struct inode *inode = d_backing_inode(dentry);
134cdcf6723STetsuo Handa 
1355625f2e3STetsuo Handa 			if (inode && S_ISDIR(inode->i_mode)) {
1365625f2e3STetsuo Handa 				buffer[buflen - 2] = '/';
1375625f2e3STetsuo Handa 				buffer[buflen - 1] = '\0';
1385625f2e3STetsuo Handa 			}
1395625f2e3STetsuo Handa 		}
1405625f2e3STetsuo Handa 	}
1415625f2e3STetsuo Handa 	return pos;
1425625f2e3STetsuo Handa }
1435625f2e3STetsuo Handa 
1445625f2e3STetsuo Handa /**
1455625f2e3STetsuo Handa  * tomoyo_get_local_path - Get the path of a dentry.
1465625f2e3STetsuo Handa  *
1475625f2e3STetsuo Handa  * @dentry: Pointer to "struct dentry".
1485625f2e3STetsuo Handa  * @buffer: Pointer to buffer to return value in.
1495625f2e3STetsuo Handa  * @buflen: Sizeof @buffer.
1505625f2e3STetsuo Handa  *
1515625f2e3STetsuo Handa  * Returns the buffer on success, an error code otherwise.
1525625f2e3STetsuo Handa  */
tomoyo_get_local_path(struct dentry * dentry,char * const buffer,const int buflen)1535625f2e3STetsuo Handa static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
1545625f2e3STetsuo Handa 				   const int buflen)
1555625f2e3STetsuo Handa {
1565625f2e3STetsuo Handa 	struct super_block *sb = dentry->d_sb;
1575625f2e3STetsuo Handa 	char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen);
158cdcf6723STetsuo Handa 
1595625f2e3STetsuo Handa 	if (IS_ERR(pos))
1605625f2e3STetsuo Handa 		return pos;
1615625f2e3STetsuo Handa 	/* Convert from $PID to self if $PID is current thread. */
1625625f2e3STetsuo Handa 	if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {
1635625f2e3STetsuo Handa 		char *ep;
1645625f2e3STetsuo Handa 		const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);
1659d78edeaSAlexey Gladkov 		struct pid_namespace *proc_pidns = proc_pid_ns(sb);
166cdcf6723STetsuo Handa 
1675625f2e3STetsuo Handa 		if (*ep == '/' && pid && pid ==
168c59f415aSAlexey Gladkov 		    task_tgid_nr_ns(current, proc_pidns)) {
1695625f2e3STetsuo Handa 			pos = ep - 5;
1705625f2e3STetsuo Handa 			if (pos < buffer)
1715625f2e3STetsuo Handa 				goto out;
1725625f2e3STetsuo Handa 			memmove(pos, "/self", 5);
1735625f2e3STetsuo Handa 		}
1745625f2e3STetsuo Handa 		goto prepend_filesystem_name;
1755625f2e3STetsuo Handa 	}
1765625f2e3STetsuo Handa 	/* Use filesystem name for unnamed devices. */
1775625f2e3STetsuo Handa 	if (!MAJOR(sb->s_dev))
1785625f2e3STetsuo Handa 		goto prepend_filesystem_name;
1795625f2e3STetsuo Handa 	{
180c6f493d6SDavid Howells 		struct inode *inode = d_backing_inode(sb->s_root);
181cdcf6723STetsuo Handa 
1825625f2e3STetsuo Handa 		/*
1835625f2e3STetsuo Handa 		 * Use filesystem name if filesystem does not support rename()
1845625f2e3STetsuo Handa 		 * operation.
1855625f2e3STetsuo Handa 		 */
1862773bf00SMiklos Szeredi 		if (!inode->i_op->rename)
1875625f2e3STetsuo Handa 			goto prepend_filesystem_name;
1885625f2e3STetsuo Handa 	}
1895625f2e3STetsuo Handa 	/* Prepend device name. */
1905625f2e3STetsuo Handa 	{
1915625f2e3STetsuo Handa 		char name[64];
1925625f2e3STetsuo Handa 		int name_len;
1935625f2e3STetsuo Handa 		const dev_t dev = sb->s_dev;
194cdcf6723STetsuo Handa 
1955625f2e3STetsuo Handa 		name[sizeof(name) - 1] = '\0';
1965625f2e3STetsuo Handa 		snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev),
1975625f2e3STetsuo Handa 			 MINOR(dev));
1985625f2e3STetsuo Handa 		name_len = strlen(name);
1995625f2e3STetsuo Handa 		pos -= name_len;
2005625f2e3STetsuo Handa 		if (pos < buffer)
2015625f2e3STetsuo Handa 			goto out;
2025625f2e3STetsuo Handa 		memmove(pos, name, name_len);
2035625f2e3STetsuo Handa 		return pos;
2045625f2e3STetsuo Handa 	}
2055625f2e3STetsuo Handa 	/* Prepend filesystem name. */
2065625f2e3STetsuo Handa prepend_filesystem_name:
2075625f2e3STetsuo Handa 	{
2085625f2e3STetsuo Handa 		const char *name = sb->s_type->name;
2095625f2e3STetsuo Handa 		const int name_len = strlen(name);
210cdcf6723STetsuo Handa 
2115625f2e3STetsuo Handa 		pos -= name_len + 1;
2125625f2e3STetsuo Handa 		if (pos < buffer)
2135625f2e3STetsuo Handa 			goto out;
2145625f2e3STetsuo Handa 		memmove(pos, name, name_len);
2155625f2e3STetsuo Handa 		pos[name_len] = ':';
2165625f2e3STetsuo Handa 	}
2175625f2e3STetsuo Handa 	return pos;
2185625f2e3STetsuo Handa out:
2195625f2e3STetsuo Handa 	return ERR_PTR(-ENOMEM);
2205625f2e3STetsuo Handa }
2215625f2e3STetsuo Handa 
2225625f2e3STetsuo Handa /**
223c73bd6d4SKentaro Takeda  * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
224c73bd6d4SKentaro Takeda  *
225c73bd6d4SKentaro Takeda  * @path: Pointer to "struct path".
226c73bd6d4SKentaro Takeda  *
227c73bd6d4SKentaro Takeda  * Returns the realpath of the given @path on success, NULL otherwise.
228c73bd6d4SKentaro Takeda  *
229c8c57e84STetsuo Handa  * If dentry is a directory, trailing '/' is appended.
230c8c57e84STetsuo Handa  * Characters out of 0x20 < c < 0x7F range are converted to
231c8c57e84STetsuo Handa  * \ooo style octal string.
232c8c57e84STetsuo Handa  * Character \ is converted to \\ string.
233c8c57e84STetsuo Handa  *
2348e2d39a1STetsuo Handa  * These functions use kzalloc(), so the caller must call kfree()
235c73bd6d4SKentaro Takeda  * if these functions didn't return NULL.
236c73bd6d4SKentaro Takeda  */
tomoyo_realpath_from_path(const struct path * path)23722473862SAl Viro char *tomoyo_realpath_from_path(const struct path *path)
238c73bd6d4SKentaro Takeda {
239c8c57e84STetsuo Handa 	char *buf = NULL;
240c8c57e84STetsuo Handa 	char *name = NULL;
241c8c57e84STetsuo Handa 	unsigned int buf_len = PAGE_SIZE / 2;
242c8c57e84STetsuo Handa 	struct dentry *dentry = path->dentry;
243*467cf8efSAl Viro 	struct super_block *sb = dentry->d_sb;
244cdcf6723STetsuo Handa 
245c8c57e84STetsuo Handa 	while (1) {
246c8c57e84STetsuo Handa 		char *pos;
2475625f2e3STetsuo Handa 		struct inode *inode;
248cdcf6723STetsuo Handa 
249c8c57e84STetsuo Handa 		buf_len <<= 1;
2508e2d39a1STetsuo Handa 		kfree(buf);
251c8c57e84STetsuo Handa 		buf = kmalloc(buf_len, GFP_NOFS);
252c8c57e84STetsuo Handa 		if (!buf)
253c8c57e84STetsuo Handa 			break;
2545625f2e3STetsuo Handa 		/* To make sure that pos is '\0' terminated. */
2555625f2e3STetsuo Handa 		buf[buf_len - 1] = '\0';
2566f7c4137STetsuo Handa 		/* For "pipe:[\$]" and "socket:[\$]". */
257c8c57e84STetsuo Handa 		if (dentry->d_op && dentry->d_op->d_dname) {
258c8c57e84STetsuo Handa 			pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
2595625f2e3STetsuo Handa 			goto encode;
260c8c57e84STetsuo Handa 		}
261c6f493d6SDavid Howells 		inode = d_backing_inode(sb->s_root);
2625625f2e3STetsuo Handa 		/*
2635625f2e3STetsuo Handa 		 * Get local name for filesystems without rename() operation
2645625f2e3STetsuo Handa 		 */
265*467cf8efSAl Viro 		if ((!inode->i_op->rename &&
26627df4b4aSTetsuo Handa 		     !(sb->s_type->fs_flags & FS_REQUIRES_DEV)))
2675625f2e3STetsuo Handa 			pos = tomoyo_get_local_path(path->dentry, buf,
2685625f2e3STetsuo Handa 						    buf_len - 1);
2695625f2e3STetsuo Handa 		/* Get absolute name for the rest. */
2701418a3e5STetsuo Handa 		else {
2715625f2e3STetsuo Handa 			pos = tomoyo_get_absolute_path(path, buf, buf_len - 1);
2721418a3e5STetsuo Handa 			/*
2731418a3e5STetsuo Handa 			 * Fall back to local name if absolute name is not
2741418a3e5STetsuo Handa 			 * available.
2751418a3e5STetsuo Handa 			 */
2761418a3e5STetsuo Handa 			if (pos == ERR_PTR(-EINVAL))
2771418a3e5STetsuo Handa 				pos = tomoyo_get_local_path(path->dentry, buf,
2781418a3e5STetsuo Handa 							    buf_len - 1);
2791418a3e5STetsuo Handa 		}
2805625f2e3STetsuo Handa encode:
281c8c57e84STetsuo Handa 		if (IS_ERR(pos))
282c8c57e84STetsuo Handa 			continue;
283c8c57e84STetsuo Handa 		name = tomoyo_encode(pos);
284c8c57e84STetsuo Handa 		break;
285c8c57e84STetsuo Handa 	}
286c8c57e84STetsuo Handa 	kfree(buf);
287c8c57e84STetsuo Handa 	if (!name)
288c8c57e84STetsuo Handa 		tomoyo_warn_oom(__func__);
289c8c57e84STetsuo Handa 	return name;
290c73bd6d4SKentaro Takeda }
291c73bd6d4SKentaro Takeda 
292c73bd6d4SKentaro Takeda /**
293c73bd6d4SKentaro Takeda  * tomoyo_realpath_nofollow - Get realpath of a pathname.
294c73bd6d4SKentaro Takeda  *
295c73bd6d4SKentaro Takeda  * @pathname: The pathname to solve.
296c73bd6d4SKentaro Takeda  *
297c73bd6d4SKentaro Takeda  * Returns the realpath of @pathname on success, NULL otherwise.
298c73bd6d4SKentaro Takeda  */
tomoyo_realpath_nofollow(const char * pathname)299c73bd6d4SKentaro Takeda char *tomoyo_realpath_nofollow(const char *pathname)
300c73bd6d4SKentaro Takeda {
301e24977d4SAl Viro 	struct path path;
302c73bd6d4SKentaro Takeda 
303e24977d4SAl Viro 	if (pathname && kern_path(pathname, 0, &path) == 0) {
304e24977d4SAl Viro 		char *buf = tomoyo_realpath_from_path(&path);
305cdcf6723STetsuo Handa 
306e24977d4SAl Viro 		path_put(&path);
307c73bd6d4SKentaro Takeda 		return buf;
308c73bd6d4SKentaro Takeda 	}
309c73bd6d4SKentaro Takeda 	return NULL;
310c73bd6d4SKentaro Takeda }
311