1 |
/* |
2 |
* fs/sakura_mount.c |
3 |
* |
4 |
* Implementation of the Domain-Free Mandatory Access Control. |
5 |
* |
6 |
* Copyright (C) 2005-2007 NTT DATA CORPORATION |
7 |
* |
8 |
* Version: 1.5.0 2007/09/20 |
9 |
* |
10 |
* This file is applicable to both 2.4.30 and 2.6.11 and later. |
11 |
* See README.ccs for ChangeLog. |
12 |
* |
13 |
*/ |
14 |
/***** SAKURA Linux start. *****/ |
15 |
|
16 |
#include <linux/ccs_common.h> |
17 |
#include <linux/sakura.h> |
18 |
#include <linux/realpath.h> |
19 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) |
20 |
#include <linux/namespace.h> |
21 |
#endif |
22 |
|
23 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) |
24 |
#define MS_UNBINDABLE (1<<17) /* change to unbindable */ |
25 |
#define MS_PRIVATE (1<<18) /* change to private */ |
26 |
#define MS_SLAVE (1<<19) /* change to slave */ |
27 |
#define MS_SHARED (1<<20) /* change to shared */ |
28 |
#endif |
29 |
|
30 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) |
31 |
#include <linux/namei.h> |
32 |
#else |
33 |
static inline void module_put(struct module *module) |
34 |
{ |
35 |
if (module) __MOD_DEC_USE_COUNT(module); |
36 |
} |
37 |
#endif |
38 |
|
39 |
extern const char *ccs_log_level; |
40 |
|
41 |
/***** KEYWORDS for mount restrictions. *****/ |
42 |
|
43 |
#define MOUNT_BIND_KEYWORD "--bind" /* Allow to call 'mount --bind /source_dir /dest_dir' */ |
44 |
#define MOUNT_MOVE_KEYWORD "--move" /* Allow to call 'mount --move /old_dir /new_dir ' */ |
45 |
#define MOUNT_REMOUNT_KEYWORD "--remount" /* Allow to call 'mount -o remount /dir ' */ |
46 |
#define MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable" /* Allow to call 'mount --make-unbindable /dir' */ |
47 |
#define MOUNT_MAKE_PRIVATE_KEYWORD "--make-private" /* Allow to call 'mount --make-private /dir' */ |
48 |
#define MOUNT_MAKE_SLAVE_KEYWORD "--make-slave" /* Allow to call 'mount --make-slave /dir' */ |
49 |
#define MOUNT_MAKE_SHARED_KEYWORD "--make-shared" /* Allow to call 'mount --make-shared /dir' */ |
50 |
|
51 |
/***** The structure for mount restrictions. *****/ |
52 |
|
53 |
struct mount_entry { |
54 |
struct mount_entry *next; |
55 |
const struct path_info *dev_name; |
56 |
const struct path_info *dir_name; |
57 |
const struct path_info *fs_type; |
58 |
unsigned long flags; |
59 |
int is_deleted; |
60 |
}; |
61 |
|
62 |
/************************* MOUNT RESTRICTION HANDLER *************************/ |
63 |
|
64 |
static void put_filesystem(struct file_system_type *fs) |
65 |
{ |
66 |
module_put(fs->owner); |
67 |
} |
68 |
|
69 |
static struct mount_entry *mount_list = NULL; |
70 |
|
71 |
static int AddMountACL(const char *dev_name, const char *dir_name, const char *fs_type, const unsigned long flags, const int is_delete) |
72 |
{ |
73 |
struct mount_entry *new_entry, *ptr; |
74 |
const struct path_info *fs, *dev, *dir; |
75 |
static DECLARE_MUTEX(lock); |
76 |
int error = -ENOMEM; |
77 |
if ((fs = SaveName(fs_type)) == NULL) return -EINVAL; |
78 |
if (!dev_name) dev_name = "<NULL>"; /* Map dev_name to "<NULL>" for if no dev_name given. */ |
79 |
if (strcmp(fs->name, MOUNT_REMOUNT_KEYWORD) == 0) dev_name = "any"; /* Fix dev_name to "any" for remount permission. */ |
80 |
if (strcmp(fs->name, MOUNT_MAKE_UNBINDABLE_KEYWORD) == 0 || |
81 |
strcmp(fs->name, MOUNT_MAKE_PRIVATE_KEYWORD) == 0 || |
82 |
strcmp(fs->name, MOUNT_MAKE_SLAVE_KEYWORD) == 0 || |
83 |
strcmp(fs->name, MOUNT_MAKE_SHARED_KEYWORD) == 0) dev_name = "any"; |
84 |
if (!IsCorrectPath(dev_name, 0, 0, 0, __FUNCTION__) || !IsCorrectPath(dir_name, 1, 0, 1, __FUNCTION__)) return -EINVAL; |
85 |
if ((dev = SaveName(dev_name)) == NULL || (dir = SaveName(dir_name)) == NULL) return -ENOMEM; |
86 |
down(&lock); |
87 |
for (ptr = mount_list; ptr; ptr = ptr->next) { |
88 |
if (ptr->flags != flags || pathcmp(ptr->dev_name, dev) || pathcmp(ptr->dir_name, dir) || pathcmp(ptr->fs_type, fs)) continue; |
89 |
error = 0; |
90 |
if (is_delete) { |
91 |
ptr->is_deleted = 1; |
92 |
goto out; |
93 |
} else { |
94 |
if (ptr->is_deleted) { |
95 |
ptr->is_deleted = 0; |
96 |
goto update; |
97 |
} |
98 |
goto out; /* No changes. */ |
99 |
} |
100 |
} |
101 |
if (is_delete) { |
102 |
error = -ENOENT; |
103 |
goto out; |
104 |
} |
105 |
if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out; |
106 |
new_entry->dev_name = dev; |
107 |
new_entry->dir_name = dir; |
108 |
new_entry->fs_type = fs; |
109 |
new_entry->flags = flags; |
110 |
mb(); /* Instead of using spinlock. */ |
111 |
if ((ptr = mount_list) != NULL) { |
112 |
while (ptr->next) ptr = ptr->next; ptr->next = new_entry; |
113 |
} else { |
114 |
mount_list = new_entry; |
115 |
} |
116 |
error = 0; |
117 |
ptr = new_entry; |
118 |
update: |
119 |
{ |
120 |
struct file_system_type *type = NULL; |
121 |
if (strcmp(fs->name, MOUNT_REMOUNT_KEYWORD) == 0) { |
122 |
printk("%sAllow remount %s with options 0x%lX.\n", ccs_log_level, dir->name, ptr->flags); |
123 |
} else if (strcmp(fs->name, MOUNT_BIND_KEYWORD) == 0 || strcmp(fs->name, MOUNT_MOVE_KEYWORD) == 0) { |
124 |
printk("%sAllow mount %s %s %s with options 0x%lX\n", ccs_log_level, fs->name, dev->name, dir->name, ptr->flags); |
125 |
} else if (strcmp(fs->name, MOUNT_MAKE_UNBINDABLE_KEYWORD) == 0 || |
126 |
strcmp(fs->name, MOUNT_MAKE_PRIVATE_KEYWORD) == 0 || |
127 |
strcmp(fs->name, MOUNT_MAKE_SLAVE_KEYWORD) == 0 || |
128 |
strcmp(fs->name, MOUNT_MAKE_SHARED_KEYWORD) == 0) { |
129 |
printk("%sAllow mount %s %s with options 0x%lX.\n", ccs_log_level, fs->name, dir->name, ptr->flags); |
130 |
} else if ((type = get_fs_type(fs->name)) != NULL && (type->fs_flags & FS_REQUIRES_DEV) != 0) { |
131 |
printk("%sAllow mount -t %s %s %s with options 0x%lX.\n", ccs_log_level, fs->name, dev->name, dir->name, ptr->flags); |
132 |
} else { |
133 |
printk("%sAllow mount %s on %s with options 0x%lX.\n", ccs_log_level, fs->name, dir->name, ptr->flags); |
134 |
} |
135 |
if (type) put_filesystem(type); |
136 |
} |
137 |
out: |
138 |
up(&lock); |
139 |
return error; |
140 |
} |
141 |
|
142 |
static int CheckMountPermission2(char *dev_name, char *dir_name, char *type, unsigned long flags) |
143 |
{ |
144 |
const int is_enforce = CheckCCSEnforce(CCS_SAKURA_RESTRICT_MOUNT); |
145 |
int error = -EPERM; |
146 |
if (!CheckCCSFlags(CCS_SAKURA_RESTRICT_MOUNT)) return 0; |
147 |
if (!type) type = "<NULL>"; |
148 |
if ((flags & MS_MGC_MSK) == MS_MGC_VAL) flags &= ~MS_MGC_MSK; |
149 |
switch (flags & (MS_REMOUNT | MS_MOVE | MS_BIND)) { |
150 |
case MS_REMOUNT: |
151 |
case MS_MOVE: |
152 |
case MS_BIND: |
153 |
case 0: |
154 |
break; |
155 |
default: |
156 |
printk("SAKURA-ERROR: %s%s%sare given for single mount operation.\n", |
157 |
flags & MS_REMOUNT ? "'remount' " : "", |
158 |
flags & MS_MOVE ? "'move' " : "", |
159 |
flags & MS_BIND ? "'bind' " : ""); |
160 |
return -EINVAL; |
161 |
} |
162 |
switch (flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED)) { |
163 |
case MS_UNBINDABLE: |
164 |
case MS_PRIVATE: |
165 |
case MS_SLAVE: |
166 |
case MS_SHARED: |
167 |
case 0: |
168 |
break; |
169 |
default: |
170 |
printk("SAKURA-ERROR: %s%s%s%sare given for single mount operation.\n", |
171 |
flags & MS_UNBINDABLE ? "'unbindable' " : "", |
172 |
flags & MS_PRIVATE ? "'private' " : "", |
173 |
flags & MS_SLAVE ? "'slave' " : "", |
174 |
flags & MS_SHARED ? "'shared' " : ""); |
175 |
return -EINVAL; |
176 |
} |
177 |
if (flags & MS_REMOUNT) { |
178 |
error = CheckMountPermission2(dev_name, dir_name, MOUNT_REMOUNT_KEYWORD, flags & ~MS_REMOUNT); |
179 |
} else if (flags & MS_MOVE) { |
180 |
error = CheckMountPermission2(dev_name, dir_name, MOUNT_MOVE_KEYWORD, flags & ~MS_MOVE); |
181 |
} else if (flags & MS_BIND) { |
182 |
error = CheckMountPermission2(dev_name, dir_name, MOUNT_BIND_KEYWORD, flags & ~MS_BIND); |
183 |
} else if (flags & MS_UNBINDABLE) { |
184 |
error = CheckMountPermission2(dev_name, dir_name, MOUNT_MAKE_UNBINDABLE_KEYWORD, flags & ~MS_UNBINDABLE); |
185 |
} else if (flags & MS_PRIVATE) { |
186 |
error = CheckMountPermission2(dev_name, dir_name, MOUNT_MAKE_PRIVATE_KEYWORD, flags & ~MS_PRIVATE); |
187 |
} else if (flags & MS_SLAVE) { |
188 |
error = CheckMountPermission2(dev_name, dir_name, MOUNT_MAKE_SLAVE_KEYWORD, flags & ~MS_SLAVE); |
189 |
} else if (flags & MS_SHARED) { |
190 |
error = CheckMountPermission2(dev_name, dir_name, MOUNT_MAKE_SHARED_KEYWORD, flags & ~MS_SHARED); |
191 |
} else { |
192 |
struct mount_entry *ptr; |
193 |
struct file_system_type *fstype = NULL; |
194 |
const char *requested_dir_name = NULL; |
195 |
const char *requested_dev_name = NULL; |
196 |
struct path_info rdev, rdir; |
197 |
int need_dev = 0; |
198 |
|
199 |
if ((requested_dir_name = realpath(dir_name)) == NULL) { |
200 |
error = -ENOENT; |
201 |
goto cleanup; |
202 |
} |
203 |
rdir.name = requested_dir_name; |
204 |
fill_path_info(&rdir); |
205 |
|
206 |
/* Compare fs name. */ |
207 |
if (strcmp(type, MOUNT_REMOUNT_KEYWORD) == 0) { |
208 |
/* Needn't to resolve dev_name */ |
209 |
} else if (strcmp(type, MOUNT_MAKE_UNBINDABLE_KEYWORD) == 0 || |
210 |
strcmp(type, MOUNT_MAKE_PRIVATE_KEYWORD) == 0 || |
211 |
strcmp(type, MOUNT_MAKE_SLAVE_KEYWORD) == 0 || |
212 |
strcmp(type, MOUNT_MAKE_SHARED_KEYWORD) == 0) { |
213 |
/* Needn't to resolve dev_name */ |
214 |
} else if (strcmp(type, MOUNT_BIND_KEYWORD) == 0 || strcmp(type, MOUNT_MOVE_KEYWORD) == 0) { |
215 |
if ((requested_dev_name = realpath(dev_name)) == NULL) { |
216 |
error = -ENOENT; |
217 |
goto cleanup; |
218 |
} |
219 |
rdev.name = requested_dev_name; |
220 |
fill_path_info(&rdev); |
221 |
need_dev = -1; /* dev_name is a directory */ |
222 |
} else if ((fstype = get_fs_type(type)) != NULL) { |
223 |
if (fstype->fs_flags & FS_REQUIRES_DEV) { |
224 |
if ((requested_dev_name = realpath(dev_name)) == NULL) { |
225 |
error = -ENOENT; |
226 |
goto cleanup; |
227 |
} |
228 |
rdev.name = requested_dev_name; |
229 |
fill_path_info(&rdev); |
230 |
need_dev = 1; /* dev_name is a block device file */ |
231 |
} |
232 |
} else { |
233 |
error = -ENODEV; |
234 |
goto cleanup; |
235 |
} |
236 |
for (ptr = mount_list; ptr; ptr = ptr->next) { |
237 |
if (ptr->is_deleted) continue; |
238 |
|
239 |
/* Compare options */ |
240 |
if (ptr->flags != flags) continue; |
241 |
|
242 |
/* Compare fs name. */ |
243 |
if (strcmp(type, ptr->fs_type->name)) continue; |
244 |
|
245 |
/* Compare mount point. */ |
246 |
if (PathMatchesToPattern(&rdir, ptr->dir_name) == 0) continue; |
247 |
|
248 |
/* Compare device name. */ |
249 |
if (requested_dev_name && PathMatchesToPattern(&rdev, ptr->dev_name) == 0) continue; |
250 |
|
251 |
/* OK. */ |
252 |
error = 0; |
253 |
|
254 |
if (need_dev > 0) { |
255 |
printk(KERN_DEBUG "SAKURA-NOTICE: 'mount -t %s %s %s 0x%lX' accepted.\n", type, requested_dev_name, requested_dir_name, flags); |
256 |
} else if (need_dev < 0) { |
257 |
printk(KERN_DEBUG "SAKURA-NOTICE: 'mount %s %s %s 0x%lX' accepted.\n", type, requested_dev_name, requested_dir_name, flags); |
258 |
} else if (strcmp(type, MOUNT_REMOUNT_KEYWORD) == 0) { |
259 |
printk(KERN_DEBUG "SAKURA-NOTICE: 'mount -o remount %s 0x%lX' accepted.\n", requested_dir_name, flags); |
260 |
} else if (strcmp(type, MOUNT_MAKE_UNBINDABLE_KEYWORD) == 0 || |
261 |
strcmp(type, MOUNT_MAKE_PRIVATE_KEYWORD) == 0 || |
262 |
strcmp(type, MOUNT_MAKE_SLAVE_KEYWORD) == 0 || |
263 |
strcmp(type, MOUNT_MAKE_SHARED_KEYWORD) == 0) { |
264 |
printk(KERN_DEBUG "SAKURA-NOTICE: 'mount %s %s 0x%lX' accepted.\n", type, requested_dir_name, flags); |
265 |
} else { |
266 |
printk(KERN_DEBUG "SAKURA-NOTICE: 'mount %s on %s 0x%lX' accepted.\n", type, requested_dir_name, flags); |
267 |
} |
268 |
break; |
269 |
} |
270 |
if (error) { |
271 |
const char *realname1 = realpath(dev_name), *realname2 = realpath(dir_name), *exename = GetEXE(); |
272 |
if (strcmp(type, MOUNT_REMOUNT_KEYWORD) == 0) { |
273 |
printk("SAKURA-%s: mount -o remount %s 0x%lX (pid=%d:exe=%s): Permission denied.\n", GetMSG(is_enforce), realname2 ? realname2 : dir_name, flags, current->pid, exename); |
274 |
if (is_enforce && CheckSupervisor("# %s is requesting\nmount -o remount %s 0x%lX\n", exename, realname2 ? realname2 : dir_name, flags) == 0) error = 0; |
275 |
} else if (strcmp(type, MOUNT_BIND_KEYWORD) == 0 || strcmp(type, MOUNT_MOVE_KEYWORD) == 0) { |
276 |
printk("SAKURA-%s: mount %s %s %s 0x%lX (pid=%d:exe=%s): Permission denied.\n", GetMSG(is_enforce), type, realname1 ? realname1 : dev_name, realname2 ? realname2 : dir_name, flags, current->pid, exename); |
277 |
if (is_enforce && CheckSupervisor("# %s is requesting\nmount %s %s %s 0x%lX\n", exename, type, realname1 ? realname1 : dev_name, realname2 ? realname2 : dir_name, flags) == 0) error = 0; |
278 |
} else if (strcmp(type, MOUNT_MAKE_UNBINDABLE_KEYWORD) == 0 || |
279 |
strcmp(type, MOUNT_MAKE_PRIVATE_KEYWORD) == 0 || |
280 |
strcmp(type, MOUNT_MAKE_SLAVE_KEYWORD) == 0 || |
281 |
strcmp(type, MOUNT_MAKE_SHARED_KEYWORD) == 0) { |
282 |
printk("SAKURA-%s: mount %s %s 0x%lX (pid=%d:exe=%s): Permission denied.\n", GetMSG(is_enforce), type, realname2 ? realname2 : dir_name, flags, current->pid, exename); |
283 |
if (is_enforce && CheckSupervisor("# %s is requesting\nmount %s %s 0x%lX", exename, type, realname2 ? realname2 : dir_name, flags) == 0) error = 0; |
284 |
} else { |
285 |
printk("SAKURA-%s: mount -t %s %s %s 0x%lX (pid=%d:exe=%s): Permission denied.\n", GetMSG(is_enforce), type, realname1 ? realname1 : dev_name, realname2 ? realname2 : dir_name, flags, current->pid, exename); |
286 |
if (is_enforce && CheckSupervisor("# %s is requesting\nmount -t %s %s %s 0x%lX\n", exename, type, realname1 ? realname1 : dev_name, realname2 ? realname2 : dir_name, flags) == 0) error = 0; |
287 |
} |
288 |
ccs_free(exename); |
289 |
ccs_free(realname2); |
290 |
ccs_free(realname1); |
291 |
} |
292 |
if (error && !is_enforce && CheckCCSAccept(CCS_SAKURA_RESTRICT_MOUNT)) { |
293 |
AddMountACL(need_dev ? requested_dev_name : dev_name, requested_dir_name, type, flags, 0); |
294 |
UpdateCounter(CCS_UPDATES_COUNTER_SYSTEM_POLICY); |
295 |
} |
296 |
cleanup: |
297 |
ccs_free(requested_dev_name); |
298 |
ccs_free(requested_dir_name); |
299 |
if (fstype) put_filesystem(fstype); |
300 |
} |
301 |
if (!is_enforce) error = 0; |
302 |
return error; |
303 |
} |
304 |
|
305 |
/* This is a wrapper to allow use of 1.4.x patch for 1.5.x . */ |
306 |
int CheckMountPermission(char *dev_name, char *dir_name, char *type, const unsigned long *flags) |
307 |
{ |
308 |
return CheckMountPermission2(dev_name, dir_name, type, *flags); |
309 |
} |
310 |
EXPORT_SYMBOL(CheckMountPermission); |
311 |
|
312 |
int AddMountPolicy(char *data, const int is_delete) |
313 |
{ |
314 |
char *cp, *cp2; |
315 |
const char *fs, *dev, *dir; |
316 |
unsigned long flags = 0; |
317 |
cp2 = data; if ((cp = strchr(cp2, ' ')) == NULL) return -EINVAL; *cp = '\0'; dev = cp2; |
318 |
cp2 = cp + 1; if ((cp = strchr(cp2, ' ')) == NULL) return -EINVAL; *cp = '\0'; dir = cp2; |
319 |
cp2 = cp + 1; if ((cp = strchr(cp2, ' ')) == NULL) return -EINVAL; *cp = '\0'; fs = cp2; |
320 |
flags = simple_strtoul(cp + 1, NULL, 0); |
321 |
return AddMountACL(dev, dir, fs, flags, is_delete); |
322 |
} |
323 |
|
324 |
int ReadMountPolicy(struct io_buffer *head) |
325 |
{ |
326 |
struct mount_entry *ptr = head->read_var2; |
327 |
if (!ptr) ptr = mount_list; |
328 |
while (ptr) { |
329 |
head->read_var2 = ptr; |
330 |
if (ptr->is_deleted == 0 && io_printf(head, KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n", ptr->dev_name->name, ptr->dir_name->name, ptr->fs_type->name, ptr->flags)) break; |
331 |
ptr = ptr->next; |
332 |
} |
333 |
return ptr ? -ENOMEM : 0; |
334 |
} |
335 |
|
336 |
/***** SAKURA Linux end. *****/ |