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