1 |
/* |
2 |
* include/linux/syaoran.h |
3 |
* |
4 |
* Implementation of the Tamper-Proof Device Filesystem. |
5 |
* |
6 |
* Copyright (C) 2005-2007 NTT DATA CORPORATION |
7 |
* |
8 |
* Version: 1.5.3-pre 2007/12/03 |
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 |
/* |
15 |
* A brief description about SYAORAN: |
16 |
* |
17 |
* SYAORAN stands for "Simple Yet All-important Object Realizing Abiding Nexus". |
18 |
* SYAORAN is a filesystem for /dev with Mandatory Access Control. |
19 |
* |
20 |
* /dev needs to be writable, but this means that files on /dev might be tampered with. |
21 |
* SYAORAN can restrict combinations of (pathname, attribute) that the system can create. |
22 |
* The attribute is one of directory, regular file, FIFO, UNIX domain socket, |
23 |
* symbolic link, character or block device file with major/minor device numbers. |
24 |
* |
25 |
* You can use SYAORAN alone, but I recommend you to use with SAKURA and TOMOYO. |
26 |
*/ |
27 |
|
28 |
#ifndef _LINUX_SYAORAN_H |
29 |
#define _LINUX_SYAORAN_H |
30 |
|
31 |
#ifndef __user |
32 |
#define __user |
33 |
#endif |
34 |
|
35 |
/***** SYAORAN start. *****/ |
36 |
|
37 |
#include <linux/version.h> |
38 |
|
39 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) |
40 |
#define s_fs_info u.generic_sbp |
41 |
#endif |
42 |
|
43 |
#define list_for_each_cookie(pos, cookie, head) \ |
44 |
for ((cookie) || ((cookie) = (head)), pos = (cookie)->next; \ |
45 |
prefetch(pos->next), pos != (head) || ((cookie) = NULL); \ |
46 |
(cookie) = pos, pos = pos->next) |
47 |
|
48 |
#ifndef list_for_each_entry_safe |
49 |
#define list_for_each_entry_safe(pos, n, head, member) \ |
50 |
for (pos = list_entry((head)->next, typeof(*pos), member), \ |
51 |
n = list_entry(pos->member.next, typeof(*pos), member); \ |
52 |
&pos->member != (head); \ |
53 |
pos = n, n = list_entry(n->member.next, typeof(*n), member)) |
54 |
#endif |
55 |
|
56 |
/* The following constants are used to restrict operations.*/ |
57 |
|
58 |
#define MAY_CREATE 1 /* This file is allowed to mknod() */ |
59 |
#define MAY_DELETE 2 /* This file is allowed to unlink() */ |
60 |
#define MAY_CHMOD 4 /* This file is allowed to chmod() */ |
61 |
#define MAY_CHOWN 8 /* This file is allowed to chown() */ |
62 |
#define DEVICE_USED 16 /* This block or character device file is used. */ |
63 |
#define NO_CREATE_AT_MOUNT 32 /* Don't create this file at mount(). */ |
64 |
|
65 |
/* some random number */ |
66 |
#define SYAORAN_MAGIC 0x2F646576 /* = '/dev' */ |
67 |
|
68 |
static void syaoran_put_super(struct super_block *sb); |
69 |
static int Syaoran_Initialize(struct super_block *sb, void *data); |
70 |
static void MakeInitialNodes(struct super_block *sb); |
71 |
static int MayCreateNode(struct dentry *dentry, int mode, int dev); |
72 |
static int MayModifyNode(struct dentry *dentry, unsigned int flags); |
73 |
static int syaoran_create_tracelog(struct super_block *sb, const char *filename); |
74 |
|
75 |
/* Wraps blkdev_open() to trace open operation for block devices. */ |
76 |
static int (*org_blkdev_open) (struct inode * inode, struct file * filp) = NULL; |
77 |
static struct file_operations wrapped_def_blk_fops; |
78 |
|
79 |
static int wrapped_blkdev_open(struct inode * inode, struct file * filp) |
80 |
{ |
81 |
int error = org_blkdev_open(inode, filp); |
82 |
if (error != -ENXIO) MayModifyNode(filp->f_dentry, DEVICE_USED); |
83 |
return error; |
84 |
} |
85 |
|
86 |
/* Wraps chrdev_open() to trace open operation for character devices. */ |
87 |
static int (*org_chrdev_open) (struct inode * inode, struct file * filp) = NULL; |
88 |
static struct file_operations wrapped_def_chr_fops; |
89 |
|
90 |
static int wrapped_chrdev_open(struct inode * inode, struct file * filp) |
91 |
{ |
92 |
int error = org_chrdev_open(inode, filp); |
93 |
if (error != -ENXIO) MayModifyNode(filp->f_dentry, DEVICE_USED); |
94 |
return error; |
95 |
} |
96 |
|
97 |
/* lookup_create() without nameidata. Called only while initialization. */ |
98 |
static struct dentry *lookup_create2(const char *name, struct dentry *base, const u8 is_dir) |
99 |
{ |
100 |
struct dentry *dentry; |
101 |
const int len = name ? strlen(name) : 0; |
102 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
103 |
mutex_lock(&base->d_inode->i_mutex); |
104 |
#else |
105 |
down(&base->d_inode->i_sem); |
106 |
#endif |
107 |
dentry = lookup_one_len(name, base, len); |
108 |
if (IS_ERR(dentry)) goto fail; |
109 |
if (!is_dir && name[len] && !dentry->d_inode) goto enoent; |
110 |
return dentry; |
111 |
enoent: |
112 |
dput(dentry); |
113 |
dentry = ERR_PTR(-ENOENT); |
114 |
fail: |
115 |
return dentry; |
116 |
} |
117 |
|
118 |
/* mkdir(). Called only while initialization. */ |
119 |
static int fs_mkdir(const char *pathname, struct dentry *base, int mode, uid_t user, gid_t group) |
120 |
{ |
121 |
struct dentry *dentry = lookup_create2(pathname, base, 1); |
122 |
int error = PTR_ERR(dentry); |
123 |
if (!IS_ERR(dentry)) { |
124 |
error = vfs_mkdir(base->d_inode, dentry, mode); |
125 |
if (!error) { |
126 |
lock_kernel(); |
127 |
dentry->d_inode->i_uid = user; |
128 |
dentry->d_inode->i_gid = group; |
129 |
unlock_kernel(); |
130 |
} |
131 |
dput(dentry); |
132 |
} |
133 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
134 |
mutex_unlock(&base->d_inode->i_mutex); |
135 |
#else |
136 |
up(&base->d_inode->i_sem); |
137 |
#endif |
138 |
return error; |
139 |
} |
140 |
|
141 |
/* mknod(). Called only while initialization. */ |
142 |
static int fs_mknod(const char *filename, struct dentry *base, int mode, dev_t dev, uid_t user, gid_t group) |
143 |
{ |
144 |
struct dentry *dentry; |
145 |
int error; |
146 |
switch (mode & S_IFMT) { |
147 |
case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: case S_IFREG: |
148 |
break; |
149 |
default: |
150 |
return -EPERM; |
151 |
} |
152 |
dentry = lookup_create2(filename, base, 0); |
153 |
error = PTR_ERR(dentry); |
154 |
if (!IS_ERR(dentry)) { |
155 |
error = vfs_mknod(base->d_inode, dentry, mode, dev); |
156 |
if (!error) { |
157 |
lock_kernel(); |
158 |
dentry->d_inode->i_uid = user; |
159 |
dentry->d_inode->i_gid = group; |
160 |
unlock_kernel(); |
161 |
} |
162 |
dput(dentry); |
163 |
} |
164 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
165 |
mutex_unlock(&base->d_inode->i_mutex); |
166 |
#else |
167 |
up(&base->d_inode->i_sem); |
168 |
#endif |
169 |
return error; |
170 |
} |
171 |
|
172 |
/* symlink(). Called only while initialization. */ |
173 |
static int fs_symlink(const char *pathname, struct dentry *base, char *oldname, int mode, uid_t user, gid_t group) |
174 |
{ |
175 |
struct dentry *dentry = lookup_create2(pathname, base, 0); |
176 |
int error = PTR_ERR(dentry); |
177 |
if (!IS_ERR(dentry)) { |
178 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) |
179 |
error = vfs_symlink(base->d_inode, dentry, oldname, S_IALLUGO); |
180 |
#else |
181 |
error = vfs_symlink(base->d_inode, dentry, oldname); |
182 |
#endif |
183 |
if (!error) { |
184 |
lock_kernel(); |
185 |
dentry->d_inode->i_mode = mode; |
186 |
dentry->d_inode->i_uid = user; |
187 |
dentry->d_inode->i_gid = group; |
188 |
unlock_kernel(); |
189 |
} |
190 |
dput(dentry); |
191 |
} |
192 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
193 |
mutex_unlock(&base->d_inode->i_mutex); |
194 |
#else |
195 |
up(&base->d_inode->i_sem); |
196 |
#endif |
197 |
return error; |
198 |
} |
199 |
|
200 |
/* |
201 |
* Format string. |
202 |
* Leading and trailing whitespaces are removed. |
203 |
* Multiple whitespaces are packed into single space. |
204 |
*/ |
205 |
static void NormalizeLine(unsigned char *buffer) |
206 |
{ |
207 |
unsigned char *sp = buffer, *dp = buffer; |
208 |
int first = 1; |
209 |
while (*sp && (*sp <= ' ' || *sp >= 127)) sp++; |
210 |
while (*sp) { |
211 |
if (!first) *dp++ = ' '; |
212 |
first = 0; |
213 |
while (*sp > ' ' && *sp < 127) *dp++ = *sp++; |
214 |
while (*sp && (*sp <= ' ' || *sp >= 127)) sp++; |
215 |
} |
216 |
*dp = '\0'; |
217 |
} |
218 |
|
219 |
/* Convert text form of filename into binary form. */ |
220 |
static void UnEscape(char *filename) |
221 |
{ |
222 |
char *cp = filename; |
223 |
char c, d, e; |
224 |
if (!cp) return; |
225 |
while ((c = *filename++) != '\0') { |
226 |
if (c != '\\') { |
227 |
*cp++ = c; |
228 |
continue; |
229 |
} |
230 |
if ((c = *filename++) == '\\') { |
231 |
*cp++ = c; |
232 |
continue; |
233 |
} |
234 |
if (c < '0' || c > '3' || |
235 |
(d = *filename++) < '0' || d > '7' || |
236 |
(e = *filename++) < '0' || e > '7') { |
237 |
break; |
238 |
} |
239 |
* (unsigned char *) cp++ = (unsigned char) (((unsigned char) (c - '0') << 6) + ((unsigned char) (d - '0') << 3) + (unsigned char) (e - '0')); |
240 |
} |
241 |
*cp = '\0'; |
242 |
} |
243 |
|
244 |
static char *strdup(const char *str) |
245 |
{ |
246 |
char *cp; |
247 |
const int len = str ? strlen(str) + 1 : 0; |
248 |
if ((cp = kmalloc(len, GFP_KERNEL)) != NULL) memmove(cp, str, len); |
249 |
return cp; |
250 |
} |
251 |
|
252 |
static int syaoran_default_mode = -1; /* -1: Not specified, 0: Enforce by default, 1: Accept by default. */ |
253 |
|
254 |
#if !defined(MODULE) |
255 |
static int __init SYAORAN_Setup(char *str) |
256 |
{ |
257 |
if (strcmp(str, "accept") == 0) syaoran_default_mode = 1; |
258 |
else if (strcmp(str, "enforce") == 0) syaoran_default_mode = 0; |
259 |
return 0; |
260 |
} |
261 |
|
262 |
__setup("SYAORAN=", SYAORAN_Setup); |
263 |
#endif |
264 |
|
265 |
struct dev_entry { |
266 |
struct list_head list; |
267 |
char *name; /* Binary form of pathname under mount point. Never NULL. */ |
268 |
mode_t mode; /* Mode and permissions. setuid/setgid/sticky bits are not supported. */ |
269 |
uid_t uid; |
270 |
gid_t gid; |
271 |
dev_t kdev; |
272 |
char *symlink_data; /* Binary form of initial contents for the symlink. NULL if not symlink. */ |
273 |
unsigned int flags; /* File access control flags. */ |
274 |
const char *printable_name; /* Text form of pathname under mount point. Never NULL. */ |
275 |
const char *printable_symlink_data; /* Text form of initial contents for the symlink. NULL if not symlink. */ |
276 |
}; |
277 |
|
278 |
struct syaoran_sb_info { |
279 |
struct list_head list; |
280 |
int initialize_done; /* Zero if initialization is in progress. */ |
281 |
int is_permissive_mode; /* Nonzero if permissive mode. */ |
282 |
}; |
283 |
|
284 |
static int RegisterNodeInfo(char *buffer, struct super_block *sb) |
285 |
{ |
286 |
enum { |
287 |
ARG_FILENAME = 0, |
288 |
ARG_PERMISSION = 1, |
289 |
ARG_UID = 2, |
290 |
ARG_GID = 3, |
291 |
ARG_FLAGS = 4, |
292 |
ARG_DEV_TYPE = 5, |
293 |
ARG_SYMLINK_DATA = 6, |
294 |
ARG_DEV_MAJOR = 6, |
295 |
ARG_DEV_MINOR = 7, |
296 |
MAX_ARG = 8 |
297 |
}; |
298 |
char *args[MAX_ARG]; |
299 |
int i; |
300 |
int error = -EINVAL; |
301 |
unsigned int perm, uid, gid, flags, major = 0, minor = 0; |
302 |
struct syaoran_sb_info *info = (struct syaoran_sb_info *) sb->s_fs_info; |
303 |
struct dev_entry *entry; |
304 |
memset(args, 0, sizeof(args)); |
305 |
args[0] = buffer; |
306 |
for (i = 1; i < MAX_ARG; i++) { |
307 |
args[i] = strchr(args[i - 1] + 1, ' '); |
308 |
if (!args[i]) break; |
309 |
*args[i]++ = '\0'; |
310 |
} |
311 |
/* printk("<%s> <%s> <%s> <%s> <%s> <%s> <%s> <%s>\n", args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); */ |
312 |
if (!args[ARG_FILENAME] || !args[ARG_PERMISSION] || !args[ARG_UID] || !args[ARG_GID] || !args[ARG_DEV_TYPE] || !args[ARG_FLAGS]) goto out; |
313 |
if (sscanf(args[ARG_PERMISSION], "%o", &perm) != 1 || !(perm <= 0777) || sscanf(args[ARG_UID], "%u", &uid) != 1 |
314 |
|| sscanf(args[ARG_GID], "%u", &gid) != 1 || sscanf(args[ARG_FLAGS], "%u", &flags) != 1 || *(args[ARG_DEV_TYPE] + 1)) goto out; |
315 |
switch (*args[ARG_DEV_TYPE]) { |
316 |
case 'c': |
317 |
perm |= S_IFCHR; |
318 |
if (!args[ARG_DEV_MAJOR] || sscanf(args[ARG_DEV_MAJOR], "%u", &major) != 1 |
319 |
|| !args[ARG_DEV_MINOR] || sscanf(args[ARG_DEV_MINOR], "%u", &minor) != 1) goto out; |
320 |
break; |
321 |
case 'b': |
322 |
perm |= S_IFBLK; |
323 |
if (!args[ARG_DEV_MAJOR] || sscanf(args[ARG_DEV_MAJOR], "%u", &major) != 1 |
324 |
|| !args[ARG_DEV_MINOR] || sscanf(args[ARG_DEV_MINOR], "%u", &minor) != 1) goto out; |
325 |
break; |
326 |
case 'l': |
327 |
perm |= S_IFLNK; |
328 |
if (!args[ARG_SYMLINK_DATA]) goto out; |
329 |
break; |
330 |
case 'd': |
331 |
perm |= S_IFDIR; |
332 |
break; |
333 |
case 's': |
334 |
perm |= S_IFSOCK; |
335 |
break; |
336 |
case 'p': |
337 |
perm |= S_IFIFO; |
338 |
break; |
339 |
case 'f': |
340 |
perm |= S_IFREG; |
341 |
break; |
342 |
default: |
343 |
goto out; |
344 |
} |
345 |
error = -ENOMEM; |
346 |
if ((entry = kmalloc(sizeof(*entry), GFP_KERNEL)) == NULL) goto out; |
347 |
memset(entry, 0, sizeof(*entry)); |
348 |
if (S_ISLNK(perm)) { |
349 |
if ((entry->printable_symlink_data = strdup(args[ARG_SYMLINK_DATA])) == NULL) goto out_freemem; |
350 |
} |
351 |
if ((entry->printable_name = strdup(args[ARG_FILENAME])) == NULL) goto out_freemem; |
352 |
if (S_ISLNK(perm)) { |
353 |
if ((entry->symlink_data = strdup(entry->printable_symlink_data)) == NULL) goto out_freemem; |
354 |
UnEscape(entry->symlink_data); |
355 |
} |
356 |
if ((entry->name = strdup(entry->printable_name)) == NULL) goto out_freemem; |
357 |
UnEscape(entry->name); |
358 |
{ /* Drop trailing '/', for GetLocalAbsolutePath() doesn't append trailing '/'. */ |
359 |
const int len = strlen(entry->name); |
360 |
if (len && entry->name[len - 1] == '/') entry->name[len - 1] = '\0'; |
361 |
} |
362 |
entry->mode = perm; |
363 |
entry->uid = uid; |
364 |
entry->gid = gid; |
365 |
entry->kdev = S_ISCHR(perm) || S_ISBLK(perm) ? MKDEV(major, minor) : 0; |
366 |
entry->flags = flags; |
367 |
list_add_tail(&entry->list, &info->list); |
368 |
/* printk("Entry added.\n"); */ |
369 |
error = 0; |
370 |
out: |
371 |
return error; |
372 |
out_freemem: |
373 |
kfree(entry->printable_symlink_data); |
374 |
kfree(entry->printable_name); |
375 |
kfree(entry->symlink_data); |
376 |
kfree(entry); |
377 |
goto out; |
378 |
} |
379 |
|
380 |
static void syaoran_put_super(struct super_block *sb) |
381 |
{ |
382 |
struct syaoran_sb_info *info; |
383 |
struct dev_entry *entry, *tmp; |
384 |
if (!sb) return; |
385 |
info = (struct syaoran_sb_info *) sb->s_fs_info; |
386 |
if (!info) return; |
387 |
list_for_each_entry_safe(entry, tmp, &info->list, list) { |
388 |
kfree(entry->name); |
389 |
kfree(entry->symlink_data); |
390 |
kfree(entry->printable_name); |
391 |
kfree(entry->printable_symlink_data); |
392 |
list_del(&entry->list); |
393 |
/* printk("Entry removed.\n"); */ |
394 |
kfree(entry); |
395 |
} |
396 |
kfree(info); |
397 |
sb->s_fs_info = NULL; |
398 |
printk("%s: Unused memory freed.\n", __FUNCTION__); |
399 |
} |
400 |
|
401 |
static int ReadConfigFile(struct file *file, struct super_block *sb) |
402 |
{ |
403 |
char *buffer; |
404 |
int error = -ENOMEM; |
405 |
if (!file) return -EINVAL; |
406 |
if ((buffer = kmalloc(PAGE_SIZE, GFP_KERNEL)) != NULL) { |
407 |
int len; |
408 |
char *cp; |
409 |
unsigned long offset = 0; |
410 |
memset(buffer, 0, PAGE_SIZE); |
411 |
while ((len = kernel_read(file, offset, buffer, PAGE_SIZE)) > 0 && (cp = memchr(buffer, '\n', len)) != NULL) { |
412 |
*cp = '\0'; |
413 |
offset += cp - buffer + 1; |
414 |
NormalizeLine(buffer); |
415 |
if (RegisterNodeInfo(buffer, sb) == -ENOMEM) goto out; |
416 |
} |
417 |
error = 0; |
418 |
} |
419 |
out: |
420 |
kfree(buffer); |
421 |
return error; |
422 |
} |
423 |
|
424 |
static void MakeNode(struct dev_entry *entry, struct dentry *root) |
425 |
{ |
426 |
struct dentry *base = dget(root); |
427 |
char *filename = entry->name; |
428 |
char *name = filename; |
429 |
unsigned int c; |
430 |
const mode_t perm = entry->mode; |
431 |
const uid_t uid = entry->uid; |
432 |
const gid_t gid = entry->gid; |
433 |
goto start; |
434 |
while ((c = * (unsigned char *) filename) != '\0') { |
435 |
if (c == '/') { |
436 |
struct dentry *new_base; |
437 |
const int len = filename - name; |
438 |
*filename = '\0'; |
439 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
440 |
mutex_lock(&base->d_inode->i_mutex); |
441 |
new_base = lookup_one_len(name, base, len); |
442 |
mutex_unlock(&base->d_inode->i_mutex); |
443 |
#else |
444 |
down(&base->d_inode->i_sem); |
445 |
new_base = lookup_one_len(name, base, len); |
446 |
up(&base->d_inode->i_sem); |
447 |
#endif |
448 |
dput(base); |
449 |
/* |
450 |
if (IS_ERR(new_base)) { |
451 |
printk("'%s' = %ld\n", entry->name, PTR_ERR(new_base)); |
452 |
} else if (!new_base->d_inode || !S_ISDIR(new_base->d_inode->i_mode)) { |
453 |
printk("Directory '%s' does not exist.\n", entry->name); |
454 |
} else { |
455 |
printk("Directory '%s' exists.\n", entry->name); |
456 |
} |
457 |
*/ |
458 |
*filename = '/'; |
459 |
filename++; |
460 |
if (IS_ERR(new_base)) { |
461 |
return; |
462 |
} else if (!new_base->d_inode || !S_ISDIR(new_base->d_inode->i_mode)) { |
463 |
dput(new_base); |
464 |
return; |
465 |
} |
466 |
base = new_base; |
467 |
start: |
468 |
name = filename; |
469 |
} else { |
470 |
filename++; |
471 |
} |
472 |
} |
473 |
filename = (char *) name; |
474 |
if (S_ISLNK(perm)) { |
475 |
fs_symlink(filename, base, entry->symlink_data, perm, uid, gid); |
476 |
} else if (S_ISDIR(perm)) { |
477 |
fs_mkdir(filename, base, perm ^ S_IFDIR, uid, gid); |
478 |
} else if (S_ISSOCK(perm) || S_ISFIFO(perm) || S_ISREG(perm)) { |
479 |
fs_mknod(filename, base, perm, 0, uid, gid); |
480 |
} else if (S_ISCHR(perm) || S_ISBLK(perm)) { |
481 |
fs_mknod(filename, base, perm, entry->kdev, uid, gid); |
482 |
} |
483 |
dput(base); |
484 |
} |
485 |
|
486 |
/* Create files according to the policy file. */ |
487 |
static void MakeInitialNodes(struct super_block *sb) |
488 |
{ |
489 |
struct syaoran_sb_info *info; |
490 |
struct dev_entry *entry; |
491 |
if (!sb) return; |
492 |
info = (struct syaoran_sb_info *) sb->s_fs_info; |
493 |
if (!info) return; |
494 |
if (info->is_permissive_mode) { |
495 |
syaoran_create_tracelog(sb, ".syaoran"); |
496 |
syaoran_create_tracelog(sb, ".syaoran_all"); |
497 |
} |
498 |
list_for_each_entry(entry, &info->list, list) { |
499 |
if ((entry->flags & NO_CREATE_AT_MOUNT) == 0) MakeNode(entry, sb->s_root); |
500 |
} |
501 |
info->initialize_done = 1; |
502 |
} |
503 |
|
504 |
/* Read policy file. */ |
505 |
static int Syaoran_Initialize(struct super_block *sb, void *data) |
506 |
{ |
507 |
int error = -EINVAL; |
508 |
static int first = 1; |
509 |
if (first) { |
510 |
first = 0; |
511 |
printk("SYAORAN: 1.5.3-pre 2007/12/03\n"); |
512 |
} |
513 |
{ |
514 |
struct inode *inode = new_inode(sb); |
515 |
if (!inode) return -EINVAL; |
516 |
/* Create /dev/ram0 to get the value of blkdev_open(). */ |
517 |
init_special_inode(inode, S_IFBLK | 0666, MKDEV(1, 0)); |
518 |
wrapped_def_blk_fops = *inode->i_fop; |
519 |
iput(inode); |
520 |
org_blkdev_open = wrapped_def_blk_fops.open; |
521 |
wrapped_def_blk_fops.open = wrapped_blkdev_open; |
522 |
} |
523 |
{ |
524 |
struct inode *inode = new_inode(sb); |
525 |
if (!inode) return -EINVAL; |
526 |
/* Create /dev/null to get the value of chrdev_open(). */ |
527 |
init_special_inode(inode, S_IFCHR | 0666, MKDEV(1, 3)); |
528 |
wrapped_def_chr_fops = *inode->i_fop; |
529 |
iput(inode); |
530 |
org_chrdev_open = wrapped_def_chr_fops.open; |
531 |
wrapped_def_chr_fops.open = wrapped_chrdev_open; |
532 |
} |
533 |
if (data) { |
534 |
struct file *f; |
535 |
char *filename = (char *) data; |
536 |
int is_permissive_mode = syaoran_default_mode; |
537 |
/* If mode is given with mount operation, use it. */ |
538 |
if (strncmp(filename, "accept=", 7) == 0) { |
539 |
filename += 7; |
540 |
is_permissive_mode = 1; |
541 |
} else if (strncmp(filename, "enforce=", 8) == 0) { |
542 |
filename += 8; |
543 |
is_permissive_mode = 0; |
544 |
} else if (is_permissive_mode == -1) { |
545 |
/* If mode is not given with command line, abort mount. */ |
546 |
printk("SYAORAN: Missing 'accept=' or 'enforce='.\n"); |
547 |
return -EINVAL; |
548 |
} |
549 |
f = filp_open(filename, O_RDONLY, 0600); |
550 |
if (!IS_ERR(f)) { |
551 |
struct syaoran_sb_info *p; |
552 |
if (!S_ISREG(f->f_dentry->d_inode->i_mode)) goto out; |
553 |
if ((p = sb->s_fs_info = kmalloc(sizeof(*p), GFP_KERNEL)) == NULL) goto out; |
554 |
memset(p, 0, sizeof(*p)); |
555 |
p->is_permissive_mode = is_permissive_mode; |
556 |
INIT_LIST_HEAD(&((struct syaoran_sb_info *) sb->s_fs_info)->list); |
557 |
printk("SYAORAN: Reading '%s'\n", filename); |
558 |
error = ReadConfigFile(f, sb); |
559 |
out: |
560 |
if (error) printk("SYAORAN: Can't read '%s'\n", filename); |
561 |
filp_close(f, NULL); |
562 |
} else { |
563 |
printk("SYAORAN: Can't open '%s'\n", filename); |
564 |
} |
565 |
} else { |
566 |
printk("SYAORAN: Missing config-file path.\n"); |
567 |
} |
568 |
return error; |
569 |
} |
570 |
|
571 |
/* Get absolute pathname from mount point. */ |
572 |
static int GetLocalAbsolutePath(struct dentry *dentry, char *buffer, int buflen) |
573 |
{ |
574 |
char *start = buffer; |
575 |
char *end = buffer + buflen; |
576 |
int namelen; |
577 |
|
578 |
if (buflen < 256) goto out; |
579 |
|
580 |
*--end = '\0'; |
581 |
buflen--; |
582 |
for (;;) { |
583 |
struct dentry *parent; |
584 |
if (IS_ROOT(dentry)) break; |
585 |
parent = dentry->d_parent; |
586 |
namelen = dentry->d_name.len; |
587 |
buflen -= namelen + 1; |
588 |
if (buflen < 0) goto out; |
589 |
end -= namelen; |
590 |
memcpy(end, dentry->d_name.name, namelen); |
591 |
*--end = '/'; |
592 |
dentry = parent; |
593 |
} |
594 |
if (*end == '/') { buflen++; end++; } |
595 |
namelen = dentry->d_name.len; |
596 |
buflen -= namelen; |
597 |
if (buflen < 0) goto out; |
598 |
end -= namelen; |
599 |
memcpy(end, dentry->d_name.name, namelen); |
600 |
memmove(start, end, strlen(end) + 1); |
601 |
return 0; |
602 |
out: |
603 |
return -ENOMEM; |
604 |
} |
605 |
|
606 |
/* Get absolute pathname of the given dentry from mount point. */ |
607 |
static int local_realpath_from_dentry(struct dentry *dentry, char *newname, int newname_len) |
608 |
{ |
609 |
int error; |
610 |
struct dentry *d_dentry; |
611 |
if (!dentry || !newname || newname_len <= 0) return -EINVAL; |
612 |
d_dentry = dget(dentry); |
613 |
/***** CRITICAL SECTION START *****/ |
614 |
spin_lock(&dcache_lock); |
615 |
error = GetLocalAbsolutePath(d_dentry, newname, newname_len); |
616 |
spin_unlock(&dcache_lock); |
617 |
/***** CRITICAL SECTION END *****/ |
618 |
dput(d_dentry); |
619 |
return error; |
620 |
} |
621 |
|
622 |
static int CheckFlags(struct syaoran_sb_info *info, struct dentry *dentry, int mode, int dev, unsigned int flags) |
623 |
{ |
624 |
int error = -EPERM; |
625 |
/* I use static buffer, for local_realpath_from_dentry() needs dcache_lock. */ |
626 |
static char filename[PAGE_SIZE]; |
627 |
static spinlock_t lock = SPIN_LOCK_UNLOCKED; |
628 |
spin_lock(&lock); |
629 |
memset(filename, 0, sizeof(filename)); |
630 |
if (local_realpath_from_dentry(dentry, filename, sizeof(filename) - 1) == 0) { |
631 |
struct dev_entry *entry; |
632 |
list_for_each_entry(entry, &info->list, list) { |
633 |
if ((mode & S_IFMT) != (entry->mode & S_IFMT)) continue; |
634 |
if ((S_ISBLK(mode) || S_ISCHR(mode)) && dev != entry->kdev) continue; |
635 |
if (strcmp(entry->name, filename + 1)) continue; |
636 |
if (info->is_permissive_mode) { |
637 |
entry->flags |= flags; |
638 |
error = 0; |
639 |
} else { |
640 |
if ((entry->flags & flags) == flags) error = 0; |
641 |
} |
642 |
break; |
643 |
} |
644 |
} |
645 |
if (error && strlen(filename) < (sizeof(filename) / 4) - 16) { |
646 |
const char *name; |
647 |
const uid_t uid = current->fsuid; |
648 |
const gid_t gid = current->fsgid; |
649 |
const mode_t perm = mode & 0777; |
650 |
flags &= ~DEVICE_USED; |
651 |
{ |
652 |
char *end = filename + sizeof(filename) - 1; |
653 |
const char *cp = strchr(filename, '\0') - 1; |
654 |
while (cp > filename) { |
655 |
const unsigned char c = *cp--; |
656 |
if (c == '\\') { |
657 |
*--end = '\\'; |
658 |
*--end = '\\'; |
659 |
} else if (c > ' ' && c < 127) { |
660 |
*--end = c; |
661 |
} else { |
662 |
*--end = (c & 7) + '0'; |
663 |
*--end = ((c >> 3) & 7) + '0'; |
664 |
*--end = (c >> 6) + '0'; |
665 |
*--end = '\\'; |
666 |
} |
667 |
} |
668 |
name = end; |
669 |
} |
670 |
switch (mode & S_IFMT) { |
671 |
case S_IFCHR: |
672 |
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %3u %3u\n", name, perm, uid, gid, flags, 'c', MAJOR(dev), MINOR(dev)); |
673 |
break; |
674 |
case S_IFBLK: |
675 |
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %3u %3u\n", name, perm, uid, gid, flags, 'b', MAJOR(dev), MINOR(dev)); |
676 |
break; |
677 |
case S_IFIFO: |
678 |
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'p'); |
679 |
break; |
680 |
case S_IFSOCK: |
681 |
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 's'); |
682 |
break; |
683 |
case S_IFDIR: |
684 |
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'd'); |
685 |
break; |
686 |
case S_IFLNK: |
687 |
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %s\n", name, perm, uid, gid, flags, 'l', "unknown"); |
688 |
break; |
689 |
case S_IFREG: |
690 |
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'f'); |
691 |
break; |
692 |
} |
693 |
} |
694 |
spin_unlock(&lock); |
695 |
return error; |
696 |
} |
697 |
|
698 |
/* Check whether the given dentry is allowed to mknod. */ |
699 |
static int MayCreateNode(struct dentry *dentry, int mode, int dev) |
700 |
{ |
701 |
struct syaoran_sb_info *info = (struct syaoran_sb_info *) dentry->d_sb->s_fs_info; |
702 |
if (!info) { |
703 |
printk("%s: dentry->d_sb->s_fs_info == NULL\n", __FUNCTION__); |
704 |
return -EPERM; |
705 |
} |
706 |
if (!info->initialize_done) return 0; |
707 |
return CheckFlags(info, dentry, mode, dev, MAY_CREATE); |
708 |
} |
709 |
|
710 |
/* Check whether the given dentry is allowed to chmod/chown/unlink. */ |
711 |
static int MayModifyNode(struct dentry *dentry, unsigned int flags) |
712 |
{ |
713 |
struct syaoran_sb_info *info = (struct syaoran_sb_info *) dentry->d_sb->s_fs_info; |
714 |
if (!info) { |
715 |
printk("%s: dentry->d_sb->s_fs_info == NULL\n", __FUNCTION__); |
716 |
return -EPERM; |
717 |
} |
718 |
if (flags == DEVICE_USED && !info->is_permissive_mode) return 0; |
719 |
if (!dentry->d_inode) return -ENOENT; |
720 |
return CheckFlags(info, dentry, dentry->d_inode->i_mode, dentry->d_inode->i_rdev, flags); |
721 |
} |
722 |
|
723 |
/* |
724 |
* The following structure and codes are used for transferring data to interfaces files. |
725 |
*/ |
726 |
|
727 |
struct syaoran_read_struct { |
728 |
char *buf; /* Buffer for reading. */ |
729 |
int avail; /* Bytes available for reading. */ |
730 |
struct super_block *sb; /* The super_block of this partition. */ |
731 |
struct dev_entry *entry; /* The entry currently reading from. */ |
732 |
_Bool read_all; /* Dump all entries? */ |
733 |
struct list_head *pos; /* Current position. */ |
734 |
}; |
735 |
|
736 |
static void ReadTable(struct syaoran_read_struct *head, char *buf, int count) |
737 |
{ |
738 |
struct super_block *sb = head->sb; |
739 |
struct syaoran_sb_info *info = (struct syaoran_sb_info *) sb->s_fs_info; |
740 |
struct list_head *pos; |
741 |
const _Bool read_all = head->read_all; |
742 |
if (!info) return; |
743 |
if (!head->pos) return; |
744 |
list_for_each_cookie(pos, head->pos, &info->list) { |
745 |
struct dev_entry *entry = list_entry(pos, struct dev_entry, list); |
746 |
const unsigned int flags = read_all ? entry->flags : entry->flags & ~DEVICE_USED; |
747 |
const char *name = entry->printable_name; |
748 |
const uid_t uid = entry->uid; |
749 |
const gid_t gid = entry->gid; |
750 |
const mode_t perm = entry->mode & 0777; |
751 |
int len = 0; |
752 |
switch (entry->mode & S_IFMT) { |
753 |
case S_IFCHR: |
754 |
if (!head->read_all && !(entry->flags & DEVICE_USED)) break; |
755 |
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c %3u %3u\n", name, perm, uid, gid, flags, 'c', MAJOR(entry->kdev), MINOR(entry->kdev)); |
756 |
break; |
757 |
case S_IFBLK: |
758 |
if (!head->read_all && !(entry->flags & DEVICE_USED)) break; |
759 |
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c %3u %3u\n", name, perm, uid, gid, flags, 'b', MAJOR(entry->kdev), MINOR(entry->kdev)); |
760 |
break; |
761 |
case S_IFIFO: |
762 |
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'p'); |
763 |
break; |
764 |
case S_IFSOCK: |
765 |
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 's'); |
766 |
break; |
767 |
case S_IFDIR: |
768 |
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'd'); |
769 |
break; |
770 |
case S_IFLNK: |
771 |
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c %s\n", name, perm, uid, gid, flags, 'l', entry->printable_symlink_data); |
772 |
break; |
773 |
case S_IFREG: |
774 |
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'f'); |
775 |
break; |
776 |
} |
777 |
if (len < 0 || count <= len) break; |
778 |
count -= len; |
779 |
buf += len; |
780 |
head->avail += len; |
781 |
} |
782 |
} |
783 |
|
784 |
static int syaoran_trace_open(struct inode *inode, struct file *file) |
785 |
{ |
786 |
struct syaoran_read_struct *head; |
787 |
if ((head = kmalloc(sizeof(*head), GFP_KERNEL)) == NULL) return -ENOMEM; |
788 |
memset(head, 0, sizeof(*head)); |
789 |
head->sb = inode->i_sb; |
790 |
head->read_all = (strcmp(file->f_dentry->d_name.name, ".syaoran_all") == 0); |
791 |
head->pos = &((struct syaoran_sb_info *) head->sb->s_fs_info)->list; |
792 |
if ((head->buf = kmalloc(PAGE_SIZE * 2, GFP_KERNEL)) == NULL) { |
793 |
kfree(head); |
794 |
return -ENOMEM; |
795 |
} |
796 |
memset(head->buf, 0, PAGE_SIZE * 2); |
797 |
file->private_data = head; |
798 |
return 0; |
799 |
} |
800 |
|
801 |
static int syaoran_trace_release(struct inode *inode, struct file *file) |
802 |
{ |
803 |
struct syaoran_read_struct *head = file->private_data; |
804 |
kfree(head->buf); |
805 |
kfree(head); |
806 |
file->private_data = NULL; |
807 |
return 0; |
808 |
} |
809 |
|
810 |
static ssize_t syaoran_trace_read(struct file *file, char __user *buf, size_t count, loff_t * ppos) |
811 |
{ |
812 |
struct syaoran_read_struct *head = (struct syaoran_read_struct *) file->private_data; |
813 |
int len = head->avail; |
814 |
char *cp = head->buf; |
815 |
if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; |
816 |
ReadTable(head, cp + len, PAGE_SIZE * 2 - len); |
817 |
len = head->avail; |
818 |
if (len > count) len = count; |
819 |
if (len > 0) { |
820 |
if (copy_to_user(buf, cp, len)) return -EFAULT; |
821 |
head->avail -= len; |
822 |
memmove(cp, cp + len, head->avail); |
823 |
} |
824 |
return len; |
825 |
} |
826 |
|
827 |
static struct file_operations syaoran_trace_operations = { |
828 |
open: syaoran_trace_open, |
829 |
release: syaoran_trace_release, |
830 |
read: syaoran_trace_read, |
831 |
}; |
832 |
|
833 |
/* Create interface files for reading status. */ |
834 |
static int syaoran_create_tracelog(struct super_block *sb, const char *filename) |
835 |
{ |
836 |
struct dentry *base = dget(sb->s_root); |
837 |
struct dentry *dentry = lookup_create2(filename, base, 0); |
838 |
int error = PTR_ERR(dentry); |
839 |
if (!IS_ERR(dentry)) { |
840 |
struct inode *inode = new_inode(sb); |
841 |
if (inode) { |
842 |
inode->i_mode = S_IFREG | 0400; |
843 |
inode->i_uid = 0; |
844 |
inode->i_gid = 0; |
845 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) |
846 |
inode->i_blksize = PAGE_CACHE_SIZE; |
847 |
#endif |
848 |
inode->i_blocks = 0; |
849 |
inode->i_mapping->a_ops = &syaoran_aops; |
850 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) |
851 |
inode->i_mapping->backing_dev_info = &syaoran_backing_dev_info; |
852 |
inode->i_op = &syaoran_file_inode_operations; |
853 |
#else |
854 |
inode->i_rdev = NODEV; |
855 |
#endif |
856 |
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; |
857 |
inode->i_fop = &syaoran_trace_operations; |
858 |
d_instantiate(dentry, inode); |
859 |
dget(dentry); /* Extra count - pin the dentry in core */ |
860 |
error = 0; |
861 |
} |
862 |
dput(dentry); |
863 |
} |
864 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
865 |
mutex_unlock(&base->d_inode->i_mutex); |
866 |
#else |
867 |
up(&base->d_inode->i_sem); |
868 |
#endif |
869 |
dput(base); |
870 |
return error; |
871 |
} |
872 |
|
873 |
/***** SYAORAN end. *****/ |
874 |
#endif |