diff options
author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2010-06-03 07:36:43 -0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2010-08-02 01:33:41 -0400 |
commit | c8c57e842720d8cc92ac8607f2d1c16d92314573 (patch) | |
tree | dc921366b931ba5817ad530433f3b1ee178bc56a /security/tomoyo/realpath.c | |
parent | 9b244373da3eab671da6c5125482121528a9ebf3 (diff) |
TOMOYO: Support longer pathname.
Allow pathnames longer than 4000 bytes.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/tomoyo/realpath.c')
-rw-r--r-- | security/tomoyo/realpath.c | 214 |
1 files changed, 113 insertions, 101 deletions
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 1fd685a94ad1..153fa23a05cc 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c | |||
@@ -12,141 +12,153 @@ | |||
12 | #include <linux/fs_struct.h> | 12 | #include <linux/fs_struct.h> |
13 | #include <linux/magic.h> | 13 | #include <linux/magic.h> |
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <net/sock.h> | ||
15 | #include "common.h" | 16 | #include "common.h" |
16 | 17 | ||
17 | /** | 18 | /** |
18 | * tomoyo_encode: Convert binary string to ascii string. | 19 | * tomoyo_encode: Convert binary string to ascii string. |
19 | * | 20 | * |
20 | * @buffer: Buffer for ASCII string. | 21 | * @str: String in binary format. |
21 | * @buflen: Size of @buffer. | ||
22 | * @str: Binary string. | ||
23 | * | 22 | * |
24 | * Returns 0 on success, -ENOMEM otherwise. | 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. | ||
25 | */ | 27 | */ |
26 | int tomoyo_encode(char *buffer, int buflen, const char *str) | 28 | char *tomoyo_encode(const char *str) |
27 | { | 29 | { |
28 | while (1) { | 30 | int len = 0; |
29 | const unsigned char c = *(unsigned char *) str++; | 31 | const char *p = str; |
32 | char *cp; | ||
33 | char *cp0; | ||
30 | 34 | ||
31 | if (tomoyo_is_valid(c)) { | 35 | if (!p) |
32 | if (--buflen <= 0) | 36 | return NULL; |
33 | break; | 37 | while (*p) { |
34 | *buffer++ = (char) c; | 38 | const unsigned char c = *p++; |
35 | if (c != '\\') | 39 | if (c == '\\') |
36 | continue; | 40 | len += 2; |
37 | if (--buflen <= 0) | 41 | else if (c > ' ' && c < 127) |
38 | break; | 42 | len++; |
39 | *buffer++ = (char) c; | 43 | else |
40 | continue; | 44 | len += 4; |
41 | } | 45 | } |
42 | if (!c) { | 46 | len++; |
43 | if (--buflen <= 0) | 47 | /* Reserve space for appending "/". */ |
44 | break; | 48 | cp = kzalloc(len + 10, GFP_NOFS); |
45 | *buffer = '\0'; | 49 | if (!cp) |
46 | return 0; | 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'; | ||
47 | } | 66 | } |
48 | buflen -= 4; | ||
49 | if (buflen <= 0) | ||
50 | break; | ||
51 | *buffer++ = '\\'; | ||
52 | *buffer++ = (c >> 6) + '0'; | ||
53 | *buffer++ = ((c >> 3) & 7) + '0'; | ||
54 | *buffer++ = (c & 7) + '0'; | ||
55 | } | 67 | } |
56 | return -ENOMEM; | 68 | return cp0; |
57 | } | 69 | } |
58 | 70 | ||
59 | /** | 71 | /** |
60 | * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root. | 72 | * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. |
61 | * | 73 | * |
62 | * @path: Pointer to "struct path". | 74 | * @path: Pointer to "struct path". |
63 | * @newname: Pointer to buffer to return value in. | ||
64 | * @newname_len: Size of @newname. | ||
65 | * | 75 | * |
66 | * Returns 0 on success, negative value otherwise. | 76 | * Returns the realpath of the given @path on success, NULL otherwise. |
67 | * | 77 | * |
68 | * If dentry is a directory, trailing '/' is appended. | 78 | * If dentry is a directory, trailing '/' is appended. |
69 | * Characters out of 0x20 < c < 0x7F range are converted to | 79 | * Characters out of 0x20 < c < 0x7F range are converted to |
70 | * \ooo style octal string. | 80 | * \ooo style octal string. |
71 | * Character \ is converted to \\ 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. | ||
72 | */ | 85 | */ |
73 | int tomoyo_realpath_from_path2(struct path *path, char *newname, | 86 | char *tomoyo_realpath_from_path(struct path *path) |
74 | int newname_len) | ||
75 | { | 87 | { |
76 | int error = -ENOMEM; | 88 | char *buf = NULL; |
89 | char *name = NULL; | ||
90 | unsigned int buf_len = PAGE_SIZE / 2; | ||
77 | struct dentry *dentry = path->dentry; | 91 | struct dentry *dentry = path->dentry; |
78 | char *sp; | 92 | bool is_dir; |
79 | 93 | if (!dentry) | |
80 | if (!dentry || !path->mnt || !newname || newname_len <= 2048) | 94 | return NULL; |
81 | return -EINVAL; | 95 | is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode); |
82 | if (dentry->d_op && dentry->d_op->d_dname) { | 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 | } | ||
83 | /* For "socket:[\$]" and "pipe:[\$]". */ | 119 | /* For "socket:[\$]" and "pipe:[\$]". */ |
84 | static const int offset = 1536; | 120 | if (dentry->d_op && dentry->d_op->d_dname) { |
85 | sp = dentry->d_op->d_dname(dentry, newname + offset, | 121 | pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); |
86 | newname_len - offset); | 122 | if (IS_ERR(pos)) |
87 | } else { | 123 | continue; |
88 | struct path ns_root = {.mnt = NULL, .dentry = NULL}; | 124 | name = tomoyo_encode(pos); |
89 | 125 | break; | |
126 | } | ||
127 | /* If we don't have a vfsmount, we can't calculate. */ | ||
128 | if (!path->mnt) | ||
129 | break; | ||
90 | spin_lock(&dcache_lock); | 130 | spin_lock(&dcache_lock); |
91 | /* go to whatever namespace root we are under */ | 131 | /* go to whatever namespace root we are under */ |
92 | sp = __d_path(path, &ns_root, newname, newname_len); | 132 | pos = __d_path(path, &ns_root, buf, buf_len); |
93 | spin_unlock(&dcache_lock); | 133 | spin_unlock(&dcache_lock); |
94 | /* Prepend "/proc" prefix if using internal proc vfs mount. */ | 134 | /* Prepend "/proc" prefix if using internal proc vfs mount. */ |
95 | if (!IS_ERR(sp) && (path->mnt->mnt_flags & MNT_INTERNAL) && | 135 | if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) && |
96 | (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { | 136 | (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { |
97 | sp -= 5; | 137 | pos -= 5; |
98 | if (sp >= newname) | 138 | if (pos >= buf) |
99 | memcpy(sp, "/proc", 5); | 139 | memcpy(pos, "/proc", 5); |
100 | else | 140 | else |
101 | sp = ERR_PTR(-ENOMEM); | 141 | pos = ERR_PTR(-ENOMEM); |
102 | } | ||
103 | } | ||
104 | if (IS_ERR(sp)) | ||
105 | error = PTR_ERR(sp); | ||
106 | else | ||
107 | error = tomoyo_encode(newname, sp - newname, sp); | ||
108 | /* Append trailing '/' if dentry is a directory. */ | ||
109 | if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode) | ||
110 | && *newname) { | ||
111 | sp = newname + strlen(newname); | ||
112 | if (*(sp - 1) != '/') { | ||
113 | if (sp < newname + newname_len - 4) { | ||
114 | *sp++ = '/'; | ||
115 | *sp = '\0'; | ||
116 | } else { | ||
117 | error = -ENOMEM; | ||
118 | } | ||
119 | } | 142 | } |
143 | if (IS_ERR(pos)) | ||
144 | continue; | ||
145 | name = tomoyo_encode(pos); | ||
146 | break; | ||
120 | } | 147 | } |
121 | if (error) | ||
122 | tomoyo_warn_oom(__func__); | ||
123 | return error; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. | ||
128 | * | ||
129 | * @path: Pointer to "struct path". | ||
130 | * | ||
131 | * Returns the realpath of the given @path on success, NULL otherwise. | ||
132 | * | ||
133 | * These functions use kzalloc(), so the caller must call kfree() | ||
134 | * if these functions didn't return NULL. | ||
135 | */ | ||
136 | char *tomoyo_realpath_from_path(struct path *path) | ||
137 | { | ||
138 | char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_NOFS); | ||
139 | |||
140 | BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX); | ||
141 | BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer) | ||
142 | <= TOMOYO_MAX_PATHNAME_LEN - 1); | ||
143 | if (!buf) | ||
144 | return NULL; | ||
145 | if (tomoyo_realpath_from_path2(path, buf, | ||
146 | TOMOYO_MAX_PATHNAME_LEN - 1) == 0) | ||
147 | return buf; | ||
148 | kfree(buf); | 148 | kfree(buf); |
149 | return NULL; | 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; | ||
150 | } | 162 | } |
151 | 163 | ||
152 | /** | 164 | /** |