realpath.c revision 0617c7ff34dc9b1d641640c3953274bb2dbe21a6
1/*
2 * security/tomoyo/realpath.c
3 *
4 * Pathname calculation functions for TOMOYO.
5 *
6 * Copyright (C) 2005-2010  NTT DATA CORPORATION
7 */
8
9#include <linux/types.h>
10#include <linux/mount.h>
11#include <linux/mnt_namespace.h>
12#include <linux/fs_struct.h>
13#include <linux/magic.h>
14#include <linux/slab.h>
15#include <net/sock.h>
16#include "common.h"
17
18/**
19 * tomoyo_encode: Convert binary string to ascii string.
20 *
21 * @str: String in binary format.
22 *
23 * Returns pointer to @str in ascii format on success, NULL otherwise.
24 *
25 * This function uses kzalloc(), so caller must kfree() if this function
26 * didn't return NULL.
27 */
28char *tomoyo_encode(const char *str)
29{
30	int len = 0;
31	const char *p = str;
32	char *cp;
33	char *cp0;
34
35	if (!p)
36		return NULL;
37	while (*p) {
38		const unsigned char c = *p++;
39		if (c == '\\')
40			len += 2;
41		else if (c > ' ' && c < 127)
42			len++;
43		else
44			len += 4;
45	}
46	len++;
47	/* Reserve space for appending "/". */
48	cp = kzalloc(len + 10, GFP_NOFS);
49	if (!cp)
50		return NULL;
51	cp0 = cp;
52	p = str;
53	while (*p) {
54		const unsigned char c = *p++;
55
56		if (c == '\\') {
57			*cp++ = '\\';
58			*cp++ = '\\';
59		} else if (c > ' ' && c < 127) {
60			*cp++ = c;
61		} else {
62			*cp++ = '\\';
63			*cp++ = (c >> 6) + '0';
64			*cp++ = ((c >> 3) & 7) + '0';
65			*cp++ = (c & 7) + '0';
66		}
67	}
68	return cp0;
69}
70
71/**
72 * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
73 *
74 * @path: Pointer to "struct path".
75 *
76 * Returns the realpath of the given @path on success, NULL otherwise.
77 *
78 * If dentry is a directory, trailing '/' is appended.
79 * Characters out of 0x20 < c < 0x7F range are converted to
80 * \ooo style octal string.
81 * Character \ is converted to \\ string.
82 *
83 * These functions use kzalloc(), so the caller must call kfree()
84 * if these functions didn't return NULL.
85 */
86char *tomoyo_realpath_from_path(struct path *path)
87{
88	char *buf = NULL;
89	char *name = NULL;
90	unsigned int buf_len = PAGE_SIZE / 2;
91	struct dentry *dentry = path->dentry;
92	bool is_dir;
93	if (!dentry)
94		return NULL;
95	is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode);
96	while (1) {
97		struct path ns_root = { .mnt = NULL, .dentry = NULL };
98		char *pos;
99		buf_len <<= 1;
100		kfree(buf);
101		buf = kmalloc(buf_len, GFP_NOFS);
102		if (!buf)
103			break;
104		/* Get better name for socket. */
105		if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) {
106			struct inode *inode = dentry->d_inode;
107			struct socket *sock = inode ? SOCKET_I(inode) : NULL;
108			struct sock *sk = sock ? sock->sk : NULL;
109			if (sk) {
110				snprintf(buf, buf_len - 1, "socket:[family=%u:"
111					 "type=%u:protocol=%u]", sk->sk_family,
112					 sk->sk_type, sk->sk_protocol);
113			} else {
114				snprintf(buf, buf_len - 1, "socket:[unknown]");
115			}
116			name = tomoyo_encode(buf);
117			break;
118		}
119		/* For "socket:[\$]" and "pipe:[\$]". */
120		if (dentry->d_op && dentry->d_op->d_dname) {
121			pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
122			if (IS_ERR(pos))
123				continue;
124			name = tomoyo_encode(pos);
125			break;
126		}
127		/* If we don't have a vfsmount, we can't calculate. */
128		if (!path->mnt)
129			break;
130		spin_lock(&dcache_lock);
131		/* go to whatever namespace root we are under */
132		pos = __d_path(path, &ns_root, buf, buf_len);
133		spin_unlock(&dcache_lock);
134		/* Prepend "/proc" prefix if using internal proc vfs mount. */
135		if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
136		    (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
137			pos -= 5;
138			if (pos >= buf)
139				memcpy(pos, "/proc", 5);
140			else
141				pos = ERR_PTR(-ENOMEM);
142		}
143		if (IS_ERR(pos))
144			continue;
145		name = tomoyo_encode(pos);
146		break;
147	}
148	kfree(buf);
149	if (!name)
150		tomoyo_warn_oom(__func__);
151	else if (is_dir && *name) {
152		/* Append trailing '/' if dentry is a directory. */
153		char *pos = name + strlen(name) - 1;
154		if (*pos != '/')
155			/*
156			 * This is OK because tomoyo_encode() reserves space
157			 * for appending "/".
158			 */
159			*++pos = '/';
160	}
161	return name;
162}
163
164/**
165 * tomoyo_realpath_nofollow - Get realpath of a pathname.
166 *
167 * @pathname: The pathname to solve.
168 *
169 * Returns the realpath of @pathname on success, NULL otherwise.
170 */
171char *tomoyo_realpath_nofollow(const char *pathname)
172{
173	struct path path;
174
175	if (pathname && kern_path(pathname, 0, &path) == 0) {
176		char *buf = tomoyo_realpath_from_path(&path);
177		path_put(&path);
178		return buf;
179	}
180	return NULL;
181}
182