オープンソース・ソフトウェアの開発とダウンロード

Subversion リポジトリの参照

Contents of /trunk/1.7.x/ccs-patch/security/ccsecurity/util.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 111 - (show annotations) (download) (as text)
Wed Feb 28 11:45:08 2007 UTC (17 years, 2 months ago) by kumaneko
Original Path: trunk/ccs-patch/fs/ccs_common.c
File MIME type: text/x-csrc
File size: 51334 byte(s)


1 /*
2 * fs/ccs_common.c
3 *
4 * Common functions for SAKURA and TOMOYO.
5 *
6 * Copyright (C) 2005-2007 NTT DATA CORPORATION
7 *
8 * Version: 1.3.2 2007/02/14
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 #include <linux/string.h>
16 #include <linux/mm.h>
17 #include <linux/utime.h>
18 #include <linux/file.h>
19 #include <linux/module.h>
20 #include <linux/slab.h>
21 #include <asm/uaccess.h>
22 #include <stdarg.h>
23 #include <linux/version.h>
24 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
25 #include <linux/namei.h>
26 #include <linux/mount.h>
27 static const int lookup_flags = LOOKUP_FOLLOW;
28 #else
29 static const int lookup_flags = LOOKUP_FOLLOW | LOOKUP_POSITIVE;
30 #endif
31 #include <linux/version.h>
32 #include <linux/realpath.h>
33 #include <linux/ccs_common.h>
34 #include <linux/ccs_proc.h>
35 #include <linux/tomoyo.h>
36
37 #ifdef CONFIG_TOMOYO_MAX_ACCEPT_FILES
38 #define MAX_ACCEPT_FILES (CONFIG_TOMOYO_MAX_ACCEPT_FILES)
39 #else
40 #define MAX_ACCEPT_FILES 2048
41 #endif
42 #ifdef CONFIG_TOMOYO_MAX_GRANT_LOG
43 #define MAX_GRANT_LOG (CONFIG_TOMOYO_MAX_GRANT_LOG)
44 #else
45 #define MAX_GRANT_LOG 1024
46 #endif
47 #ifdef CONFIG_TOMOYO_MAX_REJECT_LOG
48 #define MAX_REJECT_LOG (CONFIG_TOMOYO_MAX_REJECT_LOG)
49 #else
50 #define MAX_REJECT_LOG 1024
51 #endif
52
53 /************************* VARIABLES *************************/
54
55 /* /sbin/init started? */
56 int sbin_init_started = 0;
57
58 const char *ccs_log_level = KERN_DEBUG;
59
60 static struct {
61 const char *keyword;
62 unsigned int current_value;
63 const unsigned int max_value;
64 } ccs_control_array[CCS_MAX_CONTROL_INDEX] = {
65 [CCS_PROFILE_COMMENT] = { "COMMENT", 0, 0 }, /* Reserved for string. */
66 [CCS_TOMOYO_MAC_FOR_FILE] = { "MAC_FOR_FILE", 0, 3 },
67 [CCS_TOMOYO_MAC_FOR_ARGV0] = { "MAC_FOR_ARGV0", 0, 3 },
68 [CCS_TOMOYO_MAC_FOR_NETWORK] = { "MAC_FOR_NETWORK", 0, 3 },
69 [CCS_TOMOYO_MAC_FOR_BINDPORT] = { "MAC_FOR_BINDPORT", 0, 3 },
70 [CCS_TOMOYO_MAC_FOR_CONNECTPORT] = { "MAC_FOR_CONNECTPORT", 0, 3 },
71 [CCS_TOMOYO_MAC_FOR_SIGNAL] = { "MAC_FOR_SIGNAL", 0, 3 },
72 [CCS_SAKURA_DENY_CONCEAL_MOUNT] = { "DENY_CONCEAL_MOUNT", 0, 3 },
73 [CCS_SAKURA_RESTRICT_CHROOT] = { "RESTRICT_CHROOT", 0, 3 },
74 [CCS_SAKURA_RESTRICT_MOUNT] = { "RESTRICT_MOUNT", 0, 3 },
75 [CCS_SAKURA_RESTRICT_UNMOUNT] = { "RESTRICT_UNMOUNT", 0, 3 },
76 [CCS_SAKURA_DENY_PIVOT_ROOT] = { "DENY_PIVOT_ROOT", 0, 3 },
77 [CCS_SAKURA_TRACE_READONLY] = { "TRACE_READONLY", 0, 1 },
78 [CCS_SAKURA_RESTRICT_AUTOBIND] = { "RESTRICT_AUTOBIND", 0, 1 },
79 [CCS_TOMOYO_MAX_ACCEPT_FILES] = { "MAX_ACCEPT_FILES", MAX_ACCEPT_FILES, INT_MAX },
80 [CCS_TOMOYO_MAX_GRANT_LOG] = { "MAX_GRANT_LOG", MAX_GRANT_LOG, INT_MAX },
81 [CCS_TOMOYO_MAX_REJECT_LOG] = { "MAX_REJECT_LOG", MAX_REJECT_LOG, INT_MAX },
82 [CCS_TOMOYO_VERBOSE] = { "TOMOYO_VERBOSE", 1, 1 },
83 [CCS_ALLOW_ENFORCE_GRACE] = { "ALLOW_ENFORCE_GRACE", 0, 1 },
84 };
85
86 typedef struct {
87 unsigned int value[CCS_MAX_CONTROL_INDEX];
88 const struct path_info *comment;
89 } PROFILE;
90
91 static PROFILE *profile_ptr[MAX_PROFILES];
92
93 /************************* UTILITY FUNCTIONS *************************/
94
95 #ifdef CONFIG_TOMOYO
96 static int __init TOMOYO_Quiet_Setup(char *str)
97 {
98 ccs_control_array[CCS_TOMOYO_VERBOSE].current_value = 0;
99 return 0;
100 }
101
102 __setup("TOMOYO_QUIET", TOMOYO_Quiet_Setup);
103 #endif
104
105 /* Am I root? */
106 static int isRoot(void)
107 {
108 return !current->uid && !current->euid;
109 }
110
111 /*
112 * Format string.
113 * Leading and trailing whitespaces are removed.
114 * Multiple whitespaces are packed into single space.
115 */
116 static void NormalizeLine(unsigned char *buffer)
117 {
118 unsigned char *sp = buffer, *dp = buffer;
119 int first = 1;
120 while (*sp && (*sp <= ' ' || *sp >= 127)) sp++;
121 while (*sp) {
122 if (!first) *dp++ = ' ';
123 first = 0;
124 while (*sp > ' ' && *sp < 127) *dp++ = *sp++;
125 while (*sp && (*sp <= ' ' || *sp >= 127)) sp++;
126 }
127 *dp = '\0';
128 }
129
130 /*
131 * Check whether the given filename follows the naming rules.
132 * Returns nonzero if follows, zero otherwise.
133 */
134 int IsCorrectPath(const char *filename, const int start_type, const int pattern_type, const int end_type, const char *function)
135 {
136 int contains_pattern = 0;
137 char c, d, e;
138 const char *original_filename = filename;
139 if (!filename) goto out;
140 c = *filename;
141 if (start_type == 1) { /* Must start with '/' */
142 if (c != '/') goto out;
143 } else if (start_type == -1) { /* Must not start with '/' */
144 if (c == '/') goto out;
145 }
146 if (c) c = * (strchr(filename, '\0') - 1);
147 if (end_type == 1) { /* Must end with '/' */
148 if (c != '/') goto out;
149 } else if (end_type == -1) { /* Must not end with '/' */
150 if (c == '/') goto out;
151 }
152 while ((c = *filename++) != '\0') {
153 if (c == '\\') {
154 switch ((c = *filename++)) {
155 case '\\': /* "\\" */
156 continue;
157 case '$': /* "\$" */
158 case '+': /* "\+" */
159 case '?': /* "\?" */
160 case '*': /* "\*" */
161 case '@': /* "\@" */
162 case 'x': /* "\x" */
163 case 'X': /* "\X" */
164 case 'a': /* "\a" */
165 case 'A': /* "\A" */
166 if (pattern_type == -1) break; /* Must not contain pattern */
167 contains_pattern = 1;
168 continue;
169 case '0': /* "\ooo" */
170 case '1':
171 case '2':
172 case '3':
173 if ((d = *filename++) >= '0' && d <= '7' && (e = *filename++) >= '0' && e <= '7') {
174 const unsigned char f =
175 (((unsigned char) (c - '0')) << 6) +
176 (((unsigned char) (d - '0')) << 3) +
177 (((unsigned char) (e - '0')));
178 if (f && (f <= ' ' || f >= 127)) continue; /* pattern is not \000 */
179 }
180 }
181 goto out;
182 } else if (c <= ' ' || c >= 127) {
183 goto out;
184 }
185 }
186 if (pattern_type == 1) { /* Must contain pattern */
187 if (!contains_pattern) goto out;
188 }
189 return 1;
190 out:
191 printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function, original_filename);
192 return 0;
193 }
194
195 /*
196 * Check whether the given domainname follows the naming rules.
197 * Returns nonzero if follows, zero otherwise.
198 */
199 int IsCorrectDomain(const unsigned char *domainname, const char *function)
200 {
201 unsigned char c, d, e;
202 const char *org_domainname = domainname;
203 if (!domainname || strncmp(domainname, ROOT_NAME, ROOT_NAME_LEN)) goto out;
204 domainname += ROOT_NAME_LEN;
205 if (!*domainname) return 1;
206 do {
207 if (*domainname++ != ' ') goto out;
208 if (*domainname++ != '/') goto out;
209 while ((c = *domainname) != '\0' && c != ' ') {
210 domainname++;
211 if (c == '\\') {
212 switch ((c = *domainname++)) {
213 case '\\': /* "\\" */
214 continue;
215 case '0': /* "\ooo" */
216 case '1':
217 case '2':
218 case '3':
219 if ((d = *domainname++) >= '0' && d <= '7' && (e = *domainname++) >= '0' && e <= '7') {
220 const unsigned char f =
221 (((unsigned char) (c - '0')) << 6) +
222 (((unsigned char) (d - '0')) << 3) +
223 (((unsigned char) (e - '0')));
224 if (f && (f <= ' ' || f >= 127)) continue; /* pattern is not \000 */
225 }
226 }
227 goto out;
228 } else if (c < ' ' || c >= 127) {
229 goto out;
230 }
231 }
232 } while (*domainname);
233 return 1;
234 out:
235 printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function, org_domainname);
236 return 0;
237 }
238
239 static int PathDepth(const char *pathname)
240 {
241 int i = 0;
242 if (pathname) {
243 char *ep = strchr(pathname, '\0');
244 if (pathname < ep--) {
245 if (*ep != '/') i++;
246 while (pathname <= ep) if (*ep-- == '/') i += 2;
247 }
248 }
249 return i;
250 }
251
252 static int const_part_length(const char *filename)
253 {
254 int len = 0;
255 if (filename) {
256 char c;
257 while ((c = *filename++) != '\0') {
258 if (c != '\\') { len++; continue; }
259 switch (c = *filename++) {
260 case '\\': /* "\\" */
261 len += 2; continue;
262 case '0': /* "\ooo" */
263 case '1':
264 case '2':
265 case '3':
266 if ((c = *filename++) >= '0' && c <= '7' && (c = *filename++) >= '0' && c <= '7') { len += 4; continue; }
267 }
268 break;
269 }
270 }
271 return len;
272 }
273
274 void fill_path_info(struct path_info *ptr)
275 {
276 const char *name = ptr->name;
277 const int len = strlen(name);
278 ptr->total_len = len;
279 ptr->const_len = const_part_length(name);
280 ptr->is_dir = len && (name[len - 1] == '/');
281 ptr->is_patterned = (ptr->const_len < len);
282 ptr->hash = full_name_hash(name, len);
283 ptr->depth = PathDepth(name);
284 }
285
286 static int FileMatchesToPattern(const char *filename, const char *filename_end, const char *pattern, const char *pattern_end)
287 {
288 while (filename < filename_end && pattern < pattern_end) {
289 if (*pattern != '\\') {
290 if (*filename++ != *pattern++) return 0;
291 } else {
292 char c = *filename;
293 pattern++;
294 switch (*pattern) {
295 case '?':
296 if (c == '/') {
297 return 0;
298 } else if (c == '\\') {
299 if ((c = filename[1]) == '\\') {
300 filename++; /* safe because filename is \\ */
301 } else if (c >= '0' && c <= '3' && (c = filename[2]) >= '0' && c <= '7' && (c = filename[3]) >= '0' && c <= '7') {
302 filename += 3; /* safe because filename is \ooo */
303 } else {
304 return 0;
305 }
306 }
307 break;
308 case '\\':
309 if (c != '\\') return 0;
310 if (*++filename != '\\') return 0; /* safe because *filename != '\0' */
311 break;
312 case '+':
313 if (c < '0' || c > '9') return 0;
314 break;
315 case 'x':
316 if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) return 0;
317 break;
318 case 'a':
319 if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) return 0;
320 break;
321 case '0':
322 case '1':
323 case '2':
324 case '3':
325 if (c == '\\' && (c = filename[1]) >= '0' && c <= '3' && c == *pattern
326 && (c = filename[2]) >= '0' && c <= '7' && c == pattern[1]
327 && (c = filename[3]) >= '0' && c <= '7' && c == pattern[2]) {
328 filename += 3; /* safe because filename is \ooo */
329 pattern += 2; /* safe because pattern is \ooo */
330 break;
331 }
332 return 0; /* Not matched. */
333 case '*':
334 case '@':
335 {
336 int i;
337 for (i = 0; i <= filename_end - filename; i++) {
338 if (FileMatchesToPattern(filename + i, filename_end, pattern + 1, pattern_end)) return 1;
339 if ((c = filename[i]) == '.' && *pattern == '@') break;
340 if (c == '\\') {
341 if ((c = filename[i + 1]) == '\\') {
342 i++; /* safe because filename is \\ */
343 } else if (c >= '0' && c <= '3' && (c = filename[i + 2]) >= '0' && c <= '7' && (c = filename[i + 3]) >= '0' && c <= '7') {
344 i += 3; /* safe because filename is \ooo */
345 } else {
346 break; /* Bad pattern. */
347 }
348 }
349 }
350 return 0; /* Not matched. */
351 }
352 default:
353 {
354 int i, j = 0;
355 if ((c = *pattern) == '$') {
356 while ((c = filename[j]) >= '0' && c <= '9') j++;
357 } else if (c == 'X') {
358 while (((c = filename[j]) >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) j++;
359 } else if (c == 'A') {
360 while (((c = filename[j]) >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) j++;
361 }
362 for (i = 1; i <= j; i++) {
363 if (FileMatchesToPattern(filename + i, filename_end, pattern + 1, pattern_end)) return 1;
364 }
365 }
366 return 0; /* Not matched or bad pattern. */
367 }
368 filename++; /* safe because *filename != '\0' */
369 pattern++; /* safe because *pattern != '\0' */
370 }
371 }
372 while (*pattern == '\\' && (*(pattern + 1) == '*' || *(pattern + 1) == '@')) pattern += 2;
373 return (filename == filename_end && pattern == pattern_end);
374 }
375
376 /*
377 * Check whether the given pathname matches to the given pattern.
378 * Returns nonzero if matches, zero otherwise.
379 *
380 * The following patterns are available.
381 * \\ \ itself.
382 * \ooo Octal representation of a byte.
383 * \* More than or equals to 0 character other than '/'.
384 * \@ More than or equals to 0 character other than '/' or '.'.
385 * \? 1 byte character other than '/'.
386 * \$ More than or equals to 1 decimal digit.
387 * \+ 1 decimal digit.
388 * \X More than or equals to 1 hexadecimal digit.
389 * \x 1 hexadecimal digit.
390 * \A More than or equals to 1 alphabet character.
391 * \a 1 alphabet character.
392 */
393
394 int PathMatchesToPattern(const struct path_info *pathname0, const struct path_info *pattern0)
395 {
396 //if (!pathname || !pattern) return 0;
397 const char *pathname = pathname0->name, *pattern = pattern0->name;
398 const int len = pattern0->const_len;
399 if (!pattern0->is_patterned) return !pathcmp(pathname0, pattern0);
400 if (pathname0->depth != pattern0->depth) return 0;
401 if (strncmp(pathname, pattern, len)) return 0;
402 pathname += len; pattern += len;
403 while (*pathname && *pattern) {
404 const char *pathname_delimiter = strchr(pathname, '/'), *pattern_delimiter = strchr(pattern, '/');
405 if (!pathname_delimiter) pathname_delimiter = strchr(pathname, '\0');
406 if (!pattern_delimiter) pattern_delimiter = strchr(pattern, '\0');
407 if (!FileMatchesToPattern(pathname, pathname_delimiter, pattern, pattern_delimiter)) return 0;
408 pathname = *pathname_delimiter ? pathname_delimiter + 1 : pathname_delimiter;
409 pattern = *pattern_delimiter ? pattern_delimiter + 1 : pattern_delimiter;
410 }
411 while (*pattern == '\\' && (*(pattern + 1) == '*' || *(pattern + 1) == '@')) pattern += 2;
412 return (!*pathname && !*pattern);
413 }
414
415 /*
416 * Transactional printf() to IO_BUFFER structure.
417 * snprintf() will truncate, but io_printf() won't.
418 * Returns zero on success, nonzero otherwise.
419 */
420 int io_printf(IO_BUFFER *head, const char *fmt, ...)
421 {
422 va_list args;
423 int len, pos = head->read_avail, size = head->readbuf_size - pos;
424 if (size <= 0) return -ENOMEM;
425 va_start(args, fmt);
426 len = vsnprintf(head->read_buf + pos, size, fmt, args);
427 va_end(args);
428 if (pos + len >= head->readbuf_size) return -ENOMEM;
429 head->read_avail += len;
430 return 0;
431 }
432
433 /*
434 * Get realpath() of current process.
435 * This function uses ccs_alloc(), so caller must ccs_free() if this function didn't return NULL.
436 */
437 const char *GetEXE(void)
438 {
439 if (current->mm) {
440 struct vm_area_struct *vma = current->mm->mmap;
441 while (vma) {
442 if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
443 return realpath_from_dentry(vma->vm_file->f_dentry, vma->vm_file->f_vfsmnt);
444 }
445 vma = vma->vm_next;
446 }
447 }
448 return NULL;
449 }
450
451 const char *GetMSG(const int is_enforce)
452 {
453 if (is_enforce) return "ERROR"; else return "WARNING";
454 }
455
456 /************************* DOMAIN POLICY HANDLER *************************/
457
458 /* Check whether the given access control is enabled. */
459 unsigned int CheckCCSFlags(const unsigned int index)
460 {
461 const u8 profile = current->domain_info->profile;
462 return sbin_init_started && index < CCS_MAX_CONTROL_INDEX && profile < MAX_PROFILES && profile_ptr[profile] ? profile_ptr[profile]->value[index] : 0;
463 }
464
465 unsigned int TomoyoVerboseMode(void)
466 {
467 return CheckCCSFlags(CCS_TOMOYO_VERBOSE);
468 }
469
470 /* Check whether the given access control is enforce mode. */
471 unsigned int CheckCCSEnforce(const unsigned int index)
472 {
473 return CheckCCSFlags(index) == 3;
474 }
475
476 /* Check whether the given access control is accept mode. */
477 unsigned int CheckCCSAccept(const unsigned int index)
478 {
479 return CheckCCSFlags(index) == 1;
480 }
481
482 static PROFILE *FindOrAssignNewProfile(const unsigned int profile)
483 {
484 static DECLARE_MUTEX(profile_lock);
485 PROFILE *ptr = NULL;
486 down(&profile_lock);
487 if (profile < MAX_PROFILES && (ptr = profile_ptr[profile]) == NULL) {
488 if ((ptr = (PROFILE *) alloc_element(sizeof(PROFILE))) != NULL) {
489 int i;
490 for (i = 0; i < CCS_MAX_CONTROL_INDEX; i++) ptr->value[i] = ccs_control_array[i].current_value;
491 mb(); /* Instead of using spinlock. */
492 profile_ptr[profile] = ptr;
493 }
494 }
495 up(&profile_lock);
496 return ptr;
497 }
498
499 static int profile_loaded = 0;
500
501 static int SetStatus(IO_BUFFER *head)
502 {
503 char *data = head->write_buf;
504 unsigned int i, value;
505 char *cp;
506 PROFILE *profile;
507 if (!isRoot()) return -EPERM;
508 i = simple_strtoul(data, &cp, 10);
509 if (data != cp) {
510 if (*cp != '-') return -EINVAL;
511 data= cp + 1;
512 }
513 profile = FindOrAssignNewProfile(i);
514 if (!profile) return -EINVAL;
515 cp = strchr(data, '=');
516 if (!cp) return -EINVAL;
517 *cp = '\0';
518 profile_loaded = 1;
519 UpdateCounter(CCS_UPDATES_COUNTER_STATUS);
520 if (strcmp(data, ccs_control_array[CCS_PROFILE_COMMENT].keyword) == 0) {
521 profile->comment = SaveName(cp + 1);
522 return 0;
523 }
524 if (sscanf(cp + 1, "%u", &value) != 1) return -EINVAL;
525 #ifdef CONFIG_TOMOYO_MAC_FOR_CAPABILITY
526 if (strncmp(data, KEYWORD_MAC_FOR_CAPABILITY, KEYWORD_MAC_FOR_CAPABILITY_LEN) == 0) {
527 return SetCapabilityStatus(data + KEYWORD_MAC_FOR_CAPABILITY_LEN, value, i);
528 }
529 #endif
530 for (i = 0; i < CCS_MAX_CONTROL_INDEX; i++) {
531 if (strcmp(data, ccs_control_array[i].keyword)) continue;
532 if (value > ccs_control_array[i].max_value) value = ccs_control_array[i].max_value;
533 profile->value[i] = value;
534 return 0;
535 }
536 return -EINVAL;
537 }
538
539 static int ReadStatus(IO_BUFFER *head)
540 {
541 if (!head->read_eof) {
542 if (!isRoot()) return -EPERM;
543 if (!head->read_var2) {
544 int step;
545 for (step = head->read_step; step < MAX_PROFILES * CCS_MAX_CONTROL_INDEX; step++) {
546 const int i = step / CCS_MAX_CONTROL_INDEX, j = step % CCS_MAX_CONTROL_INDEX;
547 const PROFILE *profile = profile_ptr[i];
548 head->read_step = step;
549 if (!profile) continue;
550 switch (j) {
551 case -1: // Dummy
552 #ifndef CONFIG_TOMOYO_MAC_FOR_FILE
553 case CCS_TOMOYO_MAC_FOR_FILE:
554 case CCS_TOMOYO_MAX_ACCEPT_FILES:
555 #endif
556 #ifndef CONFIG_TOMOYO_MAC_FOR_ARGV0
557 case CCS_TOMOYO_MAC_FOR_ARGV0:
558 #endif
559 #ifndef CONFIG_TOMOYO_MAC_FOR_NETWORKPORT
560 case CCS_TOMOYO_MAC_FOR_BINDPORT:
561 case CCS_TOMOYO_MAC_FOR_CONNECTPORT:
562 #endif
563 #ifndef CONFIG_TOMOYO_MAC_FOR_NETWORK
564 case CCS_TOMOYO_MAC_FOR_NETWORK:
565 #endif
566 #ifndef CONFIG_TOMOYO_MAC_FOR_SIGNAL
567 case CCS_TOMOYO_MAC_FOR_SIGNAL:
568 #endif
569 #ifndef CONFIG_SAKURA_DENY_CONCEAL_MOUNT
570 case CCS_SAKURA_DENY_CONCEAL_MOUNT:
571 #endif
572 #ifndef CONFIG_SAKURA_RESTRICT_CHROOT
573 case CCS_SAKURA_RESTRICT_CHROOT:
574 #endif
575 #ifndef CONFIG_SAKURA_RESTRICT_MOUNT
576 case CCS_SAKURA_RESTRICT_MOUNT:
577 #endif
578 #ifndef CONFIG_SAKURA_RESTRICT_UNMOUNT
579 case CCS_SAKURA_RESTRICT_UNMOUNT:
580 #endif
581 #ifndef CONFIG_SAKURA_DENY_PIVOT_ROOT
582 case CCS_SAKURA_DENY_PIVOT_ROOT:
583 #endif
584 #ifndef CONFIG_SAKURA_TRACE_READONLY
585 case CCS_SAKURA_TRACE_READONLY:
586 #endif
587 #ifndef CONFIG_SAKURA_RESTRICT_AUTOBIND
588 case CCS_SAKURA_RESTRICT_AUTOBIND:
589 #endif
590 #ifndef CONFIG_TOMOYO
591 case CCS_TOMOYO_MAX_GRANT_LOG:
592 case CCS_TOMOYO_MAX_REJECT_LOG:
593 case CCS_TOMOYO_VERBOSE:
594 #endif
595 continue;
596 }
597 if (j == CCS_PROFILE_COMMENT) {
598 if (io_printf(head, "%u-%s=%s\n", i, ccs_control_array[CCS_PROFILE_COMMENT].keyword, profile->comment ? profile->comment->name : "")) break;
599 } else {
600 if (io_printf(head, "%u-%s=%u\n", i, ccs_control_array[j].keyword, profile->value[j])) break;
601 }
602 }
603 if (step == MAX_PROFILES * CCS_MAX_CONTROL_INDEX) {
604 head->read_var2 = (void *) "";
605 head->read_step = 0;
606 }
607 }
608 if (head->read_var2) {
609 #ifdef CONFIG_TOMOYO_MAC_FOR_CAPABILITY
610 if (ReadCapabilityStatus(head) == 0) head->read_eof = 1;
611 #else
612 head->read_eof = 1;
613 #endif
614 }
615 }
616 return 0;
617 }
618
619 /************************* POLICY MANAGER HANDLER *************************/
620
621 typedef struct policy_manager_entry {
622 struct policy_manager_entry *next;
623 const struct path_info *manager;
624 u8 is_domain;
625 u8 is_deleted;
626 } POLICY_MANAGER_ENTRY;
627
628 static POLICY_MANAGER_ENTRY *policy_manager_list = NULL;
629
630 static int AddManagerEntry(const char *manager, u8 is_delete)
631 {
632 POLICY_MANAGER_ENTRY *new_entry, *ptr;
633 static DECLARE_MUTEX(lock);
634 const struct path_info *saved_manager;
635 int error = -ENOMEM;
636 u8 is_domain = 0;
637 if (!isRoot()) return -EPERM;
638 if (IsDomainDef(manager)) {
639 if (!IsCorrectDomain(manager, __FUNCTION__)) return -EINVAL;
640 is_domain = 1;
641 } else {
642 if (!IsCorrectPath(manager, 1, -1, -1, __FUNCTION__)) return -EINVAL;
643 }
644 if ((saved_manager = SaveName(manager)) == NULL) return -ENOMEM;
645 down(&lock);
646 for (ptr = policy_manager_list; ptr; ptr = ptr->next) {
647 if (ptr->manager == saved_manager) {
648 ptr->is_deleted = is_delete;
649 error = 0;
650 goto out;
651 }
652 }
653 if (is_delete) {
654 error = -ENOENT;
655 goto out;
656 }
657 if ((new_entry = (POLICY_MANAGER_ENTRY *) alloc_element(sizeof(POLICY_MANAGER_ENTRY))) == NULL) goto out;
658 new_entry->manager = saved_manager;
659 new_entry->is_domain = is_domain;
660 mb(); /* Instead of using spinlock. */
661 if ((ptr = policy_manager_list) != NULL) {
662 while (ptr->next) ptr = ptr->next; ptr->next = new_entry;
663 } else {
664 policy_manager_list = new_entry;
665 }
666 error = 0;
667 out:
668 up(&lock);
669 if (!error) UpdateCounter(CCS_UPDATES_COUNTER_MANAGER);
670 return error;
671 }
672
673 static int AddManagerPolicy(IO_BUFFER *head)
674 {
675 const char *data = head->write_buf;
676 int is_delete = 0;
677 if (!isRoot()) return -EPERM;
678 if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {
679 data += KEYWORD_DELETE_LEN;
680 is_delete = 1;
681 }
682 return AddManagerEntry(data, is_delete);
683 }
684
685 static int ReadManagerPolicy(IO_BUFFER *head)
686 {
687 if (!head->read_eof) {
688 POLICY_MANAGER_ENTRY *ptr = (POLICY_MANAGER_ENTRY *) head->read_var2;
689 if (!isRoot()) return -EPERM;
690 if (!ptr) ptr = policy_manager_list;
691 while (ptr) {
692 head->read_var2 = (void *) ptr;
693 if (!ptr->is_deleted && io_printf(head, "%s\n", ptr->manager->name)) break;
694 ptr = ptr->next;
695 }
696 if (!ptr) head->read_eof = 1;
697 }
698 return 0;
699 }
700
701 /* Check whether the current process is a policy manager. */
702 static int IsPolicyManager(void)
703 {
704 POLICY_MANAGER_ENTRY *ptr;
705 const char *exe;
706 const struct path_info *domainname = current->domain_info->domainname;
707 if (!sbin_init_started) return 1;
708 for (ptr = policy_manager_list; ptr; ptr = ptr->next) {
709 if (!ptr->is_deleted && ptr->is_domain && !pathcmp(domainname, ptr->manager)) return 1;
710 }
711 if ((exe = GetEXE()) == NULL) return 0;
712 for (ptr = policy_manager_list; ptr; ptr = ptr->next) {
713 if (!ptr->is_deleted && !ptr->is_domain && !strcmp(exe, ptr->manager->name)) break;
714 }
715 if (!ptr) { /* Reduce error messages. */
716 static pid_t last_pid = 0;
717 const pid_t pid = current->pid;
718 if (last_pid != pid) {
719 printk("%s is not permitted to update policies.\n", exe);
720 last_pid = pid;
721 }
722 }
723 ccs_free(exe);
724 return ptr ? 1 : 0;
725 }
726
727 #ifdef CONFIG_TOMOYO
728
729 /************************* DOMAIN POLICY HANDLER *************************/
730
731 static int AddDomainPolicy(IO_BUFFER *head)
732 {
733 char *data = head->write_buf;
734 struct domain_info *domain = head->write_var1;
735 int is_delete = 0, is_select = 0, is_undelete = 0;
736 unsigned int profile;
737 if (!isRoot()) return -EPERM;
738 if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {
739 data += KEYWORD_DELETE_LEN;
740 is_delete = 1;
741 } else if (strncmp(data, KEYWORD_SELECT, KEYWORD_SELECT_LEN) == 0) {
742 data += KEYWORD_SELECT_LEN;
743 is_select = 1;
744 } else if (strncmp(data, KEYWORD_UNDELETE, KEYWORD_UNDELETE_LEN) == 0) {
745 data += KEYWORD_UNDELETE_LEN;
746 is_undelete = 1;
747 }
748 UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);
749 if (IsDomainDef(data)) {
750 if (is_delete) {
751 DeleteDomain(data);
752 domain = NULL;
753 } else if (is_select) {
754 domain = FindDomain(data);
755 } else if (is_undelete) {
756 domain = UndeleteDomain(data);
757 } else {
758 domain = FindOrAssignNewDomain(data, 0);
759 }
760 head->write_var1 = domain;
761 return 0;
762 }
763 if (!domain) return -EINVAL;
764 if (sscanf(data, KEYWORD_USE_PROFILE "%u", &profile) == 1 && profile < MAX_PROFILES) {
765 if (profile_ptr[profile] || !sbin_init_started) domain->profile = (u8) profile;
766 #ifdef CONFIG_TOMOYO_MAC_FOR_CAPABILITY
767 } else if (strncmp(data, KEYWORD_ALLOW_CAPABILITY, KEYWORD_ALLOW_CAPABILITY_LEN) == 0) {
768 return AddCapabilityPolicy(data + KEYWORD_ALLOW_CAPABILITY_LEN, domain, is_delete);
769 #endif
770 #ifdef CONFIG_TOMOYO_MAC_FOR_NETWORKPORT
771 } else if (strncmp(data, KEYWORD_ALLOW_BIND, KEYWORD_ALLOW_BIND_LEN) == 0 ||
772 strncmp(data, KEYWORD_ALLOW_CONNECT, KEYWORD_ALLOW_CONNECT_LEN) == 0) {
773 return AddPortPolicy(data, domain, is_delete);
774 #endif
775 #ifdef CONFIG_TOMOYO_MAC_FOR_NETWORK
776 } else if (strncmp(data, KEYWORD_ALLOW_NETWORK, KEYWORD_ALLOW_NETWORK_LEN) == 0) {
777 return AddNetworkPolicy(data + KEYWORD_ALLOW_NETWORK_LEN, domain, is_delete);
778 #endif
779 #ifdef CONFIG_TOMOYO_MAC_FOR_SIGNAL
780 } else if (strncmp(data, KEYWORD_ALLOW_SIGNAL, KEYWORD_ALLOW_SIGNAL_LEN) == 0) {
781 return AddSignalPolicy(data + KEYWORD_ALLOW_SIGNAL_LEN, domain, is_delete);
782 #endif
783 #ifdef CONFIG_TOMOYO_MAC_FOR_ARGV0
784 } else if (strncmp(data, KEYWORD_ALLOW_ARGV0, KEYWORD_ALLOW_ARGV0_LEN) == 0) {
785 return AddArgv0Policy(data + KEYWORD_ALLOW_ARGV0_LEN, domain, is_delete);
786 #endif
787 } else {
788 #ifdef CONFIG_TOMOYO_MAC_FOR_FILE
789 return AddFilePolicy(data, domain, is_delete);
790 #endif
791 }
792 return -EINVAL;
793 }
794
795 static int ReadDomainPolicy(IO_BUFFER *head)
796 {
797 if (!head->read_eof) {
798 struct domain_info *domain = head->read_var1;
799 switch (head->read_step) {
800 case 0: break;
801 case 1: goto step1;
802 case 2: goto step2;
803 case 3: goto step3;
804 default: return -EINVAL;
805 }
806 if (!isRoot()) return -EPERM;
807 for (domain = &KERNEL_DOMAIN; domain; domain = domain->next) {
808 struct acl_info *ptr;
809 if (domain->is_deleted) continue;
810 head->read_var1 = domain;
811 head->read_var2 = NULL; head->read_step = 1;
812 step1:
813 if (io_printf(head, "%s\n" KEYWORD_USE_PROFILE "%u\n\n", domain->domainname->name, domain->profile)) break;
814 head->read_var2 = (void *) domain->first_acl_ptr; head->read_step = 2;
815 step2:
816 for (ptr = (struct acl_info *) head->read_var2; ptr; ptr = ptr->next) {
817 const u8 acl_type = ptr->type;
818 const int pos = head->read_avail;
819 head->read_var2 = (void *) ptr;
820 if (ptr->is_deleted) continue;
821 if (0) {
822 #ifdef CONFIG_TOMOYO_MAC_FOR_FILE
823 } else if (acl_type == TYPE_FILE_ACL) {
824 const unsigned char b = ptr->u.b[1];
825 if (io_printf(head, "%d %s%s", ptr->u.b[0], b ? "@" : "", b ? ((FILE_ACL_RECORD *) ptr)->u.group->group_name->name : ((FILE_ACL_RECORD *) ptr)->u.filename->name)
826 || DumpCondition(head, ptr->cond)) {
827 head->read_avail = pos; break;
828 }
829 #endif
830 #ifdef CONFIG_TOMOYO_MAC_FOR_ARGV0
831 } else if (acl_type == TYPE_ARGV0_ACL) {
832 if (io_printf(head, KEYWORD_ALLOW_ARGV0 "%s %s", ((ARGV0_ACL_RECORD *) ptr)->filename->name, ((ARGV0_ACL_RECORD *) ptr)->argv0->name) ||
833 DumpCondition(head, ptr->cond)) {
834 head->read_avail = pos; break;
835 }
836 #endif
837 #ifdef CONFIG_TOMOYO_MAC_FOR_CAPABILITY
838 } else if (acl_type == TYPE_CAPABILITY_ACL) {
839 if (io_printf(head, KEYWORD_ALLOW_CAPABILITY "%s", capability2keyword(ptr->u.w)) ||
840 DumpCondition(head, ptr->cond)) {
841 head->read_avail = pos; break;
842 }
843 #endif
844 #ifdef CONFIG_TOMOYO_MAC_FOR_NETWORKPORT
845 } else if (acl_type == TYPE_BIND_ACL || acl_type == TYPE_CONNECT_ACL) {
846 const int is_stream = ptr->u.w;
847 const u16 min_port = ((PORT_ACL_RECORD *) ptr)->min_port, max_port = ((PORT_ACL_RECORD *) ptr)->max_port;
848 if (min_port != max_port) {
849 if (io_printf(head, "%s%s/%u-%u", acl_type == TYPE_CONNECT_ACL ? KEYWORD_ALLOW_CONNECT : KEYWORD_ALLOW_BIND, is_stream ? "TCP" : "UDP", min_port, max_port) ||
850 DumpCondition(head, ptr->cond)) {
851 head->read_avail = pos; break;
852 }
853 } else {
854 if (io_printf(head, "%s%s/%u", acl_type == TYPE_CONNECT_ACL ? KEYWORD_ALLOW_CONNECT : KEYWORD_ALLOW_BIND, is_stream ? "TCP" : "UDP", min_port) ||
855 DumpCondition(head, ptr->cond)) {
856 head->read_avail = pos; break;
857 }
858 }
859 #endif
860 #ifdef CONFIG_TOMOYO_MAC_FOR_NETWORK
861 } else if (acl_type == TYPE_IP_NETWORK_ACL) {
862 if (io_printf(head, KEYWORD_ALLOW_NETWORK "%s ", network2keyword(ptr->u.b[0]))) break;
863 switch (ptr->u.b[1]) {
864 case IP_RECORD_TYPE_ADDRESS_GROUP:
865 if (io_printf(head, "@%s", ((IP_NETWORK_ACL_RECORD *) ptr)->u.group->group_name->name)) goto print_ip_record_out;
866 break;
867 case IP_RECORD_TYPE_IPv4:
868 {
869 const u32 min_address = ((IP_NETWORK_ACL_RECORD *) ptr)->u.ipv4.min, max_address = ((IP_NETWORK_ACL_RECORD *) ptr)->u.ipv4.max;
870 if (io_printf(head, "%u.%u.%u.%u", HIPQUAD(min_address))) goto print_ip_record_out;
871 if (min_address != max_address && io_printf(head, "-%u.%u.%u.%u", HIPQUAD(max_address))) goto print_ip_record_out;
872 }
873 break;
874 case IP_RECORD_TYPE_IPv6:
875 {
876 char buf[64];
877 const u16 *min_address = ((IP_NETWORK_ACL_RECORD *) ptr)->u.ipv6.min, *max_address = ((IP_NETWORK_ACL_RECORD *) ptr)->u.ipv6.max;
878 print_ipv6(buf, sizeof(buf), min_address);
879 if (io_printf(head, "%s", buf)) goto print_ip_record_out;
880 if (memcmp(min_address, max_address, 16)) {
881 print_ipv6(buf, sizeof(buf), max_address);
882 if (io_printf(head, "-%s", buf)) goto print_ip_record_out;
883 }
884 }
885 break;
886 }
887 {
888 const u16 min_port = ((IP_NETWORK_ACL_RECORD *) ptr)->min_port, max_port = ((IP_NETWORK_ACL_RECORD *) ptr)->max_port;
889 if (io_printf(head, " %u", min_port)) goto print_ip_record_out;
890 if (min_port != max_port && io_printf(head, "-%u", max_port)) goto print_ip_record_out;
891 }
892 if (DumpCondition(head, ptr->cond)) {
893 print_ip_record_out: ;
894 head->read_avail = pos; break;
895 }
896 #endif
897 #ifdef CONFIG_TOMOYO_MAC_FOR_SIGNAL
898 } else if (acl_type == TYPE_SIGNAL_ACL) {
899 if (io_printf(head, KEYWORD_ALLOW_SIGNAL "%u %s", ptr->u.w, ((SIGNAL_ACL_RECORD *) ptr)->domainname->name) ||
900 DumpCondition(head, ptr->cond)) {
901 head->read_avail = pos; break;
902 }
903 #endif
904 #ifdef CONFIG_TOMOYO_MAC_FOR_FILE
905 } else {
906 const char *keyword = acltype2keyword(acl_type);
907 if (keyword) {
908 if (acltype2paths(acl_type) == 2) {
909 const u8 b0 = ptr->u.b[0], b1 = ptr->u.b[1];
910 if (io_printf(head, "allow_%s %s%s %s%s", keyword, b0 ? "@" : "", b0 ? ((DOUBLE_ACL_RECORD *) ptr)->u1.group1->group_name->name : ((DOUBLE_ACL_RECORD *) ptr)->u1.filename1->name, b1 ? "@" : "", b1 ? ((DOUBLE_ACL_RECORD *) ptr)->u2.group2->group_name->name : ((DOUBLE_ACL_RECORD *) ptr)->u2.filename2->name)
911 || DumpCondition(head, ptr->cond)) {
912 head->read_avail = pos; break;
913 }
914 } else {
915 const u8 b = ptr->u.b[0];
916 if (io_printf(head, "allow_%s %s%s", keyword, b ? "@" : "", b ? ((SINGLE_ACL_RECORD *) ptr)->u.group->group_name->name : ((SINGLE_ACL_RECORD *) ptr)->u.filename->name)
917 || DumpCondition(head, ptr->cond)) {
918 head->read_avail = pos; break;
919 }
920 }
921 }
922 #endif
923 }
924 }
925 if (ptr) break;
926 head->read_var2 = NULL; head->read_step = 3;
927 step3:
928 if (io_printf(head, "\n")) break;
929 }
930 if (!domain) head->read_eof = 1;
931 }
932 return 0;
933 }
934
935 static int ReadDomainProfile(IO_BUFFER *head)
936 {
937 if (!head->read_eof) {
938 struct domain_info *domain;
939 if (head->read_step == 0) {
940 head->read_var1 = &KERNEL_DOMAIN;
941 head->read_step = 1;
942 }
943 if (!isRoot()) return -EPERM;
944 for (domain = head->read_var1; domain; domain = domain->next) {
945 if (domain->is_deleted) continue;
946 head->read_var1 = domain;
947 if (io_printf(head, "%u %s\n", domain->profile, domain->domainname->name)) break;
948 }
949 if (!domain) head->read_eof = 1;
950 }
951 return 0;
952 }
953
954 static int WritePID(IO_BUFFER *head)
955 {
956 head->read_step = (int) simple_strtoul(head->write_buf, NULL, 10);
957 head->read_eof = 0;
958 return 0;
959 }
960
961 static int ReadPID(IO_BUFFER *head)
962 {
963 if (head->read_avail == 0 && !head->read_eof) {
964 const int pid = head->read_step;
965 struct task_struct *p;
966 struct domain_info *domain = NULL;
967 /***** CRITICAL SECTION START *****/
968 read_lock(&tasklist_lock);
969 p = find_task_by_pid(pid);
970 if (p) domain = p->domain_info;
971 read_unlock(&tasklist_lock);
972 /***** CRITICAL SECTION END *****/
973 if (domain) io_printf(head, "%d %u %s", pid, domain->profile, domain->domainname->name);
974 head->read_eof = 1;
975 }
976 return 0;
977 }
978
979 static int UpdateDomainProfile(IO_BUFFER *head)
980 {
981 char *data = head->write_buf;
982 char *cp = strchr(data, ' ');
983 struct domain_info *domain;
984 unsigned int profile;
985 if (!isRoot()) return -EPERM;
986 if (!cp) return -EINVAL;
987 *cp = '\0';
988 domain = FindDomain(cp + 1);
989 profile = simple_strtoul(data, NULL, 10);
990 if (domain && profile < MAX_PROFILES && (profile_ptr[profile] || !sbin_init_started)) domain->profile = (u8) profile;
991 UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);
992 return 0;
993 }
994
995 #endif
996
997 /************************* EXCEPTION POLICY HANDLER *************************/
998
999 #ifdef CONFIG_TOMOYO
1000
1001 static int AddExceptionPolicy(IO_BUFFER *head)
1002 {
1003 char *data = head->write_buf;
1004 int is_delete = 0;
1005 if (!isRoot()) return -EPERM;
1006 UpdateCounter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY);
1007 if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {
1008 data += KEYWORD_DELETE_LEN;
1009 is_delete = 1;
1010 }
1011 if (strncmp(data, KEYWORD_KEEP_DOMAIN, KEYWORD_KEEP_DOMAIN_LEN) == 0) {
1012 return AddDomainKeeperPolicy(data + KEYWORD_KEEP_DOMAIN_LEN, 0, is_delete);
1013 } else if (strncmp(data, KEYWORD_NO_KEEP_DOMAIN, KEYWORD_NO_KEEP_DOMAIN_LEN) == 0) {
1014 return AddDomainKeeperPolicy(data + KEYWORD_NO_KEEP_DOMAIN_LEN, 1, is_delete);
1015 } else if (strncmp(data, KEYWORD_INITIALIZE_DOMAIN, KEYWORD_INITIALIZE_DOMAIN_LEN) == 0) {
1016 return AddDomainInitializerPolicy(data + KEYWORD_INITIALIZE_DOMAIN_LEN, 0, is_delete, 0);
1017 } else if (strncmp(data, KEYWORD_NO_INITIALIZE_DOMAIN, KEYWORD_NO_INITIALIZE_DOMAIN_LEN) == 0) {
1018 return AddDomainInitializerPolicy(data + KEYWORD_NO_INITIALIZE_DOMAIN_LEN, 1, is_delete, 0);
1019 } else if (strncmp(data, KEYWORD_INITIALIZER, KEYWORD_INITIALIZER_LEN) == 0) {
1020 return AddDomainInitializerPolicy(data + KEYWORD_INITIALIZER_LEN, 0, is_delete, 1);
1021 } else if (strncmp(data, KEYWORD_NO_INITIALIZER, KEYWORD_NO_INITIALIZER_LEN) == 0) {
1022 return AddDomainInitializerPolicy(data + KEYWORD_NO_INITIALIZER_LEN, 1, is_delete, 1);
1023 } else if (strncmp(data, KEYWORD_ALIAS, KEYWORD_ALIAS_LEN) == 0) {
1024 return AddAliasPolicy(data + KEYWORD_ALIAS_LEN, is_delete);
1025 } else if (strncmp(data, KEYWORD_AGGREGATOR, KEYWORD_AGGREGATOR_LEN) == 0) {
1026 return AddAggregatorPolicy(data + KEYWORD_AGGREGATOR_LEN, is_delete);
1027 #ifdef CONFIG_TOMOYO_MAC_FOR_FILE
1028 } else if (strncmp(data, KEYWORD_ALLOW_READ, KEYWORD_ALLOW_READ_LEN) == 0) {
1029 return AddGloballyReadablePolicy(data + KEYWORD_ALLOW_READ_LEN, is_delete);
1030 } else if (strncmp(data, KEYWORD_FILE_PATTERN, KEYWORD_FILE_PATTERN_LEN) == 0) {
1031 return AddPatternPolicy(data + KEYWORD_FILE_PATTERN_LEN, is_delete);
1032 } else if (strncmp(data, KEYWORD_PATH_GROUP, KEYWORD_PATH_GROUP_LEN) == 0) {
1033 return AddGroupPolicy(data + KEYWORD_PATH_GROUP_LEN, is_delete);
1034 } else if (strncmp(data, KEYWORD_DENY_REWRITE, KEYWORD_DENY_REWRITE_LEN) == 0) {
1035 return AddNoRewritePolicy(data + KEYWORD_DENY_REWRITE_LEN, is_delete);
1036 #endif
1037 #ifdef CONFIG_TOMOYO_MAC_FOR_NETWORK
1038 } else if (strncmp(data, KEYWORD_ADDRESS_GROUP, KEYWORD_ADDRESS_GROUP_LEN) == 0) {
1039 return AddAddressGroupPolicy(data + KEYWORD_ADDRESS_GROUP_LEN, is_delete);
1040 #endif
1041 }
1042 return -EINVAL;
1043 }
1044
1045 static int ReadExceptionPolicy(IO_BUFFER *head)
1046 {
1047 if (!head->read_eof) {
1048 switch (head->read_step) {
1049 case 0:
1050 if (!isRoot()) return -EPERM;
1051 head->read_var2 = NULL; head->read_step = 1;
1052 case 1:
1053 if (ReadDomainKeeperPolicy(head)) break;
1054 head->read_var2 = NULL; head->read_step = 2;
1055 case 2:
1056 #ifdef CONFIG_TOMOYO_MAC_FOR_FILE
1057 if (ReadGloballyReadablePolicy(head)) break;
1058 #endif
1059 head->read_var2 = NULL; head->read_step = 3;
1060 case 3:
1061 if (ReadDomainInitializerPolicy(head)) break;
1062 head->read_var2 = NULL; head->read_step = 4;
1063 case 4:
1064 if (ReadAliasPolicy(head)) break;
1065 head->read_var2 = NULL; head->read_step = 5;
1066 case 5:
1067 if (ReadAggregatorPolicy(head)) break;
1068 head->read_var2 = NULL; head->read_step = 6;
1069 case 6:
1070 #ifdef CONFIG_TOMOYO_MAC_FOR_FILE
1071 if (ReadPatternPolicy(head)) break;
1072 #endif
1073 head->read_var2 = NULL; head->read_step = 7;
1074 case 7:
1075 #ifdef CONFIG_TOMOYO_MAC_FOR_FILE
1076 if (ReadNoRewritePolicy(head)) break;
1077 #endif
1078 head->read_var2 = NULL; head->read_step = 8;
1079 case 8:
1080 #ifdef CONFIG_TOMOYO_MAC_FOR_FILE
1081 if (ReadGroupPolicy(head)) break;
1082 #endif
1083 head->read_var2 = NULL; head->read_step = 9;
1084 case 9:
1085 #ifdef CONFIG_TOMOYO_MAC_FOR_NETWORK
1086 if (ReadAddressGroupPolicy(head)) break;
1087 #endif
1088 head->read_eof = 1;
1089 break;
1090 default:
1091 return -EINVAL;
1092 }
1093 }
1094 return 0;
1095 }
1096
1097 #endif
1098
1099 /************************* SYSTEM POLICY HANDLER *************************/
1100
1101 #ifdef CONFIG_SAKURA
1102
1103 static int AddSystemPolicy(IO_BUFFER *head)
1104 {
1105 char *data = head->write_buf;
1106 int is_delete = 0;
1107 if (!isRoot()) return -EPERM;
1108 UpdateCounter(CCS_UPDATES_COUNTER_SYSTEM_POLICY);
1109 if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {
1110 data += KEYWORD_DELETE_LEN;
1111 is_delete = 1;
1112 }
1113 #ifdef CONFIG_SAKURA_RESTRICT_MOUNT
1114 if (strncmp(data, KEYWORD_ALLOW_MOUNT, KEYWORD_ALLOW_MOUNT_LEN) == 0)
1115 return AddMountPolicy(data + KEYWORD_ALLOW_MOUNT_LEN, is_delete);
1116 #endif
1117 #ifdef CONFIG_SAKURA_RESTRICT_UNMOUNT
1118 if (strncmp(data, KEYWORD_DENY_UNMOUNT, KEYWORD_DENY_UNMOUNT_LEN) == 0)
1119 return AddNoUmountPolicy(data + KEYWORD_DENY_UNMOUNT_LEN, is_delete);
1120 #endif
1121 #ifdef CONFIG_SAKURA_RESTRICT_CHROOT
1122 if (strncmp(data, KEYWORD_ALLOW_CHROOT, KEYWORD_ALLOW_CHROOT_LEN) == 0)
1123 return AddChrootPolicy(data + KEYWORD_ALLOW_CHROOT_LEN, is_delete);
1124 #endif
1125 #ifdef CONFIG_SAKURA_RESTRICT_AUTOBIND
1126 if (strncmp(data, KEYWORD_DENY_AUTOBIND, KEYWORD_DENY_AUTOBIND_LEN) == 0)
1127 return AddReservedPortPolicy(data + KEYWORD_DENY_AUTOBIND_LEN, is_delete);
1128 #endif
1129 return -EINVAL;
1130 }
1131
1132 static int ReadSystemPolicy(IO_BUFFER *head)
1133 {
1134 if (!head->read_eof) {
1135 switch (head->read_step) {
1136 case 0:
1137 if (!isRoot()) return -EPERM;
1138 head->read_var2 = NULL; head->read_step = 1;
1139 case 1:
1140 #ifdef CONFIG_SAKURA_RESTRICT_MOUNT
1141 if (ReadMountPolicy(head)) break;
1142 #endif
1143 head->read_var2 = NULL; head->read_step = 2;
1144 case 2:
1145 #ifdef CONFIG_SAKURA_RESTRICT_UNMOUNT
1146 if (ReadNoUmountPolicy(head)) break;
1147 #endif
1148 head->read_var2 = NULL; head->read_step = 3;
1149 case 3:
1150 #ifdef CONFIG_SAKURA_RESTRICT_CHROOT
1151 if (ReadChrootPolicy(head)) break;
1152 #endif
1153 head->read_var2 = NULL; head->read_step = 4;
1154 case 4:
1155 #ifdef CONFIG_SAKURA_RESTRICT_AUTOBIND
1156 if (ReadReservedPortPolicy(head)) break;
1157 #endif
1158 head->read_eof = 1;
1159 break;
1160 default:
1161 return -EINVAL;
1162 }
1163 }
1164 return 0;
1165 }
1166
1167 #endif
1168
1169 /************************* POLICY LOADER *************************/
1170
1171 static const char *ccs_loader = NULL;
1172
1173 static int __init CCS_loader_Setup(char *str)
1174 {
1175 ccs_loader = str;
1176 return 0;
1177 }
1178
1179 __setup("CCS_loader=", CCS_loader_Setup);
1180
1181 void CCS_LoadPolicy(const char *filename)
1182 {
1183 if (sbin_init_started) return;
1184 /*
1185 * Check filename is /sbin/init or /sbin/ccs-start .
1186 * /sbin/ccs-start is a dummy filename in case where /sbin/init can't be passed.
1187 * You can create /sbin/ccs-start by "ln -s /bin/true /sbin/ccs-start", for
1188 * only the pathname is needed to activate Mandatory Access Control.
1189 */
1190 if (strcmp(filename, "/sbin/init") != 0 && strcmp(filename, "/sbin/ccs-start") != 0) return;
1191 /*
1192 * Don't activate MAC if the path given by 'CCS_loader=' option doesn't exist.
1193 * If initrd.img includes /sbin/init but real-root-dev has not mounted on / yet,
1194 * activating MAC will block the system since policies are not loaded yet.
1195 * So let do_execve() call this function everytime.
1196 */
1197 {
1198 struct nameidata nd;
1199 if (!ccs_loader) ccs_loader = "/.init";
1200 if (path_lookup(ccs_loader, lookup_flags, &nd)) {
1201 printk("Not activating Mandatory Access Control now since %s doesn't exist.\n", ccs_loader);
1202 return;
1203 }
1204 path_release(&nd);
1205 }
1206
1207 #ifdef CONFIG_SAKURA
1208 printk("SAKURA: 1.3.3-pre1 2007/02/28\n");
1209 #endif
1210 #ifdef CONFIG_TOMOYO
1211 printk("TOMOYO: 1.3.3-pre1 2007/02/28\n");
1212 #endif
1213 if (!profile_loaded) panic("No profiles loaded. Run policy loader using 'init=' option.\n");
1214 printk("Mandatory Access Control activated.\n");
1215 sbin_init_started = 1;
1216 ccs_log_level = KERN_WARNING;
1217 { /* Check all profiles currently assigned to domains are defined. */
1218 struct domain_info *domain;
1219 for (domain = &KERNEL_DOMAIN; domain; domain = domain->next) {
1220 const u8 profile = domain->profile;
1221 if (!profile_ptr[profile]) panic("Profile %u (used by '%s') not defined.\n", profile, domain->domainname->name);
1222 }
1223 }
1224 }
1225
1226
1227 /************************* MAC Decision Delayer *************************/
1228
1229 static DECLARE_WAIT_QUEUE_HEAD(query_wait);
1230
1231 static spinlock_t query_lock = SPIN_LOCK_UNLOCKED;
1232
1233 typedef struct query_entry {
1234 struct list_head list;
1235 char *query;
1236 int query_len;
1237 unsigned int serial;
1238 int timer;
1239 int answer;
1240 } QUERY_ENTRY;
1241
1242 static LIST_HEAD(query_list);
1243 static atomic_t queryd_watcher = ATOMIC_INIT(0);
1244
1245 int CheckSupervisor(const char *fmt, ...)
1246 {
1247 va_list args;
1248 int error = -EPERM;
1249 int pos, len;
1250 static unsigned int serial = 0;
1251 QUERY_ENTRY *query_entry;
1252 if (!CheckCCSFlags(CCS_ALLOW_ENFORCE_GRACE)) return -EPERM;
1253 if (!atomic_read(&queryd_watcher)) return -EPERM;
1254 va_start(args, fmt);
1255 len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
1256 va_end(args);
1257 if ((query_entry = (QUERY_ENTRY *) ccs_alloc(sizeof(QUERY_ENTRY))) == NULL ||
1258 (query_entry->query = ccs_alloc(len)) == NULL) goto out;
1259 INIT_LIST_HEAD(&query_entry->list);
1260 /***** CRITICAL SECTION START *****/
1261 spin_lock(&query_lock);
1262 query_entry->serial = serial++;
1263 spin_unlock(&query_lock);
1264 /***** CRITICAL SECTION END *****/
1265 pos = snprintf(query_entry->query, len - 1, "Q%u\n", query_entry->serial);
1266 va_start(args, fmt);
1267 vsnprintf(query_entry->query + pos, len - 1 - pos, fmt, args);
1268 query_entry->query_len = strlen(query_entry->query) + 1;
1269 va_end(args);
1270 /***** CRITICAL SECTION START *****/
1271 spin_lock(&query_lock);
1272 list_add_tail(&query_entry->list, &query_list);
1273 spin_unlock(&query_lock);
1274 /***** CRITICAL SECTION END *****/
1275 UpdateCounter(CCS_UPDATES_COUNTER_QUERY);
1276 /* Give 10 seconds for supervisor's opinion. */
1277 for (query_entry->timer = 0; atomic_read(&queryd_watcher) && CheckCCSFlags(CCS_ALLOW_ENFORCE_GRACE) && query_entry->timer < 100; query_entry->timer++) {
1278 wake_up(&query_wait);
1279 set_current_state(TASK_INTERRUPTIBLE);
1280 schedule_timeout(HZ / 10);
1281 if (query_entry->answer) break;
1282 }
1283 UpdateCounter(CCS_UPDATES_COUNTER_QUERY);
1284 /***** CRITICAL SECTION START *****/
1285 spin_lock(&query_lock);
1286 list_del(&query_entry->list);
1287 spin_unlock(&query_lock);
1288 /***** CRITICAL SECTION END *****/
1289 switch (query_entry->answer) {
1290 case 1:
1291 /* Granted by administrator. */
1292 error = 0;
1293 break;
1294 case 0:
1295 /* Timed out. */
1296 break;
1297 default:
1298 /* Rejected by administrator. */
1299 break;
1300 }
1301 out: ;
1302 if (query_entry) ccs_free(query_entry->query);
1303 ccs_free(query_entry);
1304 return error;
1305 }
1306
1307 static int PollQuery(struct file *file, poll_table *wait)
1308 {
1309 int found;
1310 /***** CRITICAL SECTION START *****/
1311 spin_lock(&query_lock);
1312 found = !list_empty(&query_list);
1313 spin_unlock(&query_lock);
1314 /***** CRITICAL SECTION END *****/
1315 if (found) return POLLIN | POLLRDNORM;
1316 poll_wait(file, &query_wait, wait);
1317 /***** CRITICAL SECTION START *****/
1318 spin_lock(&query_lock);
1319 found = !list_empty(&query_list);
1320 spin_unlock(&query_lock);
1321 /***** CRITICAL SECTION END *****/
1322 if (found) return POLLIN | POLLRDNORM;
1323 return 0;
1324 }
1325
1326 static int ReadQuery(IO_BUFFER *head)
1327 {
1328 struct list_head *tmp;
1329 int pos = 0, len = 0;
1330 char *buf;
1331 if (head->read_avail) return 0;
1332 if (head->read_buf) {
1333 ccs_free(head->read_buf); head->read_buf = NULL;
1334 head->readbuf_size = 0;
1335 }
1336 /***** CRITICAL SECTION START *****/
1337 spin_lock(&query_lock);
1338 list_for_each(tmp, &query_list) {
1339 QUERY_ENTRY *ptr = list_entry(tmp, QUERY_ENTRY, list);
1340 if (pos++ == head->read_step) {
1341 len = ptr->query_len;
1342 break;
1343 }
1344 }
1345 spin_unlock(&query_lock);
1346 /***** CRITICAL SECTION END *****/
1347 if (!len) {
1348 head->read_step = 0;
1349 return 0;
1350 }
1351 if ((buf = (char *) ccs_alloc(len)) != NULL) {
1352 pos = 0;
1353 /***** CRITICAL SECTION START *****/
1354 spin_lock(&query_lock);
1355 list_for_each(tmp, &query_list) {
1356 QUERY_ENTRY *ptr = list_entry(tmp, QUERY_ENTRY, list);
1357 if (pos++ == head->read_step) {
1358 /* Some query can be skiipped since query_list can change, but I don't care. */
1359 if (len == ptr->query_len) memmove(buf, ptr->query, len);
1360 break;
1361 }
1362 }
1363 spin_unlock(&query_lock);
1364 /***** CRITICAL SECTION END *****/
1365 if (buf[0]) {
1366 head->readbuf_size = head->read_avail = len;
1367 head->read_buf = buf;
1368 head->read_step++;
1369 } else {
1370 ccs_free(buf);
1371 }
1372 }
1373 return 0;
1374 }
1375
1376 static int WriteAnswer(IO_BUFFER *head)
1377 {
1378 char *data = head->write_buf;
1379 struct list_head *tmp;
1380 unsigned int serial, answer;
1381 /***** CRITICAL SECTION START *****/
1382 spin_lock(&query_lock);
1383 list_for_each(tmp, &query_list) {
1384 QUERY_ENTRY *ptr = list_entry(tmp, QUERY_ENTRY, list);
1385 ptr->timer = 0;
1386 }
1387 spin_unlock(&query_lock);
1388 /***** CRITICAL SECTION END *****/
1389 if (sscanf(data, "A%u=%u", &serial, &answer) != 2) return -EINVAL;
1390 /***** CRITICAL SECTION START *****/
1391 spin_lock(&query_lock);
1392 list_for_each(tmp, &query_list) {
1393 QUERY_ENTRY *ptr = list_entry(tmp, QUERY_ENTRY, list);
1394 if (ptr->serial != serial) continue;
1395 if (!ptr->answer) ptr->answer = answer;
1396 break;
1397 }
1398 spin_unlock(&query_lock);
1399 /***** CRITICAL SECTION END *****/
1400 return 0;
1401 }
1402
1403 /************************* /proc INTERFACE HANDLER *************************/
1404
1405 /* Policy updates counter. */
1406 static unsigned int updates_counter[MAX_CCS_UPDATES_COUNTER];
1407 static spinlock_t updates_counter_lock = SPIN_LOCK_UNLOCKED;
1408
1409 void UpdateCounter(const unsigned char index)
1410 {
1411 /***** CRITICAL SECTION START *****/
1412 spin_lock(&updates_counter_lock);
1413 if (index < MAX_CCS_UPDATES_COUNTER) updates_counter[index]++;
1414 spin_unlock(&updates_counter_lock);
1415 /***** CRITICAL SECTION END *****/
1416 }
1417
1418 static int ReadUpdatesCounter(IO_BUFFER *head)
1419 {
1420 if (!head->read_eof) {
1421 unsigned int counter[MAX_CCS_UPDATES_COUNTER];
1422 /***** CRITICAL SECTION START *****/
1423 spin_lock(&updates_counter_lock);
1424 memmove(counter, updates_counter, sizeof(updates_counter));
1425 memset(updates_counter, 0, sizeof(updates_counter));
1426 spin_unlock(&updates_counter_lock);
1427 /***** CRITICAL SECTION END *****/
1428 io_printf(head,
1429 "/proc/ccs/policy/system_policy: %10u\n"
1430 "/proc/ccs/policy/domain_policy: %10u\n"
1431 "/proc/ccs/policy/exception_policy: %10u\n"
1432 "/proc/ccs/status: %10u\n"
1433 "/proc/ccs/policy/query: %10u\n"
1434 "/proc/ccs/policy/manager: %10u\n"
1435 "/proc/ccs/info/grant_log: %10u\n"
1436 "/proc/ccs/info/reject_log: %10u\n",
1437 counter[CCS_UPDATES_COUNTER_SYSTEM_POLICY],
1438 counter[CCS_UPDATES_COUNTER_DOMAIN_POLICY],
1439 counter[CCS_UPDATES_COUNTER_EXCEPTION_POLICY],
1440 counter[CCS_UPDATES_COUNTER_STATUS],
1441 counter[CCS_UPDATES_COUNTER_QUERY],
1442 counter[CCS_UPDATES_COUNTER_MANAGER],
1443 counter[CCS_UPDATES_COUNTER_GRANT_LOG],
1444 counter[CCS_UPDATES_COUNTER_REJECT_LOG]);
1445 head->read_eof = 1;
1446 }
1447 return 0;
1448 }
1449
1450 static int ReadMemoryCounter(IO_BUFFER *head)
1451 {
1452 if (!head->read_eof) {
1453 const int shared = GetMemoryUsedForSaveName(), private = GetMemoryUsedForElements(), dynamic = GetMemoryUsedForDynamic();
1454 if (io_printf(head, "Shared: %10u\nPrivate: %10u\nDynamic: %10u\nTotal: %10u\n", shared, private, dynamic, shared + private + dynamic) == 0) head->read_eof = 1;
1455 }
1456 return 0;
1457 }
1458
1459 int CCS_OpenControl(const int type, struct file *file)
1460 {
1461 IO_BUFFER *head = (IO_BUFFER *) ccs_alloc(sizeof(IO_BUFFER));
1462 if (!head) return -ENOMEM;
1463 init_MUTEX(&head->read_sem);
1464 init_MUTEX(&head->write_sem);
1465 switch (type) {
1466 #ifdef CONFIG_TOMOYO
1467 case CCS_POLICY_DOMAINPOLICY:
1468 head->write = AddDomainPolicy;
1469 head->read = ReadDomainPolicy;
1470 break;
1471 case CCS_POLICY_EXCEPTIONPOLICY:
1472 head->write = AddExceptionPolicy;
1473 head->read = ReadExceptionPolicy;
1474 break;
1475 case CCS_POLICY_DOMAIN_STATUS:
1476 head->write = UpdateDomainProfile;
1477 head->read = ReadDomainProfile;
1478 break;
1479 case CCS_INFO_PROCESS_STATUS:
1480 head->write = WritePID;
1481 head->read = ReadPID;
1482 break;
1483 #ifdef CONFIG_TOMOYO_AUDIT
1484 case CCS_INFO_GRANTLOG:
1485 head->poll = PollGrantLog;
1486 head->read = ReadGrantLog;
1487 break;
1488 case CCS_INFO_REJECTLOG:
1489 head->poll = PollRejectLog;
1490 head->read = ReadRejectLog;
1491 break;
1492 #endif
1493 case CCS_INFO_SELFDOMAIN:
1494 head->read = ReadSelfDomain;
1495 break;
1496 #ifdef CONFIG_TOMOYO_MAC_FOR_FILE
1497 case CCS_INFO_MAPPING:
1498 if (!sbin_init_started) head->write = SetPermissionMapping;
1499 head->read = ReadPermissionMapping;
1500 break;
1501 #endif
1502 #endif
1503 #ifdef CONFIG_SAKURA
1504 case CCS_POLICY_SYSTEMPOLICY:
1505 head->write = AddSystemPolicy;
1506 head->read = ReadSystemPolicy;
1507 break;
1508 #endif
1509 case CCS_INFO_MEMINFO:
1510 head->read = ReadMemoryCounter;
1511 head->readbuf_size = 128;
1512 break;
1513 case CCS_STATUS:
1514 head->write = SetStatus;
1515 head->read = ReadStatus;
1516 break;
1517 case CCS_POLICY_QUERY:
1518 head->poll = PollQuery;
1519 head->write = WriteAnswer;
1520 head->read = ReadQuery;
1521 break;
1522 case CCS_POLICY_MANAGER:
1523 head->write = AddManagerPolicy;
1524 head->read = ReadManagerPolicy;
1525 break;
1526 case CCS_INFO_UPDATESCOUNTER:
1527 head->read = ReadUpdatesCounter;
1528 break;
1529 }
1530 if (type != CCS_INFO_GRANTLOG && type != CCS_INFO_REJECTLOG && type != CCS_POLICY_QUERY) {
1531 if (!head->readbuf_size) head->readbuf_size = PAGE_SIZE * 2;
1532 if ((head->read_buf = ccs_alloc(head->readbuf_size)) == NULL) {
1533 ccs_free(head);
1534 return -ENOMEM;
1535 }
1536 }
1537 if (head->write) {
1538 head->writebuf_size = PAGE_SIZE * 2;
1539 if ((head->write_buf = ccs_alloc(head->writebuf_size)) == NULL) {
1540 ccs_free(head->read_buf);
1541 ccs_free(head);
1542 return -ENOMEM;
1543 }
1544 }
1545 file->private_data = head;
1546 if (type == CCS_INFO_SELFDOMAIN) CCS_ReadControl(file, NULL, 0);
1547 else if (head->write == WriteAnswer) atomic_inc(&queryd_watcher);
1548 return 0;
1549 }
1550
1551 static int CopyToUser(IO_BUFFER *head, char __user * buffer, int buffer_len)
1552 {
1553 int len = head->read_avail;
1554 char *cp = head->read_buf;
1555 if (len > buffer_len) len = buffer_len;
1556 if (len) {
1557 if (copy_to_user(buffer, cp, len)) return -EFAULT;
1558 head->read_avail -= len;
1559 memmove(cp, cp + len, head->read_avail);
1560 }
1561 return len;
1562 }
1563
1564 int CCS_PollControl(struct file *file, poll_table *wait)
1565 {
1566 IO_BUFFER *head = (IO_BUFFER *) file->private_data;
1567 if (!head->poll) return -ENOSYS;
1568 return head->poll(file, wait);
1569 }
1570
1571 int CCS_ReadControl(struct file *file, char __user *buffer, const int buffer_len)
1572 {
1573 int len = 0;
1574 IO_BUFFER *head = (IO_BUFFER *) file->private_data;
1575 if (!head->read) return -ENOSYS;
1576 if (!access_ok(VERIFY_WRITE, buffer, buffer_len)) return -EFAULT;
1577 if (down_interruptible(&head->read_sem)) return -EINTR;
1578 len = head->read(head);
1579 if (len >= 0) len = CopyToUser(head, buffer, buffer_len);
1580 up(&head->read_sem);
1581 return len;
1582 }
1583
1584 int CCS_WriteControl(struct file *file, const char __user *buffer, const int buffer_len)
1585 {
1586 IO_BUFFER *head = (IO_BUFFER *) file->private_data;
1587 int error = buffer_len;
1588 int avail_len = buffer_len;
1589 char *cp0 = head->write_buf;
1590 if (!head->write) return -ENOSYS;
1591 if (!access_ok(VERIFY_READ, buffer, buffer_len)) return -EFAULT;
1592 if (!isRoot()) return -EPERM;
1593 if (head->write != WritePID && !IsPolicyManager()) {
1594 return -EPERM; /* Forbid updating policies for non manager programs. */
1595 }
1596 if (down_interruptible(&head->write_sem)) return -EINTR;
1597 while (avail_len > 0) {
1598 char c;
1599 if (head->write_avail >= head->writebuf_size - 1) {
1600 error = -ENOMEM;
1601 break;
1602 } else if (get_user(c, buffer)) {
1603 error = -EFAULT;
1604 break;
1605 }
1606 buffer++; avail_len--;
1607 cp0[head->write_avail++] = c;
1608 if (c != '\n') continue;
1609 cp0[head->write_avail - 1] = '\0';
1610 head->write_avail = 0;
1611 NormalizeLine(cp0);
1612 head->write(head);
1613 }
1614 up(&head->write_sem);
1615 return error;
1616 }
1617
1618
1619 int CCS_CloseControl(struct file *file)
1620 {
1621 IO_BUFFER *head = file->private_data;
1622 if (head->write == WriteAnswer) atomic_dec(&queryd_watcher);
1623 ccs_free(head->read_buf); head->read_buf = NULL;
1624 ccs_free(head->write_buf); head->write_buf = NULL;
1625 ccs_free(head); head = NULL;
1626 file->private_data = NULL;
1627 return 0;
1628 }
1629
1630 EXPORT_SYMBOL(CheckCCSFlags);
1631 EXPORT_SYMBOL(CheckCCSEnforce);
1632 EXPORT_SYMBOL(CheckCCSAccept);

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26