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