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

Subversion リポジトリの参照

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 853 - (show annotations) (download) (as text)
Wed Jan 2 07:32:11 2008 UTC (16 years, 4 months ago) by kumaneko
Original Path: trunk/1.5.x/ccs-patch/fs/ccs_common.c
File MIME type: text/x-csrc
File size: 53230 byte(s)


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

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