TOMOYO: Support longer pathname.
[linux-2.6.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
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                 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 - Get realpath of a pathname.
166  *
167  * @pathname: The pathname to solve.
168  *
169  * Returns the realpath of @pathname on success, NULL otherwise.
170  */
171 char *tomoyo_realpath(const char *pathname)
172 {
173         struct path path;
174
175         if (pathname && kern_path(pathname, LOOKUP_FOLLOW, &path) == 0) {
176                 char *buf = tomoyo_realpath_from_path(&path);
177                 path_put(&path);
178                 return buf;
179         }
180         return NULL;
181 }
182
183 /**
184  * tomoyo_realpath_nofollow - Get realpath of a pathname.
185  *
186  * @pathname: The pathname to solve.
187  *
188  * Returns the realpath of @pathname on success, NULL otherwise.
189  */
190 char *tomoyo_realpath_nofollow(const char *pathname)
191 {
192         struct path path;
193
194         if (pathname && kern_path(pathname, 0, &path) == 0) {
195                 char *buf = tomoyo_realpath_from_path(&path);
196                 path_put(&path);
197                 return buf;
198         }
199         return NULL;
200 }