diff options
author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2010-05-16 21:10:31 -0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2010-08-02 01:33:37 -0400 |
commit | 2106ccd972dcd9fda7df9b181505fac1741b3508 (patch) | |
tree | 4361f9498c303cabc20abc85c1b5ee0afa677b0f /security/tomoyo/mount.c | |
parent | a1f9bb6a375a8dbf7797ffbd6739c46b338a77f7 (diff) |
TOMOYO: Add mount restriction.
mount(2) has three string and one numeric parameters.
Split mount restriction code from security/tomoyo/file.c .
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/mount.c')
-rw-r--r-- | security/tomoyo/mount.c | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c new file mode 100644 index 000000000000..507be09e93a9 --- /dev/null +++ b/security/tomoyo/mount.c | |||
@@ -0,0 +1,366 @@ | |||
1 | /* | ||
2 | * security/tomoyo/mount.c | ||
3 | * | ||
4 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
5 | */ | ||
6 | |||
7 | #include <linux/slab.h> | ||
8 | #include "common.h" | ||
9 | |||
10 | /* Keywords for mount restrictions. */ | ||
11 | |||
12 | /* Allow to call 'mount --bind /source_dir /dest_dir' */ | ||
13 | #define TOMOYO_MOUNT_BIND_KEYWORD "--bind" | ||
14 | /* Allow to call 'mount --move /old_dir /new_dir ' */ | ||
15 | #define TOMOYO_MOUNT_MOVE_KEYWORD "--move" | ||
16 | /* Allow to call 'mount -o remount /dir ' */ | ||
17 | #define TOMOYO_MOUNT_REMOUNT_KEYWORD "--remount" | ||
18 | /* Allow to call 'mount --make-unbindable /dir' */ | ||
19 | #define TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable" | ||
20 | /* Allow to call 'mount --make-private /dir' */ | ||
21 | #define TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD "--make-private" | ||
22 | /* Allow to call 'mount --make-slave /dir' */ | ||
23 | #define TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD "--make-slave" | ||
24 | /* Allow to call 'mount --make-shared /dir' */ | ||
25 | #define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD "--make-shared" | ||
26 | |||
27 | /** | ||
28 | * tomoyo_encode2: Encode binary string to ascii string. | ||
29 | * | ||
30 | * @str: String in binary format. | ||
31 | * | ||
32 | * Returns pointer to @str in ascii format on success, NULL otherwise. | ||
33 | * | ||
34 | * This function uses kzalloc(), so caller must kfree() if this function | ||
35 | * didn't return NULL. | ||
36 | */ | ||
37 | static char *tomoyo_encode2(const char *str) | ||
38 | { | ||
39 | int len = 0; | ||
40 | const char *p = str; | ||
41 | char *cp; | ||
42 | char *cp0; | ||
43 | if (!p) | ||
44 | return NULL; | ||
45 | while (*p) { | ||
46 | const unsigned char c = *p++; | ||
47 | if (c == '\\') | ||
48 | len += 2; | ||
49 | else if (c > ' ' && c < 127) | ||
50 | len++; | ||
51 | else | ||
52 | len += 4; | ||
53 | } | ||
54 | len++; | ||
55 | /* Reserve space for appending "/". */ | ||
56 | cp = kzalloc(len + 10, GFP_NOFS); | ||
57 | if (!cp) | ||
58 | return NULL; | ||
59 | cp0 = cp; | ||
60 | p = str; | ||
61 | while (*p) { | ||
62 | const unsigned char c = *p++; | ||
63 | if (c == '\\') { | ||
64 | *cp++ = '\\'; | ||
65 | *cp++ = '\\'; | ||
66 | } else if (c > ' ' && c < 127) { | ||
67 | *cp++ = c; | ||
68 | } else { | ||
69 | *cp++ = '\\'; | ||
70 | *cp++ = (c >> 6) + '0'; | ||
71 | *cp++ = ((c >> 3) & 7) + '0'; | ||
72 | *cp++ = (c & 7) + '0'; | ||
73 | } | ||
74 | } | ||
75 | return cp0; | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * tomoyo_mount_acl2 - Check permission for mount() operation. | ||
80 | * | ||
81 | * @r: Pointer to "struct tomoyo_request_info". | ||
82 | * @dev_name: Name of device file. | ||
83 | * @dir: Pointer to "struct path". | ||
84 | * @type: Name of filesystem type. | ||
85 | * @flags: Mount options. | ||
86 | * | ||
87 | * Returns 0 on success, negative value otherwise. | ||
88 | * | ||
89 | * Caller holds tomoyo_read_lock(). | ||
90 | */ | ||
91 | static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, | ||
92 | struct path *dir, char *type, unsigned long flags) | ||
93 | { | ||
94 | struct path path; | ||
95 | struct tomoyo_acl_info *ptr; | ||
96 | struct file_system_type *fstype = NULL; | ||
97 | const char *requested_type = NULL; | ||
98 | const char *requested_dir_name = NULL; | ||
99 | const char *requested_dev_name = NULL; | ||
100 | struct tomoyo_path_info rtype; | ||
101 | struct tomoyo_path_info rdev; | ||
102 | struct tomoyo_path_info rdir; | ||
103 | int need_dev = 0; | ||
104 | int error = -ENOMEM; | ||
105 | |||
106 | /* Get fstype. */ | ||
107 | requested_type = tomoyo_encode2(type); | ||
108 | if (!requested_type) | ||
109 | goto out; | ||
110 | rtype.name = requested_type; | ||
111 | tomoyo_fill_path_info(&rtype); | ||
112 | |||
113 | /* Get mount point. */ | ||
114 | requested_dir_name = tomoyo_realpath_from_path(dir); | ||
115 | if (!requested_dir_name) { | ||
116 | error = -ENOMEM; | ||
117 | goto out; | ||
118 | } | ||
119 | rdir.name = requested_dir_name; | ||
120 | tomoyo_fill_path_info(&rdir); | ||
121 | |||
122 | /* Compare fs name. */ | ||
123 | if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) { | ||
124 | /* dev_name is ignored. */ | ||
125 | } else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || | ||
126 | !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || | ||
127 | !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || | ||
128 | !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) { | ||
129 | /* dev_name is ignored. */ | ||
130 | } else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) || | ||
131 | !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) { | ||
132 | need_dev = -1; /* dev_name is a directory */ | ||
133 | } else { | ||
134 | fstype = get_fs_type(type); | ||
135 | if (!fstype) { | ||
136 | error = -ENODEV; | ||
137 | goto out; | ||
138 | } | ||
139 | if (fstype->fs_flags & FS_REQUIRES_DEV) | ||
140 | /* dev_name is a block device file. */ | ||
141 | need_dev = 1; | ||
142 | } | ||
143 | if (need_dev) { | ||
144 | /* Get mount point or device file. */ | ||
145 | if (kern_path(dev_name, LOOKUP_FOLLOW, &path)) { | ||
146 | error = -ENOENT; | ||
147 | goto out; | ||
148 | } | ||
149 | requested_dev_name = tomoyo_realpath_from_path(&path); | ||
150 | if (!requested_dev_name) { | ||
151 | error = -ENOENT; | ||
152 | goto out; | ||
153 | } | ||
154 | } else { | ||
155 | /* Map dev_name to "<NULL>" if no dev_name given. */ | ||
156 | if (!dev_name) | ||
157 | dev_name = "<NULL>"; | ||
158 | requested_dev_name = tomoyo_encode2(dev_name); | ||
159 | if (!requested_dev_name) { | ||
160 | error = -ENOMEM; | ||
161 | goto out; | ||
162 | } | ||
163 | } | ||
164 | rdev.name = requested_dev_name; | ||
165 | tomoyo_fill_path_info(&rdev); | ||
166 | list_for_each_entry_rcu(ptr, &r->domain->acl_info_list, list) { | ||
167 | struct tomoyo_mount_acl *acl; | ||
168 | if (ptr->type != TOMOYO_TYPE_MOUNT_ACL) | ||
169 | continue; | ||
170 | acl = container_of(ptr, struct tomoyo_mount_acl, head); | ||
171 | if (acl->is_deleted || | ||
172 | !tomoyo_compare_number_union(flags, &acl->flags) || | ||
173 | !tomoyo_compare_name_union(&rtype, &acl->fs_type) || | ||
174 | !tomoyo_compare_name_union(&rdir, &acl->dir_name) || | ||
175 | (need_dev && | ||
176 | !tomoyo_compare_name_union(&rdev, &acl->dev_name))) | ||
177 | continue; | ||
178 | error = 0; | ||
179 | break; | ||
180 | } | ||
181 | if (error) { | ||
182 | const char *dev = tomoyo_get_file_pattern(&rdev)->name; | ||
183 | const char *dir = tomoyo_get_file_pattern(&rdir)->name; | ||
184 | int len = strlen(dev) + strlen(dir) + strlen(requested_type) | ||
185 | + 64; | ||
186 | char *buf = kzalloc(len, GFP_NOFS); | ||
187 | if (buf) { | ||
188 | snprintf(buf, len - 1, "%s %s %s 0x%lX", | ||
189 | dev, dir, requested_type, flags); | ||
190 | tomoyo_write_mount_policy(buf, r->domain, false); | ||
191 | kfree(buf); | ||
192 | } | ||
193 | } | ||
194 | out: | ||
195 | kfree(requested_dev_name); | ||
196 | kfree(requested_dir_name); | ||
197 | if (fstype) | ||
198 | put_filesystem(fstype); | ||
199 | kfree(requested_type); | ||
200 | return error; | ||
201 | } | ||
202 | |||
203 | /** | ||
204 | * tomoyo_mount_acl - Check permission for mount() operation. | ||
205 | * | ||
206 | * @r: Pointer to "struct tomoyo_request_info". | ||
207 | * @dev_name: Name of device file. | ||
208 | * @dir: Pointer to "struct path". | ||
209 | * @type: Name of filesystem type. | ||
210 | * @flags: Mount options. | ||
211 | * | ||
212 | * Returns 0 on success, negative value otherwise. | ||
213 | * | ||
214 | * Caller holds tomoyo_read_lock(). | ||
215 | */ | ||
216 | static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, | ||
217 | struct path *dir, char *type, unsigned long flags) | ||
218 | { | ||
219 | int error; | ||
220 | error = -EPERM; | ||
221 | if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | ||
222 | flags &= ~MS_MGC_MSK; | ||
223 | switch (flags & (MS_REMOUNT | MS_MOVE | MS_BIND)) { | ||
224 | case MS_REMOUNT: | ||
225 | case MS_MOVE: | ||
226 | case MS_BIND: | ||
227 | case 0: | ||
228 | break; | ||
229 | default: | ||
230 | printk(KERN_WARNING "ERROR: " | ||
231 | "%s%s%sare given for single mount operation.\n", | ||
232 | flags & MS_REMOUNT ? "'remount' " : "", | ||
233 | flags & MS_MOVE ? "'move' " : "", | ||
234 | flags & MS_BIND ? "'bind' " : ""); | ||
235 | return -EINVAL; | ||
236 | } | ||
237 | switch (flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED)) { | ||
238 | case MS_UNBINDABLE: | ||
239 | case MS_PRIVATE: | ||
240 | case MS_SLAVE: | ||
241 | case MS_SHARED: | ||
242 | case 0: | ||
243 | break; | ||
244 | default: | ||
245 | printk(KERN_WARNING "ERROR: " | ||
246 | "%s%s%s%sare given for single mount operation.\n", | ||
247 | flags & MS_UNBINDABLE ? "'unbindable' " : "", | ||
248 | flags & MS_PRIVATE ? "'private' " : "", | ||
249 | flags & MS_SLAVE ? "'slave' " : "", | ||
250 | flags & MS_SHARED ? "'shared' " : ""); | ||
251 | return -EINVAL; | ||
252 | } | ||
253 | if (flags & MS_REMOUNT) | ||
254 | error = tomoyo_mount_acl(r, dev_name, dir, | ||
255 | TOMOYO_MOUNT_REMOUNT_KEYWORD, | ||
256 | flags & ~MS_REMOUNT); | ||
257 | else if (flags & MS_MOVE) | ||
258 | error = tomoyo_mount_acl(r, dev_name, dir, | ||
259 | TOMOYO_MOUNT_MOVE_KEYWORD, | ||
260 | flags & ~MS_MOVE); | ||
261 | else if (flags & MS_BIND) | ||
262 | error = tomoyo_mount_acl(r, dev_name, dir, | ||
263 | TOMOYO_MOUNT_BIND_KEYWORD, | ||
264 | flags & ~MS_BIND); | ||
265 | else if (flags & MS_UNBINDABLE) | ||
266 | error = tomoyo_mount_acl(r, dev_name, dir, | ||
267 | TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD, | ||
268 | flags & ~MS_UNBINDABLE); | ||
269 | else if (flags & MS_PRIVATE) | ||
270 | error = tomoyo_mount_acl(r, dev_name, dir, | ||
271 | TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD, | ||
272 | flags & ~MS_PRIVATE); | ||
273 | else if (flags & MS_SLAVE) | ||
274 | error = tomoyo_mount_acl(r, dev_name, dir, | ||
275 | TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD, | ||
276 | flags & ~MS_SLAVE); | ||
277 | else if (flags & MS_SHARED) | ||
278 | error = tomoyo_mount_acl(r, dev_name, dir, | ||
279 | TOMOYO_MOUNT_MAKE_SHARED_KEYWORD, | ||
280 | flags & ~MS_SHARED); | ||
281 | else | ||
282 | error = tomoyo_mount_acl2(r, dev_name, dir, type, flags); | ||
283 | if (r->mode != TOMOYO_CONFIG_ENFORCING) | ||
284 | error = 0; | ||
285 | return error; | ||
286 | } | ||
287 | |||
288 | /** | ||
289 | * tomoyo_mount_permission - Check permission for mount() operation. | ||
290 | * | ||
291 | * @dev_name: Name of device file. | ||
292 | * @path: Pointer to "struct path". | ||
293 | * @type: Name of filesystem type. May be NULL. | ||
294 | * @flags: Mount options. | ||
295 | * @data_page: Optional data. May be NULL. | ||
296 | * | ||
297 | * Returns 0 on success, negative value otherwise. | ||
298 | */ | ||
299 | int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, | ||
300 | unsigned long flags, void *data_page) | ||
301 | { | ||
302 | struct tomoyo_request_info r; | ||
303 | int error; | ||
304 | int idx; | ||
305 | |||
306 | if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED) | ||
307 | return 0; | ||
308 | if (!type) | ||
309 | type = "<NULL>"; | ||
310 | idx = tomoyo_read_lock(); | ||
311 | error = tomoyo_mount_acl(&r, dev_name, path, type, flags); | ||
312 | tomoyo_read_unlock(idx); | ||
313 | return error; | ||
314 | } | ||
315 | |||
316 | /** | ||
317 | * tomoyo_write_mount_policy - Write "struct tomoyo_mount_acl" list. | ||
318 | * | ||
319 | * @data: String to parse. | ||
320 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
321 | * @is_delete: True if it is a delete request. | ||
322 | * | ||
323 | * Returns 0 on success, negative value otherwise. | ||
324 | */ | ||
325 | int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain, | ||
326 | const bool is_delete) | ||
327 | { | ||
328 | struct tomoyo_acl_info *ptr; | ||
329 | struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; | ||
330 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
331 | char *w[4]; | ||
332 | if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0]) | ||
333 | return -EINVAL; | ||
334 | if (!tomoyo_parse_name_union(w[0], &e.dev_name) || | ||
335 | !tomoyo_parse_name_union(w[1], &e.dir_name) || | ||
336 | !tomoyo_parse_name_union(w[2], &e.fs_type) || | ||
337 | !tomoyo_parse_number_union(w[3], &e.flags)) | ||
338 | goto out; | ||
339 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
340 | goto out; | ||
341 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | ||
342 | struct tomoyo_mount_acl *acl = | ||
343 | container_of(ptr, struct tomoyo_mount_acl, head); | ||
344 | if (!tomoyo_is_same_mount_acl(acl, &e)) | ||
345 | continue; | ||
346 | acl->is_deleted = is_delete; | ||
347 | error = 0; | ||
348 | break; | ||
349 | } | ||
350 | if (!is_delete && error) { | ||
351 | struct tomoyo_mount_acl *entry = | ||
352 | tomoyo_commit_ok(&e, sizeof(e)); | ||
353 | if (entry) { | ||
354 | list_add_tail_rcu(&entry->head.list, | ||
355 | &domain->acl_info_list); | ||
356 | error = 0; | ||
357 | } | ||
358 | } | ||
359 | mutex_unlock(&tomoyo_policy_lock); | ||
360 | out: | ||
361 | tomoyo_put_name_union(&e.dev_name); | ||
362 | tomoyo_put_name_union(&e.dir_name); | ||
363 | tomoyo_put_name_union(&e.fs_type); | ||
364 | tomoyo_put_number_union(&e.flags); | ||
365 | return error; | ||
366 | } | ||