Commit | Line | Data |
---|---|---|
2106ccd9 TH |
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 | ||
2106ccd9 TH |
27 | /** |
28 | * tomoyo_mount_acl2 - Check permission for mount() operation. | |
29 | * | |
30 | * @r: Pointer to "struct tomoyo_request_info". | |
31 | * @dev_name: Name of device file. | |
32 | * @dir: Pointer to "struct path". | |
33 | * @type: Name of filesystem type. | |
34 | * @flags: Mount options. | |
35 | * | |
36 | * Returns 0 on success, negative value otherwise. | |
37 | * | |
38 | * Caller holds tomoyo_read_lock(). | |
39 | */ | |
40 | static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, | |
41 | struct path *dir, char *type, unsigned long flags) | |
42 | { | |
43 | struct path path; | |
44 | struct tomoyo_acl_info *ptr; | |
45 | struct file_system_type *fstype = NULL; | |
46 | const char *requested_type = NULL; | |
47 | const char *requested_dir_name = NULL; | |
48 | const char *requested_dev_name = NULL; | |
49 | struct tomoyo_path_info rtype; | |
50 | struct tomoyo_path_info rdev; | |
51 | struct tomoyo_path_info rdir; | |
52 | int need_dev = 0; | |
53 | int error = -ENOMEM; | |
54 | ||
55 | /* Get fstype. */ | |
c8c57e84 | 56 | requested_type = tomoyo_encode(type); |
2106ccd9 TH |
57 | if (!requested_type) |
58 | goto out; | |
59 | rtype.name = requested_type; | |
60 | tomoyo_fill_path_info(&rtype); | |
61 | ||
62 | /* Get mount point. */ | |
63 | requested_dir_name = tomoyo_realpath_from_path(dir); | |
64 | if (!requested_dir_name) { | |
65 | error = -ENOMEM; | |
66 | goto out; | |
67 | } | |
68 | rdir.name = requested_dir_name; | |
69 | tomoyo_fill_path_info(&rdir); | |
70 | ||
71 | /* Compare fs name. */ | |
72 | if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) { | |
73 | /* dev_name is ignored. */ | |
74 | } else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || | |
75 | !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || | |
76 | !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || | |
77 | !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) { | |
78 | /* dev_name is ignored. */ | |
79 | } else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) || | |
80 | !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) { | |
81 | need_dev = -1; /* dev_name is a directory */ | |
82 | } else { | |
83 | fstype = get_fs_type(type); | |
84 | if (!fstype) { | |
85 | error = -ENODEV; | |
86 | goto out; | |
87 | } | |
88 | if (fstype->fs_flags & FS_REQUIRES_DEV) | |
89 | /* dev_name is a block device file. */ | |
90 | need_dev = 1; | |
91 | } | |
92 | if (need_dev) { | |
93 | /* Get mount point or device file. */ | |
94 | if (kern_path(dev_name, LOOKUP_FOLLOW, &path)) { | |
95 | error = -ENOENT; | |
96 | goto out; | |
97 | } | |
98 | requested_dev_name = tomoyo_realpath_from_path(&path); | |
99 | if (!requested_dev_name) { | |
100 | error = -ENOENT; | |
101 | goto out; | |
102 | } | |
103 | } else { | |
104 | /* Map dev_name to "<NULL>" if no dev_name given. */ | |
105 | if (!dev_name) | |
106 | dev_name = "<NULL>"; | |
c8c57e84 | 107 | requested_dev_name = tomoyo_encode(dev_name); |
2106ccd9 TH |
108 | if (!requested_dev_name) { |
109 | error = -ENOMEM; | |
110 | goto out; | |
111 | } | |
112 | } | |
113 | rdev.name = requested_dev_name; | |
114 | tomoyo_fill_path_info(&rdev); | |
115 | list_for_each_entry_rcu(ptr, &r->domain->acl_info_list, list) { | |
116 | struct tomoyo_mount_acl *acl; | |
117 | if (ptr->type != TOMOYO_TYPE_MOUNT_ACL) | |
118 | continue; | |
119 | acl = container_of(ptr, struct tomoyo_mount_acl, head); | |
120 | if (acl->is_deleted || | |
121 | !tomoyo_compare_number_union(flags, &acl->flags) || | |
122 | !tomoyo_compare_name_union(&rtype, &acl->fs_type) || | |
123 | !tomoyo_compare_name_union(&rdir, &acl->dir_name) || | |
124 | (need_dev && | |
125 | !tomoyo_compare_name_union(&rdev, &acl->dev_name))) | |
126 | continue; | |
127 | error = 0; | |
128 | break; | |
129 | } | |
17fcfbd9 TH |
130 | if (error) |
131 | error = tomoyo_supervisor(r, TOMOYO_KEYWORD_ALLOW_MOUNT | |
132 | "%s %s %s 0x%lX\n", | |
133 | tomoyo_file_pattern(&rdev), | |
134 | tomoyo_file_pattern(&rdir), | |
135 | requested_type, flags); | |
2106ccd9 TH |
136 | out: |
137 | kfree(requested_dev_name); | |
138 | kfree(requested_dir_name); | |
139 | if (fstype) | |
140 | put_filesystem(fstype); | |
141 | kfree(requested_type); | |
142 | return error; | |
143 | } | |
144 | ||
145 | /** | |
146 | * tomoyo_mount_acl - Check permission for mount() operation. | |
147 | * | |
148 | * @r: Pointer to "struct tomoyo_request_info". | |
149 | * @dev_name: Name of device file. | |
150 | * @dir: Pointer to "struct path". | |
151 | * @type: Name of filesystem type. | |
152 | * @flags: Mount options. | |
153 | * | |
154 | * Returns 0 on success, negative value otherwise. | |
155 | * | |
156 | * Caller holds tomoyo_read_lock(). | |
157 | */ | |
158 | static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, | |
159 | struct path *dir, char *type, unsigned long flags) | |
160 | { | |
161 | int error; | |
162 | error = -EPERM; | |
163 | if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | |
164 | flags &= ~MS_MGC_MSK; | |
165 | switch (flags & (MS_REMOUNT | MS_MOVE | MS_BIND)) { | |
166 | case MS_REMOUNT: | |
167 | case MS_MOVE: | |
168 | case MS_BIND: | |
169 | case 0: | |
170 | break; | |
171 | default: | |
172 | printk(KERN_WARNING "ERROR: " | |
173 | "%s%s%sare given for single mount operation.\n", | |
174 | flags & MS_REMOUNT ? "'remount' " : "", | |
175 | flags & MS_MOVE ? "'move' " : "", | |
176 | flags & MS_BIND ? "'bind' " : ""); | |
177 | return -EINVAL; | |
178 | } | |
179 | switch (flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED)) { | |
180 | case MS_UNBINDABLE: | |
181 | case MS_PRIVATE: | |
182 | case MS_SLAVE: | |
183 | case MS_SHARED: | |
184 | case 0: | |
185 | break; | |
186 | default: | |
187 | printk(KERN_WARNING "ERROR: " | |
188 | "%s%s%s%sare given for single mount operation.\n", | |
189 | flags & MS_UNBINDABLE ? "'unbindable' " : "", | |
190 | flags & MS_PRIVATE ? "'private' " : "", | |
191 | flags & MS_SLAVE ? "'slave' " : "", | |
192 | flags & MS_SHARED ? "'shared' " : ""); | |
193 | return -EINVAL; | |
194 | } | |
195 | if (flags & MS_REMOUNT) | |
196 | error = tomoyo_mount_acl(r, dev_name, dir, | |
197 | TOMOYO_MOUNT_REMOUNT_KEYWORD, | |
198 | flags & ~MS_REMOUNT); | |
199 | else if (flags & MS_MOVE) | |
200 | error = tomoyo_mount_acl(r, dev_name, dir, | |
201 | TOMOYO_MOUNT_MOVE_KEYWORD, | |
202 | flags & ~MS_MOVE); | |
203 | else if (flags & MS_BIND) | |
204 | error = tomoyo_mount_acl(r, dev_name, dir, | |
205 | TOMOYO_MOUNT_BIND_KEYWORD, | |
206 | flags & ~MS_BIND); | |
207 | else if (flags & MS_UNBINDABLE) | |
208 | error = tomoyo_mount_acl(r, dev_name, dir, | |
209 | TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD, | |
210 | flags & ~MS_UNBINDABLE); | |
211 | else if (flags & MS_PRIVATE) | |
212 | error = tomoyo_mount_acl(r, dev_name, dir, | |
213 | TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD, | |
214 | flags & ~MS_PRIVATE); | |
215 | else if (flags & MS_SLAVE) | |
216 | error = tomoyo_mount_acl(r, dev_name, dir, | |
217 | TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD, | |
218 | flags & ~MS_SLAVE); | |
219 | else if (flags & MS_SHARED) | |
220 | error = tomoyo_mount_acl(r, dev_name, dir, | |
221 | TOMOYO_MOUNT_MAKE_SHARED_KEYWORD, | |
222 | flags & ~MS_SHARED); | |
223 | else | |
17fcfbd9 TH |
224 | do { |
225 | error = tomoyo_mount_acl2(r, dev_name, dir, type, | |
226 | flags); | |
227 | } while (error == TOMOYO_RETRY_REQUEST); | |
2106ccd9 TH |
228 | if (r->mode != TOMOYO_CONFIG_ENFORCING) |
229 | error = 0; | |
230 | return error; | |
231 | } | |
232 | ||
233 | /** | |
234 | * tomoyo_mount_permission - Check permission for mount() operation. | |
235 | * | |
236 | * @dev_name: Name of device file. | |
237 | * @path: Pointer to "struct path". | |
238 | * @type: Name of filesystem type. May be NULL. | |
239 | * @flags: Mount options. | |
240 | * @data_page: Optional data. May be NULL. | |
241 | * | |
242 | * Returns 0 on success, negative value otherwise. | |
243 | */ | |
244 | int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, | |
245 | unsigned long flags, void *data_page) | |
246 | { | |
247 | struct tomoyo_request_info r; | |
248 | int error; | |
249 | int idx; | |
250 | ||
57c2590f TH |
251 | if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT) |
252 | == TOMOYO_CONFIG_DISABLED) | |
2106ccd9 TH |
253 | return 0; |
254 | if (!type) | |
255 | type = "<NULL>"; | |
256 | idx = tomoyo_read_lock(); | |
257 | error = tomoyo_mount_acl(&r, dev_name, path, type, flags); | |
258 | tomoyo_read_unlock(idx); | |
259 | return error; | |
260 | } | |
261 | ||
262 | /** | |
263 | * tomoyo_write_mount_policy - Write "struct tomoyo_mount_acl" list. | |
264 | * | |
265 | * @data: String to parse. | |
266 | * @domain: Pointer to "struct tomoyo_domain_info". | |
267 | * @is_delete: True if it is a delete request. | |
268 | * | |
269 | * Returns 0 on success, negative value otherwise. | |
270 | */ | |
271 | int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain, | |
272 | const bool is_delete) | |
273 | { | |
274 | struct tomoyo_acl_info *ptr; | |
275 | struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; | |
276 | int error = is_delete ? -ENOENT : -ENOMEM; | |
277 | char *w[4]; | |
278 | if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0]) | |
279 | return -EINVAL; | |
280 | if (!tomoyo_parse_name_union(w[0], &e.dev_name) || | |
281 | !tomoyo_parse_name_union(w[1], &e.dir_name) || | |
282 | !tomoyo_parse_name_union(w[2], &e.fs_type) || | |
283 | !tomoyo_parse_number_union(w[3], &e.flags)) | |
284 | goto out; | |
285 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | |
286 | goto out; | |
287 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | |
288 | struct tomoyo_mount_acl *acl = | |
289 | container_of(ptr, struct tomoyo_mount_acl, head); | |
290 | if (!tomoyo_is_same_mount_acl(acl, &e)) | |
291 | continue; | |
292 | acl->is_deleted = is_delete; | |
293 | error = 0; | |
294 | break; | |
295 | } | |
296 | if (!is_delete && error) { | |
297 | struct tomoyo_mount_acl *entry = | |
298 | tomoyo_commit_ok(&e, sizeof(e)); | |
299 | if (entry) { | |
300 | list_add_tail_rcu(&entry->head.list, | |
301 | &domain->acl_info_list); | |
302 | error = 0; | |
303 | } | |
304 | } | |
305 | mutex_unlock(&tomoyo_policy_lock); | |
306 | out: | |
307 | tomoyo_put_name_union(&e.dev_name); | |
308 | tomoyo_put_name_union(&e.dir_name); | |
309 | tomoyo_put_name_union(&e.fs_type); | |
310 | tomoyo_put_number_union(&e.flags); | |
311 | return error; | |
312 | } |