Merge tag 'fbdev-v5.1' of git://github.com/bzolnier/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
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_get_socket_name - Get the name of a socket.
222  *
223  * @path:   Pointer to "struct path".
224  * @buffer: Pointer to buffer to return value in.
225  * @buflen: Sizeof @buffer.
226  *
227  * Returns the buffer.
228  */
229 static char *tomoyo_get_socket_name(const struct path *path, char * const buffer,
230                                     const int buflen)
231 {
232         struct inode *inode = d_backing_inode(path->dentry);
233         struct socket *sock = inode ? SOCKET_I(inode) : NULL;
234         struct sock *sk = sock ? sock->sk : NULL;
235
236         if (sk) {
237                 snprintf(buffer, buflen, "socket:[family=%u:type=%u:protocol=%u]",
238                          sk->sk_family, sk->sk_type, sk->sk_protocol);
239         } else {
240                 snprintf(buffer, buflen, "socket:[unknown]");
241         }
242         return buffer;
243 }
244
245 /**
246  * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
247  *
248  * @path: Pointer to "struct path".
249  *
250  * Returns the realpath of the given @path on success, NULL otherwise.
251  *
252  * If dentry is a directory, trailing '/' is appended.
253  * Characters out of 0x20 < c < 0x7F range are converted to
254  * \ooo style octal string.
255  * Character \ is converted to \\ string.
256  *
257  * These functions use kzalloc(), so the caller must call kfree()
258  * if these functions didn't return NULL.
259  */
260 char *tomoyo_realpath_from_path(const struct path *path)
261 {
262         char *buf = NULL;
263         char *name = NULL;
264         unsigned int buf_len = PAGE_SIZE / 2;
265         struct dentry *dentry = path->dentry;
266         struct super_block *sb;
267
268         if (!dentry)
269                 return NULL;
270         sb = dentry->d_sb;
271         while (1) {
272                 char *pos;
273                 struct inode *inode;
274
275                 buf_len <<= 1;
276                 kfree(buf);
277                 buf = kmalloc(buf_len, GFP_NOFS);
278                 if (!buf)
279                         break;
280                 /* To make sure that pos is '\0' terminated. */
281                 buf[buf_len - 1] = '\0';
282                 /* Get better name for socket. */
283                 if (sb->s_magic == SOCKFS_MAGIC) {
284                         pos = tomoyo_get_socket_name(path, buf, buf_len - 1);
285                         goto encode;
286                 }
287                 /* For "pipe:[\$]". */
288                 if (dentry->d_op && dentry->d_op->d_dname) {
289                         pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
290                         goto encode;
291                 }
292                 inode = d_backing_inode(sb->s_root);
293                 /*
294                  * Get local name for filesystems without rename() operation
295                  * or dentry without vfsmount.
296                  */
297                 if (!path->mnt ||
298                     (!inode->i_op->rename))
299                         pos = tomoyo_get_local_path(path->dentry, buf,
300                                                     buf_len - 1);
301                 /* Get absolute name for the rest. */
302                 else {
303                         pos = tomoyo_get_absolute_path(path, buf, buf_len - 1);
304                         /*
305                          * Fall back to local name if absolute name is not
306                          * available.
307                          */
308                         if (pos == ERR_PTR(-EINVAL))
309                                 pos = tomoyo_get_local_path(path->dentry, buf,
310                                                             buf_len - 1);
311                 }
312 encode:
313                 if (IS_ERR(pos))
314                         continue;
315                 name = tomoyo_encode(pos);
316                 break;
317         }
318         kfree(buf);
319         if (!name)
320                 tomoyo_warn_oom(__func__);
321         return name;
322 }
323
324 /**
325  * tomoyo_realpath_nofollow - Get realpath of a pathname.
326  *
327  * @pathname: The pathname to solve.
328  *
329  * Returns the realpath of @pathname on success, NULL otherwise.
330  */
331 char *tomoyo_realpath_nofollow(const char *pathname)
332 {
333         struct path path;
334
335         if (pathname && kern_path(pathname, 0, &path) == 0) {
336                 char *buf = tomoyo_realpath_from_path(&path);
337
338                 path_put(&path);
339                 return buf;
340         }
341         return NULL;
342 }