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