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