realpath.c 4.61 KB
Newer Older
1 2 3
/*
 * security/tomoyo/realpath.c
 *
4
 * Pathname calculation functions for TOMOYO.
5
 *
6
 * Copyright (C) 2005-2010  NTT DATA CORPORATION
7 8 9 10 11
 */

#include <linux/types.h>
#include <linux/mount.h>
#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 17 18 19 20
#include "common.h"

/**
 * tomoyo_encode: Convert binary string to ascii string.
 *
21
 * @str: String in binary format.
22
 *
23 24 25 26
 * 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.
27
 */
28
char *tomoyo_encode(const char *str)
29
{
30 31 32 33
	int len = 0;
	const char *p = str;
	char *cp;
	char *cp0;
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
	if (!p)
		return NULL;
	while (*p) {
		const unsigned char c = *p++;
		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;
	while (*p) {
		const unsigned char c = *p++;

		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';
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 79 80 81
 *
 * 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.
82 83 84
 *
 * These functions use kzalloc(), so the caller must call kfree()
 * if these functions didn't return NULL.
85
 */
86
char *tomoyo_realpath_from_path(struct path *path)
87
{
88 89 90
	char *buf = NULL;
	char *name = NULL;
	unsigned int buf_len = PAGE_SIZE / 2;
91
	struct dentry *dentry = path->dentry;
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
	bool is_dir;
	if (!dentry)
		return NULL;
	is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode);
	while (1) {
		struct path ns_root = { .mnt = NULL, .dentry = NULL };
		char *pos;
		buf_len <<= 1;
		kfree(buf);
		buf = kmalloc(buf_len, GFP_NOFS);
		if (!buf)
			break;
		/* Get better name for socket. */
		if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) {
			struct inode *inode = dentry->d_inode;
			struct socket *sock = inode ? SOCKET_I(inode) : NULL;
			struct sock *sk = sock ? sock->sk : NULL;
			if (sk) {
				snprintf(buf, buf_len - 1, "socket:[family=%u:"
					 "type=%u:protocol=%u]", sk->sk_family,
					 sk->sk_type, sk->sk_protocol);
			} else {
				snprintf(buf, buf_len - 1, "socket:[unknown]");
			}
			name = tomoyo_encode(buf);
			break;
		}
119
		/* For "socket:[\$]" and "pipe:[\$]". */
120 121 122 123 124 125 126 127 128 129
		if (dentry->d_op && dentry->d_op->d_dname) {
			pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
			if (IS_ERR(pos))
				continue;
			name = tomoyo_encode(pos);
			break;
		}
		/* If we don't have a vfsmount, we can't calculate. */
		if (!path->mnt)
			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 138 139
			pos -= 5;
			if (pos >= buf)
				memcpy(pos, "/proc", 5);
140
			else
141
				pos = ERR_PTR(-ENOMEM);
142
		}
143 144 145 146
		if (IS_ERR(pos))
			continue;
		name = tomoyo_encode(pos);
		break;
147
	}
148
	kfree(buf);
149 150 151 152 153 154 155 156 157 158 159 160 161
	if (!name)
		tomoyo_warn_oom(__func__);
	else if (is_dir && *name) {
		/* Append trailing '/' if dentry is a directory. */
		char *pos = name + strlen(name) - 1;
		if (*pos != '/')
			/*
			 * This is OK because tomoyo_encode() reserves space
			 * for appending "/".
			 */
			*++pos = '/';
	}
	return name;
162 163 164 165 166 167 168 169 170 171 172
}

/**
 * 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
173
	struct path path;
174

Al Viro's avatar
Al Viro committed
175 176 177
	if (pathname && kern_path(pathname, LOOKUP_FOLLOW, &path) == 0) {
		char *buf = tomoyo_realpath_from_path(&path);
		path_put(&path);
178 179 180 181 182 183 184 185 186 187 188 189 190 191
		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
192
	struct path path;
193

Al Viro's avatar
Al Viro committed
194 195 196
	if (pathname && kern_path(pathname, 0, &path) == 0) {
		char *buf = tomoyo_realpath_from_path(&path);
		path_put(&path);
197 198 199 200
		return buf;
	}
	return NULL;
}