Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/security/tomoyo/realpath.c
26378 views
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 = dentry->d_sb;
244
245
while (1) {
246
char *pos;
247
struct inode *inode;
248
249
buf_len <<= 1;
250
kfree(buf);
251
buf = kmalloc(buf_len, GFP_NOFS);
252
if (!buf)
253
break;
254
/* To make sure that pos is '\0' terminated. */
255
buf[buf_len - 1] = '\0';
256
/* For "pipe:[\$]" and "socket:[\$]". */
257
if (dentry->d_op && dentry->d_op->d_dname) {
258
pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
259
goto encode;
260
}
261
inode = d_backing_inode(sb->s_root);
262
/*
263
* Get local name for filesystems without rename() operation
264
*/
265
if ((!inode->i_op->rename &&
266
!(sb->s_type->fs_flags & FS_REQUIRES_DEV)))
267
pos = tomoyo_get_local_path(path->dentry, buf,
268
buf_len - 1);
269
/* Get absolute name for the rest. */
270
else {
271
pos = tomoyo_get_absolute_path(path, buf, buf_len - 1);
272
/*
273
* Fall back to local name if absolute name is not
274
* available.
275
*/
276
if (pos == ERR_PTR(-EINVAL))
277
pos = tomoyo_get_local_path(path->dentry, buf,
278
buf_len - 1);
279
}
280
encode:
281
if (IS_ERR(pos))
282
continue;
283
name = tomoyo_encode(pos);
284
break;
285
}
286
kfree(buf);
287
if (!name)
288
tomoyo_warn_oom(__func__);
289
return name;
290
}
291
292
/**
293
* tomoyo_realpath_nofollow - Get realpath of a pathname.
294
*
295
* @pathname: The pathname to solve.
296
*
297
* Returns the realpath of @pathname on success, NULL otherwise.
298
*/
299
char *tomoyo_realpath_nofollow(const char *pathname)
300
{
301
struct path path;
302
303
if (pathname && kern_path(pathname, 0, &path) == 0) {
304
char *buf = tomoyo_realpath_from_path(&path);
305
306
path_put(&path);
307
return buf;
308
}
309
return NULL;
310
}
311
312