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