xref: /openbmc/linux/security/tomoyo/realpath.c (revision baa7eb025ab14f3cba2e35c0a8648f9c9f01d24f)
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  */
28 char *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  */
86 char *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 		/* go to whatever namespace root we are under */
131 		pos = __d_path(path, &ns_root, buf, buf_len);
132 		/* Prepend "/proc" prefix if using internal proc vfs mount. */
133 		if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
134 		    (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
135 			pos -= 5;
136 			if (pos >= buf)
137 				memcpy(pos, "/proc", 5);
138 			else
139 				pos = ERR_PTR(-ENOMEM);
140 		}
141 		if (IS_ERR(pos))
142 			continue;
143 		name = tomoyo_encode(pos);
144 		break;
145 	}
146 	kfree(buf);
147 	if (!name)
148 		tomoyo_warn_oom(__func__);
149 	else if (is_dir && *name) {
150 		/* Append trailing '/' if dentry is a directory. */
151 		char *pos = name + strlen(name) - 1;
152 		if (*pos != '/')
153 			/*
154 			 * This is OK because tomoyo_encode() reserves space
155 			 * for appending "/".
156 			 */
157 			*++pos = '/';
158 	}
159 	return name;
160 }
161 
162 /**
163  * tomoyo_realpath_nofollow - Get realpath of a pathname.
164  *
165  * @pathname: The pathname to solve.
166  *
167  * Returns the realpath of @pathname on success, NULL otherwise.
168  */
169 char *tomoyo_realpath_nofollow(const char *pathname)
170 {
171 	struct path path;
172 
173 	if (pathname && kern_path(pathname, 0, &path) == 0) {
174 		char *buf = tomoyo_realpath_from_path(&path);
175 		path_put(&path);
176 		return buf;
177 	}
178 	return NULL;
179 }
180