Blame view

security/tomoyo/realpath.c 7.84 KB
c73bd6d47   Kentaro Takeda   Memory and pathna...
1
2
3
  /*
   * security/tomoyo/realpath.c
   *
0f2a55d5b   Tetsuo Handa   TOMOYO: Update ke...
4
   * Copyright (C) 2005-2011  NTT DATA CORPORATION
c73bd6d47   Kentaro Takeda   Memory and pathna...
5
   */
c73bd6d47   Kentaro Takeda   Memory and pathna...
6
  #include "common.h"
d10577a8d   Al Viro   vfs: trim include...
7
  #include <linux/magic.h>
c73bd6d47   Kentaro Takeda   Memory and pathna...
8
9
  
  /**
059d84dbb   Tetsuo Handa   TOMOYO: Add socke...
10
   * tomoyo_encode2 - Encode binary string to ascii string.
c73bd6d47   Kentaro Takeda   Memory and pathna...
11
   *
059d84dbb   Tetsuo Handa   TOMOYO: Add socke...
12
13
   * @str:     String in binary format.
   * @str_len: Size of @str in byte.
c73bd6d47   Kentaro Takeda   Memory and pathna...
14
   *
c8c57e842   Tetsuo Handa   TOMOYO: Support l...
15
16
17
18
   * Returns pointer to @str in ascii format on success, NULL otherwise.
   *
   * This function uses kzalloc(), so caller must kfree() if this function
   * didn't return NULL.
c73bd6d47   Kentaro Takeda   Memory and pathna...
19
   */
059d84dbb   Tetsuo Handa   TOMOYO: Add socke...
20
  char *tomoyo_encode2(const char *str, int str_len)
c73bd6d47   Kentaro Takeda   Memory and pathna...
21
  {
059d84dbb   Tetsuo Handa   TOMOYO: Add socke...
22
  	int i;
c8c57e842   Tetsuo Handa   TOMOYO: Support l...
23
24
25
26
  	int len = 0;
  	const char *p = str;
  	char *cp;
  	char *cp0;
c73bd6d47   Kentaro Takeda   Memory and pathna...
27

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

e24977d45   Al Viro   Reduce path_looku...
320
321
322
  	if (pathname && kern_path(pathname, 0, &path) == 0) {
  		char *buf = tomoyo_realpath_from_path(&path);
  		path_put(&path);
c73bd6d47   Kentaro Takeda   Memory and pathna...
323
324
325
326
  		return buf;
  	}
  	return NULL;
  }