xref: /OK3568_Linux_fs/kernel/security/tomoyo/realpath.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * security/tomoyo/realpath.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2005-2011  NTT DATA CORPORATION
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include "common.h"
9*4882a593Smuzhiyun #include <linux/magic.h>
10*4882a593Smuzhiyun #include <linux/proc_fs.h>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun /**
13*4882a593Smuzhiyun  * tomoyo_encode2 - Encode binary string to ascii string.
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  * @str:     String in binary format.
16*4882a593Smuzhiyun  * @str_len: Size of @str in byte.
17*4882a593Smuzhiyun  *
18*4882a593Smuzhiyun  * Returns pointer to @str in ascii format on success, NULL otherwise.
19*4882a593Smuzhiyun  *
20*4882a593Smuzhiyun  * This function uses kzalloc(), so caller must kfree() if this function
21*4882a593Smuzhiyun  * didn't return NULL.
22*4882a593Smuzhiyun  */
tomoyo_encode2(const char * str,int str_len)23*4882a593Smuzhiyun char *tomoyo_encode2(const char *str, int str_len)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun 	int i;
26*4882a593Smuzhiyun 	int len = 0;
27*4882a593Smuzhiyun 	const char *p = str;
28*4882a593Smuzhiyun 	char *cp;
29*4882a593Smuzhiyun 	char *cp0;
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun 	if (!p)
32*4882a593Smuzhiyun 		return NULL;
33*4882a593Smuzhiyun 	for (i = 0; i < str_len; i++) {
34*4882a593Smuzhiyun 		const unsigned char c = p[i];
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 		if (c == '\\')
37*4882a593Smuzhiyun 			len += 2;
38*4882a593Smuzhiyun 		else if (c > ' ' && c < 127)
39*4882a593Smuzhiyun 			len++;
40*4882a593Smuzhiyun 		else
41*4882a593Smuzhiyun 			len += 4;
42*4882a593Smuzhiyun 	}
43*4882a593Smuzhiyun 	len++;
44*4882a593Smuzhiyun 	/* Reserve space for appending "/". */
45*4882a593Smuzhiyun 	cp = kzalloc(len + 10, GFP_NOFS);
46*4882a593Smuzhiyun 	if (!cp)
47*4882a593Smuzhiyun 		return NULL;
48*4882a593Smuzhiyun 	cp0 = cp;
49*4882a593Smuzhiyun 	p = str;
50*4882a593Smuzhiyun 	for (i = 0; i < str_len; i++) {
51*4882a593Smuzhiyun 		const unsigned char c = p[i];
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 		if (c == '\\') {
54*4882a593Smuzhiyun 			*cp++ = '\\';
55*4882a593Smuzhiyun 			*cp++ = '\\';
56*4882a593Smuzhiyun 		} else if (c > ' ' && c < 127) {
57*4882a593Smuzhiyun 			*cp++ = c;
58*4882a593Smuzhiyun 		} else {
59*4882a593Smuzhiyun 			*cp++ = '\\';
60*4882a593Smuzhiyun 			*cp++ = (c >> 6) + '0';
61*4882a593Smuzhiyun 			*cp++ = ((c >> 3) & 7) + '0';
62*4882a593Smuzhiyun 			*cp++ = (c & 7) + '0';
63*4882a593Smuzhiyun 		}
64*4882a593Smuzhiyun 	}
65*4882a593Smuzhiyun 	return cp0;
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun /**
69*4882a593Smuzhiyun  * tomoyo_encode - Encode binary string to ascii string.
70*4882a593Smuzhiyun  *
71*4882a593Smuzhiyun  * @str: String in binary format.
72*4882a593Smuzhiyun  *
73*4882a593Smuzhiyun  * Returns pointer to @str in ascii format on success, NULL otherwise.
74*4882a593Smuzhiyun  *
75*4882a593Smuzhiyun  * This function uses kzalloc(), so caller must kfree() if this function
76*4882a593Smuzhiyun  * didn't return NULL.
77*4882a593Smuzhiyun  */
tomoyo_encode(const char * str)78*4882a593Smuzhiyun char *tomoyo_encode(const char *str)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun 	return str ? tomoyo_encode2(str, strlen(str)) : NULL;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun /**
84*4882a593Smuzhiyun  * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
85*4882a593Smuzhiyun  *
86*4882a593Smuzhiyun  * @path:   Pointer to "struct path".
87*4882a593Smuzhiyun  * @buffer: Pointer to buffer to return value in.
88*4882a593Smuzhiyun  * @buflen: Sizeof @buffer.
89*4882a593Smuzhiyun  *
90*4882a593Smuzhiyun  * Returns the buffer on success, an error code otherwise.
91*4882a593Smuzhiyun  *
92*4882a593Smuzhiyun  * If dentry is a directory, trailing '/' is appended.
93*4882a593Smuzhiyun  */
tomoyo_get_absolute_path(const struct path * path,char * const buffer,const int buflen)94*4882a593Smuzhiyun static char *tomoyo_get_absolute_path(const struct path *path, char * const buffer,
95*4882a593Smuzhiyun 				      const int buflen)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	char *pos = ERR_PTR(-ENOMEM);
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	if (buflen >= 256) {
100*4882a593Smuzhiyun 		/* go to whatever namespace root we are under */
101*4882a593Smuzhiyun 		pos = d_absolute_path(path, buffer, buflen - 1);
102*4882a593Smuzhiyun 		if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
103*4882a593Smuzhiyun 			struct inode *inode = d_backing_inode(path->dentry);
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 			if (inode && S_ISDIR(inode->i_mode)) {
106*4882a593Smuzhiyun 				buffer[buflen - 2] = '/';
107*4882a593Smuzhiyun 				buffer[buflen - 1] = '\0';
108*4882a593Smuzhiyun 			}
109*4882a593Smuzhiyun 		}
110*4882a593Smuzhiyun 	}
111*4882a593Smuzhiyun 	return pos;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun /**
115*4882a593Smuzhiyun  * tomoyo_get_dentry_path - Get the path of a dentry.
116*4882a593Smuzhiyun  *
117*4882a593Smuzhiyun  * @dentry: Pointer to "struct dentry".
118*4882a593Smuzhiyun  * @buffer: Pointer to buffer to return value in.
119*4882a593Smuzhiyun  * @buflen: Sizeof @buffer.
120*4882a593Smuzhiyun  *
121*4882a593Smuzhiyun  * Returns the buffer on success, an error code otherwise.
122*4882a593Smuzhiyun  *
123*4882a593Smuzhiyun  * If dentry is a directory, trailing '/' is appended.
124*4882a593Smuzhiyun  */
tomoyo_get_dentry_path(struct dentry * dentry,char * const buffer,const int buflen)125*4882a593Smuzhiyun static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer,
126*4882a593Smuzhiyun 				    const int buflen)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	char *pos = ERR_PTR(-ENOMEM);
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	if (buflen >= 256) {
131*4882a593Smuzhiyun 		pos = dentry_path_raw(dentry, buffer, buflen - 1);
132*4882a593Smuzhiyun 		if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
133*4882a593Smuzhiyun 			struct inode *inode = d_backing_inode(dentry);
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 			if (inode && S_ISDIR(inode->i_mode)) {
136*4882a593Smuzhiyun 				buffer[buflen - 2] = '/';
137*4882a593Smuzhiyun 				buffer[buflen - 1] = '\0';
138*4882a593Smuzhiyun 			}
139*4882a593Smuzhiyun 		}
140*4882a593Smuzhiyun 	}
141*4882a593Smuzhiyun 	return pos;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun /**
145*4882a593Smuzhiyun  * tomoyo_get_local_path - Get the path of a dentry.
146*4882a593Smuzhiyun  *
147*4882a593Smuzhiyun  * @dentry: Pointer to "struct dentry".
148*4882a593Smuzhiyun  * @buffer: Pointer to buffer to return value in.
149*4882a593Smuzhiyun  * @buflen: Sizeof @buffer.
150*4882a593Smuzhiyun  *
151*4882a593Smuzhiyun  * Returns the buffer on success, an error code otherwise.
152*4882a593Smuzhiyun  */
tomoyo_get_local_path(struct dentry * dentry,char * const buffer,const int buflen)153*4882a593Smuzhiyun static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
154*4882a593Smuzhiyun 				   const int buflen)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun 	struct super_block *sb = dentry->d_sb;
157*4882a593Smuzhiyun 	char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen);
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	if (IS_ERR(pos))
160*4882a593Smuzhiyun 		return pos;
161*4882a593Smuzhiyun 	/* Convert from $PID to self if $PID is current thread. */
162*4882a593Smuzhiyun 	if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {
163*4882a593Smuzhiyun 		char *ep;
164*4882a593Smuzhiyun 		const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);
165*4882a593Smuzhiyun 		struct pid_namespace *proc_pidns = proc_pid_ns(sb);
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 		if (*ep == '/' && pid && pid ==
168*4882a593Smuzhiyun 		    task_tgid_nr_ns(current, proc_pidns)) {
169*4882a593Smuzhiyun 			pos = ep - 5;
170*4882a593Smuzhiyun 			if (pos < buffer)
171*4882a593Smuzhiyun 				goto out;
172*4882a593Smuzhiyun 			memmove(pos, "/self", 5);
173*4882a593Smuzhiyun 		}
174*4882a593Smuzhiyun 		goto prepend_filesystem_name;
175*4882a593Smuzhiyun 	}
176*4882a593Smuzhiyun 	/* Use filesystem name for unnamed devices. */
177*4882a593Smuzhiyun 	if (!MAJOR(sb->s_dev))
178*4882a593Smuzhiyun 		goto prepend_filesystem_name;
179*4882a593Smuzhiyun 	{
180*4882a593Smuzhiyun 		struct inode *inode = d_backing_inode(sb->s_root);
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 		/*
183*4882a593Smuzhiyun 		 * Use filesystem name if filesystem does not support rename()
184*4882a593Smuzhiyun 		 * operation.
185*4882a593Smuzhiyun 		 */
186*4882a593Smuzhiyun 		if (!inode->i_op->rename)
187*4882a593Smuzhiyun 			goto prepend_filesystem_name;
188*4882a593Smuzhiyun 	}
189*4882a593Smuzhiyun 	/* Prepend device name. */
190*4882a593Smuzhiyun 	{
191*4882a593Smuzhiyun 		char name[64];
192*4882a593Smuzhiyun 		int name_len;
193*4882a593Smuzhiyun 		const dev_t dev = sb->s_dev;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 		name[sizeof(name) - 1] = '\0';
196*4882a593Smuzhiyun 		snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev),
197*4882a593Smuzhiyun 			 MINOR(dev));
198*4882a593Smuzhiyun 		name_len = strlen(name);
199*4882a593Smuzhiyun 		pos -= name_len;
200*4882a593Smuzhiyun 		if (pos < buffer)
201*4882a593Smuzhiyun 			goto out;
202*4882a593Smuzhiyun 		memmove(pos, name, name_len);
203*4882a593Smuzhiyun 		return pos;
204*4882a593Smuzhiyun 	}
205*4882a593Smuzhiyun 	/* Prepend filesystem name. */
206*4882a593Smuzhiyun prepend_filesystem_name:
207*4882a593Smuzhiyun 	{
208*4882a593Smuzhiyun 		const char *name = sb->s_type->name;
209*4882a593Smuzhiyun 		const int name_len = strlen(name);
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 		pos -= name_len + 1;
212*4882a593Smuzhiyun 		if (pos < buffer)
213*4882a593Smuzhiyun 			goto out;
214*4882a593Smuzhiyun 		memmove(pos, name, name_len);
215*4882a593Smuzhiyun 		pos[name_len] = ':';
216*4882a593Smuzhiyun 	}
217*4882a593Smuzhiyun 	return pos;
218*4882a593Smuzhiyun out:
219*4882a593Smuzhiyun 	return ERR_PTR(-ENOMEM);
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun /**
223*4882a593Smuzhiyun  * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
224*4882a593Smuzhiyun  *
225*4882a593Smuzhiyun  * @path: Pointer to "struct path".
226*4882a593Smuzhiyun  *
227*4882a593Smuzhiyun  * Returns the realpath of the given @path on success, NULL otherwise.
228*4882a593Smuzhiyun  *
229*4882a593Smuzhiyun  * If dentry is a directory, trailing '/' is appended.
230*4882a593Smuzhiyun  * Characters out of 0x20 < c < 0x7F range are converted to
231*4882a593Smuzhiyun  * \ooo style octal string.
232*4882a593Smuzhiyun  * Character \ is converted to \\ string.
233*4882a593Smuzhiyun  *
234*4882a593Smuzhiyun  * These functions use kzalloc(), so the caller must call kfree()
235*4882a593Smuzhiyun  * if these functions didn't return NULL.
236*4882a593Smuzhiyun  */
tomoyo_realpath_from_path(const struct path * path)237*4882a593Smuzhiyun char *tomoyo_realpath_from_path(const struct path *path)
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun 	char *buf = NULL;
240*4882a593Smuzhiyun 	char *name = NULL;
241*4882a593Smuzhiyun 	unsigned int buf_len = PAGE_SIZE / 2;
242*4882a593Smuzhiyun 	struct dentry *dentry = path->dentry;
243*4882a593Smuzhiyun 	struct super_block *sb;
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	if (!dentry)
246*4882a593Smuzhiyun 		return NULL;
247*4882a593Smuzhiyun 	sb = dentry->d_sb;
248*4882a593Smuzhiyun 	while (1) {
249*4882a593Smuzhiyun 		char *pos;
250*4882a593Smuzhiyun 		struct inode *inode;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 		buf_len <<= 1;
253*4882a593Smuzhiyun 		kfree(buf);
254*4882a593Smuzhiyun 		buf = kmalloc(buf_len, GFP_NOFS);
255*4882a593Smuzhiyun 		if (!buf)
256*4882a593Smuzhiyun 			break;
257*4882a593Smuzhiyun 		/* To make sure that pos is '\0' terminated. */
258*4882a593Smuzhiyun 		buf[buf_len - 1] = '\0';
259*4882a593Smuzhiyun 		/* For "pipe:[\$]" and "socket:[\$]". */
260*4882a593Smuzhiyun 		if (dentry->d_op && dentry->d_op->d_dname) {
261*4882a593Smuzhiyun 			pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
262*4882a593Smuzhiyun 			goto encode;
263*4882a593Smuzhiyun 		}
264*4882a593Smuzhiyun 		inode = d_backing_inode(sb->s_root);
265*4882a593Smuzhiyun 		/*
266*4882a593Smuzhiyun 		 * Get local name for filesystems without rename() operation
267*4882a593Smuzhiyun 		 * or dentry without vfsmount.
268*4882a593Smuzhiyun 		 */
269*4882a593Smuzhiyun 		if (!path->mnt ||
270*4882a593Smuzhiyun 		    (!inode->i_op->rename &&
271*4882a593Smuzhiyun 		     !(sb->s_type->fs_flags & FS_REQUIRES_DEV)))
272*4882a593Smuzhiyun 			pos = tomoyo_get_local_path(path->dentry, buf,
273*4882a593Smuzhiyun 						    buf_len - 1);
274*4882a593Smuzhiyun 		/* Get absolute name for the rest. */
275*4882a593Smuzhiyun 		else {
276*4882a593Smuzhiyun 			pos = tomoyo_get_absolute_path(path, buf, buf_len - 1);
277*4882a593Smuzhiyun 			/*
278*4882a593Smuzhiyun 			 * Fall back to local name if absolute name is not
279*4882a593Smuzhiyun 			 * available.
280*4882a593Smuzhiyun 			 */
281*4882a593Smuzhiyun 			if (pos == ERR_PTR(-EINVAL))
282*4882a593Smuzhiyun 				pos = tomoyo_get_local_path(path->dentry, buf,
283*4882a593Smuzhiyun 							    buf_len - 1);
284*4882a593Smuzhiyun 		}
285*4882a593Smuzhiyun encode:
286*4882a593Smuzhiyun 		if (IS_ERR(pos))
287*4882a593Smuzhiyun 			continue;
288*4882a593Smuzhiyun 		name = tomoyo_encode(pos);
289*4882a593Smuzhiyun 		break;
290*4882a593Smuzhiyun 	}
291*4882a593Smuzhiyun 	kfree(buf);
292*4882a593Smuzhiyun 	if (!name)
293*4882a593Smuzhiyun 		tomoyo_warn_oom(__func__);
294*4882a593Smuzhiyun 	return name;
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun /**
298*4882a593Smuzhiyun  * tomoyo_realpath_nofollow - Get realpath of a pathname.
299*4882a593Smuzhiyun  *
300*4882a593Smuzhiyun  * @pathname: The pathname to solve.
301*4882a593Smuzhiyun  *
302*4882a593Smuzhiyun  * Returns the realpath of @pathname on success, NULL otherwise.
303*4882a593Smuzhiyun  */
tomoyo_realpath_nofollow(const char * pathname)304*4882a593Smuzhiyun char *tomoyo_realpath_nofollow(const char *pathname)
305*4882a593Smuzhiyun {
306*4882a593Smuzhiyun 	struct path path;
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	if (pathname && kern_path(pathname, 0, &path) == 0) {
309*4882a593Smuzhiyun 		char *buf = tomoyo_realpath_from_path(&path);
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 		path_put(&path);
312*4882a593Smuzhiyun 		return buf;
313*4882a593Smuzhiyun 	}
314*4882a593Smuzhiyun 	return NULL;
315*4882a593Smuzhiyun }
316