realpath.c 9.49 KB
Newer Older
1
2
3
4
5
6
7
/*
 * security/tomoyo/realpath.c
 *
 * Get the canonicalized absolute pathnames. The basis for TOMOYO.
 *
 * Copyright (C) 2005-2009  NTT DATA CORPORATION
 *
Tetsuo Handa's avatar
Tetsuo Handa committed
8
 * Version: 2.2.0   2009/04/01
9
10
11
12
13
14
 *
 */

#include <linux/types.h>
#include <linux/mount.h>
#include <linux/mnt_namespace.h>
15
#include <linux/fs_struct.h>
16
#include <linux/hash.h>
17
#include <linux/magic.h>
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include "common.h"

/**
 * tomoyo_encode: Convert binary string to ascii string.
 *
 * @buffer:  Buffer for ASCII string.
 * @buflen:  Size of @buffer.
 * @str:     Binary string.
 *
 * Returns 0 on success, -ENOMEM otherwise.
 */
int tomoyo_encode(char *buffer, int buflen, const char *str)
{
	while (1) {
		const unsigned char c = *(unsigned char *) str++;

		if (tomoyo_is_valid(c)) {
			if (--buflen <= 0)
				break;
			*buffer++ = (char) c;
			if (c != '\\')
				continue;
			if (--buflen <= 0)
				break;
			*buffer++ = (char) c;
			continue;
		}
		if (!c) {
			if (--buflen <= 0)
				break;
			*buffer = '\0';
			return 0;
		}
		buflen -= 4;
		if (buflen <= 0)
			break;
		*buffer++ = '\\';
		*buffer++ = (c >> 6) + '0';
		*buffer++ = ((c >> 3) & 7) + '0';
		*buffer++ = (c & 7) + '0';
	}
	return -ENOMEM;
}

/**
 * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root.
 *
 * @path:        Pointer to "struct path".
 * @newname:     Pointer to buffer to return value in.
 * @newname_len: Size of @newname.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * 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.
 */
int tomoyo_realpath_from_path2(struct path *path, char *newname,
			       int newname_len)
{
	int error = -ENOMEM;
	struct dentry *dentry = path->dentry;
	char *sp;

	if (!dentry || !path->mnt || !newname || newname_len <= 2048)
		return -EINVAL;
	if (dentry->d_op && dentry->d_op->d_dname) {
		/* For "socket:[\$]" and "pipe:[\$]". */
		static const int offset = 1536;
		sp = dentry->d_op->d_dname(dentry, newname + offset,
					   newname_len - offset);
	} else {
		/* Taken from d_namespace_path(). */
		struct path root;
		struct path ns_root = { };
		struct path tmp;

		read_lock(&current->fs->lock);
		root = current->fs->root;
		path_get(&root);
		read_unlock(&current->fs->lock);
		spin_lock(&vfsmount_lock);
		if (root.mnt && root.mnt->mnt_ns)
			ns_root.mnt = mntget(root.mnt->mnt_ns->root);
		if (ns_root.mnt)
			ns_root.dentry = dget(ns_root.mnt->mnt_root);
		spin_unlock(&vfsmount_lock);
		spin_lock(&dcache_lock);
		tmp = ns_root;
		sp = __d_path(path, &tmp, newname, newname_len);
		spin_unlock(&dcache_lock);
		path_put(&root);
		path_put(&ns_root);
112
113
		/* Prepend "/proc" prefix if using internal proc vfs mount. */
		if (!IS_ERR(sp) && (path->mnt->mnt_parent == path->mnt) &&
114
		    (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
115
116
117
118
119
120
			sp -= 5;
			if (sp >= newname)
				memcpy(sp, "/proc", 5);
			else
				sp = ERR_PTR(-ENOMEM);
		}
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
	}
	if (IS_ERR(sp))
		error = PTR_ERR(sp);
	else
		error = tomoyo_encode(newname, sp - newname, sp);
	/* Append trailing '/' if dentry is a directory. */
	if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)
	    && *newname) {
		sp = newname + strlen(newname);
		if (*(sp - 1) != '/') {
			if (sp < newname + newname_len - 4) {
				*sp++ = '/';
				*sp = '\0';
			} else {
				error = -ENOMEM;
			}
		}
	}
	if (error)
		printk(KERN_WARNING "tomoyo_realpath: Pathname too long.\n");
	return error;
}

/**
 * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
 *
 * @path: Pointer to "struct path".
 *
 * Returns the realpath of the given @path on success, NULL otherwise.
 *
151
 * These functions use kzalloc(), so the caller must call kfree()
152
153
154
155
 * if these functions didn't return NULL.
 */
char *tomoyo_realpath_from_path(struct path *path)
{
156
	char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_KERNEL);
157
158
159
160
161
162
163
164

	BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer)
		     <= TOMOYO_MAX_PATHNAME_LEN - 1);
	if (!buf)
		return NULL;
	if (tomoyo_realpath_from_path2(path, buf,
				       TOMOYO_MAX_PATHNAME_LEN - 1) == 0)
		return buf;
165
	kfree(buf);
166
167
168
169
170
171
172
173
174
175
176
177
	return NULL;
}

/**
 * tomoyo_realpath - Get realpath of a pathname.
 *
 * @pathname: The pathname to solve.
 *
 * Returns the realpath of @pathname on success, NULL otherwise.
 */
char *tomoyo_realpath(const char *pathname)
{
Al Viro's avatar
Al Viro committed
178
	struct path path;
179

Al Viro's avatar
Al Viro committed
180
181
182
	if (pathname && kern_path(pathname, LOOKUP_FOLLOW, &path) == 0) {
		char *buf = tomoyo_realpath_from_path(&path);
		path_put(&path);
183
184
185
186
187
188
189
190
191
192
193
194
195
196
		return buf;
	}
	return NULL;
}

/**
 * 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)
{
Al Viro's avatar
Al Viro committed
197
	struct path path;
198

Al Viro's avatar
Al Viro committed
199
200
201
	if (pathname && kern_path(pathname, 0, &path) == 0) {
		char *buf = tomoyo_realpath_from_path(&path);
		path_put(&path);
202
203
204
205
206
207
		return buf;
	}
	return NULL;
}

/* Memory allocated for non-string data. */
Tetsuo Handa's avatar
Tetsuo Handa committed
208
209
210
static atomic_t tomoyo_policy_memory_size;
/* Quota for holding policy. */
static unsigned int tomoyo_quota_for_policy;
211
212

/**
213
 * tomoyo_memory_ok - Check memory quota.
214
 *
215
 * @ptr: Pointer to allocated memory.
216
 *
217
 * Returns true on success, false otherwise.
218
 *
219
220
 * Caller holds tomoyo_policy_lock.
 * Memory pointed by @ptr will be zeroed on success.
221
 */
222
bool tomoyo_memory_ok(void *ptr)
223
{
224
	int allocated_len = ptr ? ksize(ptr) : 0;
Tetsuo Handa's avatar
Tetsuo Handa committed
225
226
227
228
	atomic_add(allocated_len, &tomoyo_policy_memory_size);
	if (ptr && (!tomoyo_quota_for_policy ||
		    atomic_read(&tomoyo_policy_memory_size)
		    <= tomoyo_quota_for_policy)) {
229
		memset(ptr, 0, allocated_len);
Tetsuo Handa's avatar
Tetsuo Handa committed
230
		return true;
231
	}
Tetsuo Handa's avatar
Tetsuo Handa committed
232
233
234
235
236
	printk(KERN_WARNING "ERROR: Out of memory "
	       "for tomoyo_alloc_element().\n");
	if (!tomoyo_policy_loaded)
		panic("MAC Initialization failed.\n");
	return false;
237
238
}

Tetsuo Handa's avatar
Tetsuo Handa committed
239
240
241
242
243
244
245
246
247
248
/**
 * tomoyo_memory_free - Free memory for elements.
 *
 * @ptr:  Pointer to allocated memory.
 */
void tomoyo_memory_free(void *ptr)
{
	atomic_sub(ksize(ptr), &tomoyo_policy_memory_size);
	kfree(ptr);
}
249
250

/*
251
252
253
254
 * tomoyo_name_list is used for holding string data used by TOMOYO.
 * Since same string data is likely used for multiple times (e.g.
 * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of
 * "const struct tomoyo_path_info *".
255
 */
Tetsuo Handa's avatar
Tetsuo Handa committed
256
257
258
struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
/* Lock for protecting tomoyo_name_list . */
DEFINE_MUTEX(tomoyo_name_list_lock);
259
260

/**
261
 * tomoyo_get_name - Allocate permanent memory for string data.
262
263
264
265
266
 *
 * @name: The string to store into the permernent memory.
 *
 * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
 */
267
const struct tomoyo_path_info *tomoyo_get_name(const char *name)
268
269
270
271
{
	struct tomoyo_name_entry *ptr;
	unsigned int hash;
	int len;
272
	int allocated_len;
273
	struct list_head *head;
274
275
276
277
278

	if (!name)
		return NULL;
	len = strlen(name) + 1;
	hash = full_name_hash((const unsigned char *) name, len - 1);
279
	head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)];
Tetsuo Handa's avatar
Tetsuo Handa committed
280
	mutex_lock(&tomoyo_name_list_lock);
281
	list_for_each_entry(ptr, head, list) {
282
283
284
285
		if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
			continue;
		atomic_inc(&ptr->users);
		goto out;
286
	}
287
288
	ptr = kzalloc(sizeof(*ptr) + len, GFP_KERNEL);
	allocated_len = ptr ? ksize(ptr) : 0;
Tetsuo Handa's avatar
Tetsuo Handa committed
289
290
291
	if (!ptr || (tomoyo_quota_for_policy &&
		     atomic_read(&tomoyo_policy_memory_size) + allocated_len
		     > tomoyo_quota_for_policy)) {
292
		kfree(ptr);
293
		printk(KERN_WARNING "ERROR: Out of memory "
294
		       "for tomoyo_get_name().\n");
295
296
297
298
299
		if (!tomoyo_policy_loaded)
			panic("MAC Initialization failed.\n");
		ptr = NULL;
		goto out;
	}
Tetsuo Handa's avatar
Tetsuo Handa committed
300
	atomic_add(allocated_len, &tomoyo_policy_memory_size);
301
302
	ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
	memmove((char *) ptr->entry.name, name, len);
303
	atomic_set(&ptr->users, 1);
304
	tomoyo_fill_path_info(&ptr->entry);
305
	list_add_tail(&ptr->list, head);
306
 out:
Tetsuo Handa's avatar
Tetsuo Handa committed
307
	mutex_unlock(&tomoyo_name_list_lock);
308
309
310
311
312
313
	return ptr ? &ptr->entry : NULL;
}

/**
 * tomoyo_realpath_init - Initialize realpath related code.
 */
314
void __init tomoyo_realpath_init(void)
315
316
317
318
319
320
321
{
	int i;

	BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX);
	for (i = 0; i < TOMOYO_MAX_HASH; i++)
		INIT_LIST_HEAD(&tomoyo_name_list[i]);
	INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
322
	tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
323
324
325
326
327
	/*
	 * tomoyo_read_lock() is not needed because this function is
	 * called before the first "delete" request.
	 */
	list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
328
329
330
331
332
333
334
335
336
337
338
339
340
341
	if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
		panic("Can't register tomoyo_kernel_domain");
}

/**
 * tomoyo_read_memory_counter - Check for memory usage in bytes.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns memory usage.
 */
int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
{
	if (!head->read_eof) {
Tetsuo Handa's avatar
Tetsuo Handa committed
342
343
		const unsigned int policy
			= atomic_read(&tomoyo_policy_memory_size);
344
345
346
		char buffer[64];

		memset(buffer, 0, sizeof(buffer));
Tetsuo Handa's avatar
Tetsuo Handa committed
347
		if (tomoyo_quota_for_policy)
348
349
			snprintf(buffer, sizeof(buffer) - 1,
				 "   (Quota: %10u)",
Tetsuo Handa's avatar
Tetsuo Handa committed
350
				 tomoyo_quota_for_policy);
351
352
		else
			buffer[0] = '\0';
Tetsuo Handa's avatar
Tetsuo Handa committed
353
354
		tomoyo_io_printf(head, "Policy:  %10u%s\n", policy, buffer);
		tomoyo_io_printf(head, "Total:   %10u\n", policy);
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
		head->read_eof = true;
	}
	return 0;
}

/**
 * tomoyo_write_memory_quota - Set memory quota.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 *
 * Returns 0.
 */
int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
{
	char *data = head->write_buf;
	unsigned int size;

Tetsuo Handa's avatar
Tetsuo Handa committed
372
373
	if (sscanf(data, "Policy: %u", &size) == 1)
		tomoyo_quota_for_policy = size;
374
375
	return 0;
}