diff options
Diffstat (limited to 'security/tomoyo/util.c')
-rw-r--r-- | security/tomoyo/util.c | 963 |
1 files changed, 963 insertions, 0 deletions
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c new file mode 100644 index 000000000000..9bfc1ee8222d --- /dev/null +++ b/security/tomoyo/util.c | |||
@@ -0,0 +1,963 @@ | |||
1 | /* | ||
2 | * security/tomoyo/util.c | ||
3 | * | ||
4 | * Utility functions for TOMOYO. | ||
5 | * | ||
6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
7 | */ | ||
8 | |||
9 | #include <linux/slab.h> | ||
10 | #include "common.h" | ||
11 | |||
12 | /* Lock for protecting policy. */ | ||
13 | DEFINE_MUTEX(tomoyo_policy_lock); | ||
14 | |||
15 | /* Has /sbin/init started? */ | ||
16 | bool tomoyo_policy_loaded; | ||
17 | |||
18 | /** | ||
19 | * tomoyo_parse_ulong - Parse an "unsigned long" value. | ||
20 | * | ||
21 | * @result: Pointer to "unsigned long". | ||
22 | * @str: Pointer to string to parse. | ||
23 | * | ||
24 | * Returns value type on success, 0 otherwise. | ||
25 | * | ||
26 | * The @src is updated to point the first character after the value | ||
27 | * on success. | ||
28 | */ | ||
29 | static u8 tomoyo_parse_ulong(unsigned long *result, char **str) | ||
30 | { | ||
31 | const char *cp = *str; | ||
32 | char *ep; | ||
33 | int base = 10; | ||
34 | if (*cp == '0') { | ||
35 | char c = *(cp + 1); | ||
36 | if (c == 'x' || c == 'X') { | ||
37 | base = 16; | ||
38 | cp += 2; | ||
39 | } else if (c >= '0' && c <= '7') { | ||
40 | base = 8; | ||
41 | cp++; | ||
42 | } | ||
43 | } | ||
44 | *result = simple_strtoul(cp, &ep, base); | ||
45 | if (cp == ep) | ||
46 | return 0; | ||
47 | *str = ep; | ||
48 | switch (base) { | ||
49 | case 16: | ||
50 | return TOMOYO_VALUE_TYPE_HEXADECIMAL; | ||
51 | case 8: | ||
52 | return TOMOYO_VALUE_TYPE_OCTAL; | ||
53 | default: | ||
54 | return TOMOYO_VALUE_TYPE_DECIMAL; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * tomoyo_print_ulong - Print an "unsigned long" value. | ||
60 | * | ||
61 | * @buffer: Pointer to buffer. | ||
62 | * @buffer_len: Size of @buffer. | ||
63 | * @value: An "unsigned long" value. | ||
64 | * @type: Type of @value. | ||
65 | * | ||
66 | * Returns nothing. | ||
67 | */ | ||
68 | void tomoyo_print_ulong(char *buffer, const int buffer_len, | ||
69 | const unsigned long value, const u8 type) | ||
70 | { | ||
71 | if (type == TOMOYO_VALUE_TYPE_DECIMAL) | ||
72 | snprintf(buffer, buffer_len, "%lu", value); | ||
73 | else if (type == TOMOYO_VALUE_TYPE_OCTAL) | ||
74 | snprintf(buffer, buffer_len, "0%lo", value); | ||
75 | else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL) | ||
76 | snprintf(buffer, buffer_len, "0x%lX", value); | ||
77 | else | ||
78 | snprintf(buffer, buffer_len, "type(%u)", type); | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * tomoyo_parse_name_union - Parse a tomoyo_name_union. | ||
83 | * | ||
84 | * @filename: Name or name group. | ||
85 | * @ptr: Pointer to "struct tomoyo_name_union". | ||
86 | * | ||
87 | * Returns true on success, false otherwise. | ||
88 | */ | ||
89 | bool tomoyo_parse_name_union(const char *filename, | ||
90 | struct tomoyo_name_union *ptr) | ||
91 | { | ||
92 | if (!tomoyo_correct_word(filename)) | ||
93 | return false; | ||
94 | if (filename[0] == '@') { | ||
95 | ptr->group = tomoyo_get_group(filename + 1, TOMOYO_PATH_GROUP); | ||
96 | ptr->is_group = true; | ||
97 | return ptr->group != NULL; | ||
98 | } | ||
99 | ptr->filename = tomoyo_get_name(filename); | ||
100 | ptr->is_group = false; | ||
101 | return ptr->filename != NULL; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * tomoyo_parse_number_union - Parse a tomoyo_number_union. | ||
106 | * | ||
107 | * @data: Number or number range or number group. | ||
108 | * @ptr: Pointer to "struct tomoyo_number_union". | ||
109 | * | ||
110 | * Returns true on success, false otherwise. | ||
111 | */ | ||
112 | bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) | ||
113 | { | ||
114 | u8 type; | ||
115 | unsigned long v; | ||
116 | memset(num, 0, sizeof(*num)); | ||
117 | if (data[0] == '@') { | ||
118 | if (!tomoyo_correct_word(data)) | ||
119 | return false; | ||
120 | num->group = tomoyo_get_group(data + 1, TOMOYO_NUMBER_GROUP); | ||
121 | num->is_group = true; | ||
122 | return num->group != NULL; | ||
123 | } | ||
124 | type = tomoyo_parse_ulong(&v, &data); | ||
125 | if (!type) | ||
126 | return false; | ||
127 | num->values[0] = v; | ||
128 | num->min_type = type; | ||
129 | if (!*data) { | ||
130 | num->values[1] = v; | ||
131 | num->max_type = type; | ||
132 | return true; | ||
133 | } | ||
134 | if (*data++ != '-') | ||
135 | return false; | ||
136 | type = tomoyo_parse_ulong(&v, &data); | ||
137 | if (!type || *data) | ||
138 | return false; | ||
139 | num->values[1] = v; | ||
140 | num->max_type = type; | ||
141 | return true; | ||
142 | } | ||
143 | |||
144 | /** | ||
145 | * tomoyo_byte_range - Check whether the string is a \ooo style octal value. | ||
146 | * | ||
147 | * @str: Pointer to the string. | ||
148 | * | ||
149 | * Returns true if @str is a \ooo style octal value, false otherwise. | ||
150 | * | ||
151 | * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF. | ||
152 | * This function verifies that \ooo is in valid range. | ||
153 | */ | ||
154 | static inline bool tomoyo_byte_range(const char *str) | ||
155 | { | ||
156 | return *str >= '0' && *str++ <= '3' && | ||
157 | *str >= '0' && *str++ <= '7' && | ||
158 | *str >= '0' && *str <= '7'; | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * tomoyo_alphabet_char - Check whether the character is an alphabet. | ||
163 | * | ||
164 | * @c: The character to check. | ||
165 | * | ||
166 | * Returns true if @c is an alphabet character, false otherwise. | ||
167 | */ | ||
168 | static inline bool tomoyo_alphabet_char(const char c) | ||
169 | { | ||
170 | return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * tomoyo_make_byte - Make byte value from three octal characters. | ||
175 | * | ||
176 | * @c1: The first character. | ||
177 | * @c2: The second character. | ||
178 | * @c3: The third character. | ||
179 | * | ||
180 | * Returns byte value. | ||
181 | */ | ||
182 | static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3) | ||
183 | { | ||
184 | return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); | ||
185 | } | ||
186 | |||
187 | /** | ||
188 | * tomoyo_str_starts - Check whether the given string starts with the given keyword. | ||
189 | * | ||
190 | * @src: Pointer to pointer to the string. | ||
191 | * @find: Pointer to the keyword. | ||
192 | * | ||
193 | * Returns true if @src starts with @find, false otherwise. | ||
194 | * | ||
195 | * The @src is updated to point the first character after the @find | ||
196 | * if @src starts with @find. | ||
197 | */ | ||
198 | bool tomoyo_str_starts(char **src, const char *find) | ||
199 | { | ||
200 | const int len = strlen(find); | ||
201 | char *tmp = *src; | ||
202 | |||
203 | if (strncmp(tmp, find, len)) | ||
204 | return false; | ||
205 | tmp += len; | ||
206 | *src = tmp; | ||
207 | return true; | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * tomoyo_normalize_line - Format string. | ||
212 | * | ||
213 | * @buffer: The line to normalize. | ||
214 | * | ||
215 | * Leading and trailing whitespaces are removed. | ||
216 | * Multiple whitespaces are packed into single space. | ||
217 | * | ||
218 | * Returns nothing. | ||
219 | */ | ||
220 | void tomoyo_normalize_line(unsigned char *buffer) | ||
221 | { | ||
222 | unsigned char *sp = buffer; | ||
223 | unsigned char *dp = buffer; | ||
224 | bool first = true; | ||
225 | |||
226 | while (tomoyo_invalid(*sp)) | ||
227 | sp++; | ||
228 | while (*sp) { | ||
229 | if (!first) | ||
230 | *dp++ = ' '; | ||
231 | first = false; | ||
232 | while (tomoyo_valid(*sp)) | ||
233 | *dp++ = *sp++; | ||
234 | while (tomoyo_invalid(*sp)) | ||
235 | sp++; | ||
236 | } | ||
237 | *dp = '\0'; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * tomoyo_tokenize - Tokenize string. | ||
242 | * | ||
243 | * @buffer: The line to tokenize. | ||
244 | * @w: Pointer to "char *". | ||
245 | * @size: Sizeof @w . | ||
246 | * | ||
247 | * Returns true on success, false otherwise. | ||
248 | */ | ||
249 | bool tomoyo_tokenize(char *buffer, char *w[], size_t size) | ||
250 | { | ||
251 | int count = size / sizeof(char *); | ||
252 | int i; | ||
253 | for (i = 0; i < count; i++) | ||
254 | w[i] = ""; | ||
255 | for (i = 0; i < count; i++) { | ||
256 | char *cp = strchr(buffer, ' '); | ||
257 | if (cp) | ||
258 | *cp = '\0'; | ||
259 | w[i] = buffer; | ||
260 | if (!cp) | ||
261 | break; | ||
262 | buffer = cp + 1; | ||
263 | } | ||
264 | return i < count || !*buffer; | ||
265 | } | ||
266 | |||
267 | /** | ||
268 | * tomoyo_correct_word2 - Validate a string. | ||
269 | * | ||
270 | * @string: The string to check. May be non-'\0'-terminated. | ||
271 | * @len: Length of @string. | ||
272 | * | ||
273 | * Check whether the given string follows the naming rules. | ||
274 | * Returns true if @string follows the naming rules, false otherwise. | ||
275 | */ | ||
276 | static bool tomoyo_correct_word2(const char *string, size_t len) | ||
277 | { | ||
278 | const char *const start = string; | ||
279 | bool in_repetition = false; | ||
280 | unsigned char c; | ||
281 | unsigned char d; | ||
282 | unsigned char e; | ||
283 | if (!len) | ||
284 | goto out; | ||
285 | while (len--) { | ||
286 | c = *string++; | ||
287 | if (c == '\\') { | ||
288 | if (!len--) | ||
289 | goto out; | ||
290 | c = *string++; | ||
291 | switch (c) { | ||
292 | case '\\': /* "\\" */ | ||
293 | continue; | ||
294 | case '$': /* "\$" */ | ||
295 | case '+': /* "\+" */ | ||
296 | case '?': /* "\?" */ | ||
297 | case '*': /* "\*" */ | ||
298 | case '@': /* "\@" */ | ||
299 | case 'x': /* "\x" */ | ||
300 | case 'X': /* "\X" */ | ||
301 | case 'a': /* "\a" */ | ||
302 | case 'A': /* "\A" */ | ||
303 | case '-': /* "\-" */ | ||
304 | continue; | ||
305 | case '{': /* "/\{" */ | ||
306 | if (string - 3 < start || *(string - 3) != '/') | ||
307 | break; | ||
308 | in_repetition = true; | ||
309 | continue; | ||
310 | case '}': /* "\}/" */ | ||
311 | if (*string != '/') | ||
312 | break; | ||
313 | if (!in_repetition) | ||
314 | break; | ||
315 | in_repetition = false; | ||
316 | continue; | ||
317 | case '0': /* "\ooo" */ | ||
318 | case '1': | ||
319 | case '2': | ||
320 | case '3': | ||
321 | if (!len-- || !len--) | ||
322 | break; | ||
323 | d = *string++; | ||
324 | e = *string++; | ||
325 | if (d < '0' || d > '7' || e < '0' || e > '7') | ||
326 | break; | ||
327 | c = tomoyo_make_byte(c, d, e); | ||
328 | if (tomoyo_invalid(c)) | ||
329 | continue; /* pattern is not \000 */ | ||
330 | } | ||
331 | goto out; | ||
332 | } else if (in_repetition && c == '/') { | ||
333 | goto out; | ||
334 | } else if (tomoyo_invalid(c)) { | ||
335 | goto out; | ||
336 | } | ||
337 | } | ||
338 | if (in_repetition) | ||
339 | goto out; | ||
340 | return true; | ||
341 | out: | ||
342 | return false; | ||
343 | } | ||
344 | |||
345 | /** | ||
346 | * tomoyo_correct_word - Validate a string. | ||
347 | * | ||
348 | * @string: The string to check. | ||
349 | * | ||
350 | * Check whether the given string follows the naming rules. | ||
351 | * Returns true if @string follows the naming rules, false otherwise. | ||
352 | */ | ||
353 | bool tomoyo_correct_word(const char *string) | ||
354 | { | ||
355 | return tomoyo_correct_word2(string, strlen(string)); | ||
356 | } | ||
357 | |||
358 | /** | ||
359 | * tomoyo_correct_path - Validate a pathname. | ||
360 | * | ||
361 | * @filename: The pathname to check. | ||
362 | * | ||
363 | * Check whether the given pathname follows the naming rules. | ||
364 | * Returns true if @filename follows the naming rules, false otherwise. | ||
365 | */ | ||
366 | bool tomoyo_correct_path(const char *filename) | ||
367 | { | ||
368 | return *filename == '/' && tomoyo_correct_word(filename); | ||
369 | } | ||
370 | |||
371 | /** | ||
372 | * tomoyo_correct_domain - Check whether the given domainname follows the naming rules. | ||
373 | * | ||
374 | * @domainname: The domainname to check. | ||
375 | * | ||
376 | * Returns true if @domainname follows the naming rules, false otherwise. | ||
377 | */ | ||
378 | bool tomoyo_correct_domain(const unsigned char *domainname) | ||
379 | { | ||
380 | if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, | ||
381 | TOMOYO_ROOT_NAME_LEN)) | ||
382 | goto out; | ||
383 | domainname += TOMOYO_ROOT_NAME_LEN; | ||
384 | if (!*domainname) | ||
385 | return true; | ||
386 | if (*domainname++ != ' ') | ||
387 | goto out; | ||
388 | while (1) { | ||
389 | const unsigned char *cp = strchr(domainname, ' '); | ||
390 | if (!cp) | ||
391 | break; | ||
392 | if (*domainname != '/' || | ||
393 | !tomoyo_correct_word2(domainname, cp - domainname - 1)) | ||
394 | goto out; | ||
395 | domainname = cp + 1; | ||
396 | } | ||
397 | return tomoyo_correct_path(domainname); | ||
398 | out: | ||
399 | return false; | ||
400 | } | ||
401 | |||
402 | /** | ||
403 | * tomoyo_domain_def - Check whether the given token can be a domainname. | ||
404 | * | ||
405 | * @buffer: The token to check. | ||
406 | * | ||
407 | * Returns true if @buffer possibly be a domainname, false otherwise. | ||
408 | */ | ||
409 | bool tomoyo_domain_def(const unsigned char *buffer) | ||
410 | { | ||
411 | return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); | ||
412 | } | ||
413 | |||
414 | /** | ||
415 | * tomoyo_find_domain - Find a domain by the given name. | ||
416 | * | ||
417 | * @domainname: The domainname to find. | ||
418 | * | ||
419 | * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. | ||
420 | * | ||
421 | * Caller holds tomoyo_read_lock(). | ||
422 | */ | ||
423 | struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) | ||
424 | { | ||
425 | struct tomoyo_domain_info *domain; | ||
426 | struct tomoyo_path_info name; | ||
427 | |||
428 | name.name = domainname; | ||
429 | tomoyo_fill_path_info(&name); | ||
430 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | ||
431 | if (!domain->is_deleted && | ||
432 | !tomoyo_pathcmp(&name, domain->domainname)) | ||
433 | return domain; | ||
434 | } | ||
435 | return NULL; | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. | ||
440 | * | ||
441 | * @filename: The string to evaluate. | ||
442 | * | ||
443 | * Returns the initial length without a pattern in @filename. | ||
444 | */ | ||
445 | static int tomoyo_const_part_length(const char *filename) | ||
446 | { | ||
447 | char c; | ||
448 | int len = 0; | ||
449 | |||
450 | if (!filename) | ||
451 | return 0; | ||
452 | while ((c = *filename++) != '\0') { | ||
453 | if (c != '\\') { | ||
454 | len++; | ||
455 | continue; | ||
456 | } | ||
457 | c = *filename++; | ||
458 | switch (c) { | ||
459 | case '\\': /* "\\" */ | ||
460 | len += 2; | ||
461 | continue; | ||
462 | case '0': /* "\ooo" */ | ||
463 | case '1': | ||
464 | case '2': | ||
465 | case '3': | ||
466 | c = *filename++; | ||
467 | if (c < '0' || c > '7') | ||
468 | break; | ||
469 | c = *filename++; | ||
470 | if (c < '0' || c > '7') | ||
471 | break; | ||
472 | len += 4; | ||
473 | continue; | ||
474 | } | ||
475 | break; | ||
476 | } | ||
477 | return len; | ||
478 | } | ||
479 | |||
480 | /** | ||
481 | * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members. | ||
482 | * | ||
483 | * @ptr: Pointer to "struct tomoyo_path_info" to fill in. | ||
484 | * | ||
485 | * The caller sets "struct tomoyo_path_info"->name. | ||
486 | */ | ||
487 | void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) | ||
488 | { | ||
489 | const char *name = ptr->name; | ||
490 | const int len = strlen(name); | ||
491 | |||
492 | ptr->const_len = tomoyo_const_part_length(name); | ||
493 | ptr->is_dir = len && (name[len - 1] == '/'); | ||
494 | ptr->is_patterned = (ptr->const_len < len); | ||
495 | ptr->hash = full_name_hash(name, len); | ||
496 | } | ||
497 | |||
498 | /** | ||
499 | * tomoyo_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. | ||
500 | * | ||
501 | * @filename: The start of string to check. | ||
502 | * @filename_end: The end of string to check. | ||
503 | * @pattern: The start of pattern to compare. | ||
504 | * @pattern_end: The end of pattern to compare. | ||
505 | * | ||
506 | * Returns true if @filename matches @pattern, false otherwise. | ||
507 | */ | ||
508 | static bool tomoyo_file_matches_pattern2(const char *filename, | ||
509 | const char *filename_end, | ||
510 | const char *pattern, | ||
511 | const char *pattern_end) | ||
512 | { | ||
513 | while (filename < filename_end && pattern < pattern_end) { | ||
514 | char c; | ||
515 | if (*pattern != '\\') { | ||
516 | if (*filename++ != *pattern++) | ||
517 | return false; | ||
518 | continue; | ||
519 | } | ||
520 | c = *filename; | ||
521 | pattern++; | ||
522 | switch (*pattern) { | ||
523 | int i; | ||
524 | int j; | ||
525 | case '?': | ||
526 | if (c == '/') { | ||
527 | return false; | ||
528 | } else if (c == '\\') { | ||
529 | if (filename[1] == '\\') | ||
530 | filename++; | ||
531 | else if (tomoyo_byte_range(filename + 1)) | ||
532 | filename += 3; | ||
533 | else | ||
534 | return false; | ||
535 | } | ||
536 | break; | ||
537 | case '\\': | ||
538 | if (c != '\\') | ||
539 | return false; | ||
540 | if (*++filename != '\\') | ||
541 | return false; | ||
542 | break; | ||
543 | case '+': | ||
544 | if (!isdigit(c)) | ||
545 | return false; | ||
546 | break; | ||
547 | case 'x': | ||
548 | if (!isxdigit(c)) | ||
549 | return false; | ||
550 | break; | ||
551 | case 'a': | ||
552 | if (!tomoyo_alphabet_char(c)) | ||
553 | return false; | ||
554 | break; | ||
555 | case '0': | ||
556 | case '1': | ||
557 | case '2': | ||
558 | case '3': | ||
559 | if (c == '\\' && tomoyo_byte_range(filename + 1) | ||
560 | && strncmp(filename + 1, pattern, 3) == 0) { | ||
561 | filename += 3; | ||
562 | pattern += 2; | ||
563 | break; | ||
564 | } | ||
565 | return false; /* Not matched. */ | ||
566 | case '*': | ||
567 | case '@': | ||
568 | for (i = 0; i <= filename_end - filename; i++) { | ||
569 | if (tomoyo_file_matches_pattern2( | ||
570 | filename + i, filename_end, | ||
571 | pattern + 1, pattern_end)) | ||
572 | return true; | ||
573 | c = filename[i]; | ||
574 | if (c == '.' && *pattern == '@') | ||
575 | break; | ||
576 | if (c != '\\') | ||
577 | continue; | ||
578 | if (filename[i + 1] == '\\') | ||
579 | i++; | ||
580 | else if (tomoyo_byte_range(filename + i + 1)) | ||
581 | i += 3; | ||
582 | else | ||
583 | break; /* Bad pattern. */ | ||
584 | } | ||
585 | return false; /* Not matched. */ | ||
586 | default: | ||
587 | j = 0; | ||
588 | c = *pattern; | ||
589 | if (c == '$') { | ||
590 | while (isdigit(filename[j])) | ||
591 | j++; | ||
592 | } else if (c == 'X') { | ||
593 | while (isxdigit(filename[j])) | ||
594 | j++; | ||
595 | } else if (c == 'A') { | ||
596 | while (tomoyo_alphabet_char(filename[j])) | ||
597 | j++; | ||
598 | } | ||
599 | for (i = 1; i <= j; i++) { | ||
600 | if (tomoyo_file_matches_pattern2( | ||
601 | filename + i, filename_end, | ||
602 | pattern + 1, pattern_end)) | ||
603 | return true; | ||
604 | } | ||
605 | return false; /* Not matched or bad pattern. */ | ||
606 | } | ||
607 | filename++; | ||
608 | pattern++; | ||
609 | } | ||
610 | while (*pattern == '\\' && | ||
611 | (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | ||
612 | pattern += 2; | ||
613 | return filename == filename_end && pattern == pattern_end; | ||
614 | } | ||
615 | |||
616 | /** | ||
617 | * tomoyo_file_matches_pattern - Pattern matching without '/' character. | ||
618 | * | ||
619 | * @filename: The start of string to check. | ||
620 | * @filename_end: The end of string to check. | ||
621 | * @pattern: The start of pattern to compare. | ||
622 | * @pattern_end: The end of pattern to compare. | ||
623 | * | ||
624 | * Returns true if @filename matches @pattern, false otherwise. | ||
625 | */ | ||
626 | static bool tomoyo_file_matches_pattern(const char *filename, | ||
627 | const char *filename_end, | ||
628 | const char *pattern, | ||
629 | const char *pattern_end) | ||
630 | { | ||
631 | const char *pattern_start = pattern; | ||
632 | bool first = true; | ||
633 | bool result; | ||
634 | |||
635 | while (pattern < pattern_end - 1) { | ||
636 | /* Split at "\-" pattern. */ | ||
637 | if (*pattern++ != '\\' || *pattern++ != '-') | ||
638 | continue; | ||
639 | result = tomoyo_file_matches_pattern2(filename, | ||
640 | filename_end, | ||
641 | pattern_start, | ||
642 | pattern - 2); | ||
643 | if (first) | ||
644 | result = !result; | ||
645 | if (result) | ||
646 | return false; | ||
647 | first = false; | ||
648 | pattern_start = pattern; | ||
649 | } | ||
650 | result = tomoyo_file_matches_pattern2(filename, filename_end, | ||
651 | pattern_start, pattern_end); | ||
652 | return first ? result : !result; | ||
653 | } | ||
654 | |||
655 | /** | ||
656 | * tomoyo_path_matches_pattern2 - Do pathname pattern matching. | ||
657 | * | ||
658 | * @f: The start of string to check. | ||
659 | * @p: The start of pattern to compare. | ||
660 | * | ||
661 | * Returns true if @f matches @p, false otherwise. | ||
662 | */ | ||
663 | static bool tomoyo_path_matches_pattern2(const char *f, const char *p) | ||
664 | { | ||
665 | const char *f_delimiter; | ||
666 | const char *p_delimiter; | ||
667 | |||
668 | while (*f && *p) { | ||
669 | f_delimiter = strchr(f, '/'); | ||
670 | if (!f_delimiter) | ||
671 | f_delimiter = f + strlen(f); | ||
672 | p_delimiter = strchr(p, '/'); | ||
673 | if (!p_delimiter) | ||
674 | p_delimiter = p + strlen(p); | ||
675 | if (*p == '\\' && *(p + 1) == '{') | ||
676 | goto recursive; | ||
677 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p, | ||
678 | p_delimiter)) | ||
679 | return false; | ||
680 | f = f_delimiter; | ||
681 | if (*f) | ||
682 | f++; | ||
683 | p = p_delimiter; | ||
684 | if (*p) | ||
685 | p++; | ||
686 | } | ||
687 | /* Ignore trailing "\*" and "\@" in @pattern. */ | ||
688 | while (*p == '\\' && | ||
689 | (*(p + 1) == '*' || *(p + 1) == '@')) | ||
690 | p += 2; | ||
691 | return !*f && !*p; | ||
692 | recursive: | ||
693 | /* | ||
694 | * The "\{" pattern is permitted only after '/' character. | ||
695 | * This guarantees that below "*(p - 1)" is safe. | ||
696 | * Also, the "\}" pattern is permitted only before '/' character | ||
697 | * so that "\{" + "\}" pair will not break the "\-" operator. | ||
698 | */ | ||
699 | if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || | ||
700 | *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') | ||
701 | return false; /* Bad pattern. */ | ||
702 | do { | ||
703 | /* Compare current component with pattern. */ | ||
704 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2, | ||
705 | p_delimiter - 2)) | ||
706 | break; | ||
707 | /* Proceed to next component. */ | ||
708 | f = f_delimiter; | ||
709 | if (!*f) | ||
710 | break; | ||
711 | f++; | ||
712 | /* Continue comparison. */ | ||
713 | if (tomoyo_path_matches_pattern2(f, p_delimiter + 1)) | ||
714 | return true; | ||
715 | f_delimiter = strchr(f, '/'); | ||
716 | } while (f_delimiter); | ||
717 | return false; /* Not matched. */ | ||
718 | } | ||
719 | |||
720 | /** | ||
721 | * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern. | ||
722 | * | ||
723 | * @filename: The filename to check. | ||
724 | * @pattern: The pattern to compare. | ||
725 | * | ||
726 | * Returns true if matches, false otherwise. | ||
727 | * | ||
728 | * The following patterns are available. | ||
729 | * \\ \ itself. | ||
730 | * \ooo Octal representation of a byte. | ||
731 | * \* Zero or more repetitions of characters other than '/'. | ||
732 | * \@ Zero or more repetitions of characters other than '/' or '.'. | ||
733 | * \? 1 byte character other than '/'. | ||
734 | * \$ One or more repetitions of decimal digits. | ||
735 | * \+ 1 decimal digit. | ||
736 | * \X One or more repetitions of hexadecimal digits. | ||
737 | * \x 1 hexadecimal digit. | ||
738 | * \A One or more repetitions of alphabet characters. | ||
739 | * \a 1 alphabet character. | ||
740 | * | ||
741 | * \- Subtraction operator. | ||
742 | * | ||
743 | * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | ||
744 | * /dir/dir/dir/ ). | ||
745 | */ | ||
746 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, | ||
747 | const struct tomoyo_path_info *pattern) | ||
748 | { | ||
749 | const char *f = filename->name; | ||
750 | const char *p = pattern->name; | ||
751 | const int len = pattern->const_len; | ||
752 | |||
753 | /* If @pattern doesn't contain pattern, I can use strcmp(). */ | ||
754 | if (!pattern->is_patterned) | ||
755 | return !tomoyo_pathcmp(filename, pattern); | ||
756 | /* Don't compare directory and non-directory. */ | ||
757 | if (filename->is_dir != pattern->is_dir) | ||
758 | return false; | ||
759 | /* Compare the initial length without patterns. */ | ||
760 | if (strncmp(f, p, len)) | ||
761 | return false; | ||
762 | f += len; | ||
763 | p += len; | ||
764 | return tomoyo_path_matches_pattern2(f, p); | ||
765 | } | ||
766 | |||
767 | /** | ||
768 | * tomoyo_get_exe - Get tomoyo_realpath() of current process. | ||
769 | * | ||
770 | * Returns the tomoyo_realpath() of current process on success, NULL otherwise. | ||
771 | * | ||
772 | * This function uses kzalloc(), so the caller must call kfree() | ||
773 | * if this function didn't return NULL. | ||
774 | */ | ||
775 | const char *tomoyo_get_exe(void) | ||
776 | { | ||
777 | struct mm_struct *mm = current->mm; | ||
778 | struct vm_area_struct *vma; | ||
779 | const char *cp = NULL; | ||
780 | |||
781 | if (!mm) | ||
782 | return NULL; | ||
783 | down_read(&mm->mmap_sem); | ||
784 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | ||
785 | if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | ||
786 | cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); | ||
787 | break; | ||
788 | } | ||
789 | } | ||
790 | up_read(&mm->mmap_sem); | ||
791 | return cp; | ||
792 | } | ||
793 | |||
794 | /** | ||
795 | * tomoyo_get_mode - Get MAC mode. | ||
796 | * | ||
797 | * @profile: Profile number. | ||
798 | * @index: Index number of functionality. | ||
799 | * | ||
800 | * Returns mode. | ||
801 | */ | ||
802 | int tomoyo_get_mode(const u8 profile, const u8 index) | ||
803 | { | ||
804 | u8 mode; | ||
805 | const u8 category = TOMOYO_MAC_CATEGORY_FILE; | ||
806 | if (!tomoyo_policy_loaded) | ||
807 | return TOMOYO_CONFIG_DISABLED; | ||
808 | mode = tomoyo_profile(profile)->config[index]; | ||
809 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) | ||
810 | mode = tomoyo_profile(profile)->config[category]; | ||
811 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) | ||
812 | mode = tomoyo_profile(profile)->default_config; | ||
813 | return mode & 3; | ||
814 | } | ||
815 | |||
816 | /** | ||
817 | * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members. | ||
818 | * | ||
819 | * @r: Pointer to "struct tomoyo_request_info" to initialize. | ||
820 | * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain(). | ||
821 | * @index: Index number of functionality. | ||
822 | * | ||
823 | * Returns mode. | ||
824 | */ | ||
825 | int tomoyo_init_request_info(struct tomoyo_request_info *r, | ||
826 | struct tomoyo_domain_info *domain, const u8 index) | ||
827 | { | ||
828 | u8 profile; | ||
829 | memset(r, 0, sizeof(*r)); | ||
830 | if (!domain) | ||
831 | domain = tomoyo_domain(); | ||
832 | r->domain = domain; | ||
833 | profile = domain->profile; | ||
834 | r->profile = profile; | ||
835 | r->type = index; | ||
836 | r->mode = tomoyo_get_mode(profile, index); | ||
837 | return r->mode; | ||
838 | } | ||
839 | |||
840 | /** | ||
841 | * tomoyo_last_word - Get last component of a line. | ||
842 | * | ||
843 | * @line: A line. | ||
844 | * | ||
845 | * Returns the last word of a line. | ||
846 | */ | ||
847 | const char *tomoyo_last_word(const char *name) | ||
848 | { | ||
849 | const char *cp = strrchr(name, ' '); | ||
850 | if (cp) | ||
851 | return cp + 1; | ||
852 | return name; | ||
853 | } | ||
854 | |||
855 | /** | ||
856 | * tomoyo_warn_log - Print warning or error message on console. | ||
857 | * | ||
858 | * @r: Pointer to "struct tomoyo_request_info". | ||
859 | * @fmt: The printf()'s format string, followed by parameters. | ||
860 | */ | ||
861 | void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) | ||
862 | { | ||
863 | va_list args; | ||
864 | char *buffer; | ||
865 | const struct tomoyo_domain_info * const domain = r->domain; | ||
866 | const struct tomoyo_profile *profile = tomoyo_profile(domain->profile); | ||
867 | switch (r->mode) { | ||
868 | case TOMOYO_CONFIG_ENFORCING: | ||
869 | if (!profile->enforcing->enforcing_verbose) | ||
870 | return; | ||
871 | break; | ||
872 | case TOMOYO_CONFIG_PERMISSIVE: | ||
873 | if (!profile->permissive->permissive_verbose) | ||
874 | return; | ||
875 | break; | ||
876 | case TOMOYO_CONFIG_LEARNING: | ||
877 | if (!profile->learning->learning_verbose) | ||
878 | return; | ||
879 | break; | ||
880 | } | ||
881 | buffer = kmalloc(4096, GFP_NOFS); | ||
882 | if (!buffer) | ||
883 | return; | ||
884 | va_start(args, fmt); | ||
885 | vsnprintf(buffer, 4095, fmt, args); | ||
886 | va_end(args); | ||
887 | buffer[4095] = '\0'; | ||
888 | printk(KERN_WARNING "%s: Access %s denied for %s\n", | ||
889 | r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer, | ||
890 | tomoyo_last_word(domain->domainname->name)); | ||
891 | kfree(buffer); | ||
892 | } | ||
893 | |||
894 | /** | ||
895 | * tomoyo_domain_quota_is_ok - Check for domain's quota. | ||
896 | * | ||
897 | * @r: Pointer to "struct tomoyo_request_info". | ||
898 | * | ||
899 | * Returns true if the domain is not exceeded quota, false otherwise. | ||
900 | * | ||
901 | * Caller holds tomoyo_read_lock(). | ||
902 | */ | ||
903 | bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) | ||
904 | { | ||
905 | unsigned int count = 0; | ||
906 | struct tomoyo_domain_info *domain = r->domain; | ||
907 | struct tomoyo_acl_info *ptr; | ||
908 | |||
909 | if (r->mode != TOMOYO_CONFIG_LEARNING) | ||
910 | return false; | ||
911 | if (!domain) | ||
912 | return true; | ||
913 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | ||
914 | if (ptr->is_deleted) | ||
915 | continue; | ||
916 | switch (ptr->type) { | ||
917 | u16 perm; | ||
918 | u8 i; | ||
919 | case TOMOYO_TYPE_PATH_ACL: | ||
920 | perm = container_of(ptr, struct tomoyo_path_acl, head) | ||
921 | ->perm; | ||
922 | for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) | ||
923 | if (perm & (1 << i)) | ||
924 | count++; | ||
925 | if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) | ||
926 | count -= 2; | ||
927 | break; | ||
928 | case TOMOYO_TYPE_PATH2_ACL: | ||
929 | perm = container_of(ptr, struct tomoyo_path2_acl, head) | ||
930 | ->perm; | ||
931 | for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) | ||
932 | if (perm & (1 << i)) | ||
933 | count++; | ||
934 | break; | ||
935 | case TOMOYO_TYPE_PATH_NUMBER_ACL: | ||
936 | perm = container_of(ptr, struct tomoyo_path_number_acl, | ||
937 | head)->perm; | ||
938 | for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++) | ||
939 | if (perm & (1 << i)) | ||
940 | count++; | ||
941 | break; | ||
942 | case TOMOYO_TYPE_MKDEV_ACL: | ||
943 | perm = container_of(ptr, struct tomoyo_mkdev_acl, | ||
944 | head)->perm; | ||
945 | for (i = 0; i < TOMOYO_MAX_MKDEV_OPERATION; i++) | ||
946 | if (perm & (1 << i)) | ||
947 | count++; | ||
948 | break; | ||
949 | default: | ||
950 | count++; | ||
951 | } | ||
952 | } | ||
953 | if (count < tomoyo_profile(domain->profile)->learning-> | ||
954 | learning_max_entry) | ||
955 | return true; | ||
956 | if (!domain->quota_warned) { | ||
957 | domain->quota_warned = true; | ||
958 | printk(KERN_WARNING "TOMOYO-WARNING: " | ||
959 | "Domain '%s' has so many ACLs to hold. " | ||
960 | "Stopped learning mode.\n", domain->domainname->name); | ||
961 | } | ||
962 | return false; | ||
963 | } | ||