diff options
author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2010-05-16 21:12:46 -0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2010-08-02 01:33:39 -0400 |
commit | c3ef1500ec833890275172c7d063333404b64d60 (patch) | |
tree | 2453368e521a1f7a00098eef06afbedb8404503d /security/tomoyo/util.c | |
parent | 17fcfbd9d45b57f38d40e31f9d28db53f4af5c88 (diff) |
TOMOYO: Split files into some pieces.
security/tomoyo/common.c became too large to read.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/tomoyo/util.c')
-rw-r--r-- | security/tomoyo/util.c | 951 |
1 files changed, 951 insertions, 0 deletions
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c new file mode 100644 index 000000000000..7b023f5e1314 --- /dev/null +++ b/security/tomoyo/util.c | |||
@@ -0,0 +1,951 @@ | |||
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 | 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_is_correct_path(filename, 0, 0, 0)) | ||
93 | return false; | ||
94 | if (filename[0] == '@') { | ||
95 | ptr->group = tomoyo_get_path_group(filename + 1); | ||
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_is_correct_path(data, 0, 0, 0)) | ||
119 | return false; | ||
120 | num->group = tomoyo_get_number_group(data + 1); | ||
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_is_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_is_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_is_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_is_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_is_invalid(*sp)) | ||
227 | sp++; | ||
228 | while (*sp) { | ||
229 | if (!first) | ||
230 | *dp++ = ' '; | ||
231 | first = false; | ||
232 | while (tomoyo_is_valid(*sp)) | ||
233 | *dp++ = *sp++; | ||
234 | while (tomoyo_is_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_is_correct_path - Validate a pathname. | ||
269 | * | ||
270 | * @filename: The pathname to check. | ||
271 | * @start_type: Should the pathname start with '/'? | ||
272 | * 1 = must / -1 = must not / 0 = don't care | ||
273 | * @pattern_type: Can the pathname contain a wildcard? | ||
274 | * 1 = must / -1 = must not / 0 = don't care | ||
275 | * @end_type: Should the pathname end with '/'? | ||
276 | * 1 = must / -1 = must not / 0 = don't care | ||
277 | * | ||
278 | * Check whether the given filename follows the naming rules. | ||
279 | * Returns true if @filename follows the naming rules, false otherwise. | ||
280 | */ | ||
281 | bool tomoyo_is_correct_path(const char *filename, const s8 start_type, | ||
282 | const s8 pattern_type, const s8 end_type) | ||
283 | { | ||
284 | const char *const start = filename; | ||
285 | bool in_repetition = false; | ||
286 | bool contains_pattern = false; | ||
287 | unsigned char c; | ||
288 | unsigned char d; | ||
289 | unsigned char e; | ||
290 | |||
291 | if (!filename) | ||
292 | goto out; | ||
293 | c = *filename; | ||
294 | if (start_type == 1) { /* Must start with '/' */ | ||
295 | if (c != '/') | ||
296 | goto out; | ||
297 | } else if (start_type == -1) { /* Must not start with '/' */ | ||
298 | if (c == '/') | ||
299 | goto out; | ||
300 | } | ||
301 | if (c) | ||
302 | c = *(filename + strlen(filename) - 1); | ||
303 | if (end_type == 1) { /* Must end with '/' */ | ||
304 | if (c != '/') | ||
305 | goto out; | ||
306 | } else if (end_type == -1) { /* Must not end with '/' */ | ||
307 | if (c == '/') | ||
308 | goto out; | ||
309 | } | ||
310 | while (1) { | ||
311 | c = *filename++; | ||
312 | if (!c) | ||
313 | break; | ||
314 | if (c == '\\') { | ||
315 | c = *filename++; | ||
316 | switch (c) { | ||
317 | case '\\': /* "\\" */ | ||
318 | continue; | ||
319 | case '$': /* "\$" */ | ||
320 | case '+': /* "\+" */ | ||
321 | case '?': /* "\?" */ | ||
322 | case '*': /* "\*" */ | ||
323 | case '@': /* "\@" */ | ||
324 | case 'x': /* "\x" */ | ||
325 | case 'X': /* "\X" */ | ||
326 | case 'a': /* "\a" */ | ||
327 | case 'A': /* "\A" */ | ||
328 | case '-': /* "\-" */ | ||
329 | if (pattern_type == -1) | ||
330 | break; /* Must not contain pattern */ | ||
331 | contains_pattern = true; | ||
332 | continue; | ||
333 | case '{': /* "/\{" */ | ||
334 | if (filename - 3 < start || | ||
335 | *(filename - 3) != '/') | ||
336 | break; | ||
337 | if (pattern_type == -1) | ||
338 | break; /* Must not contain pattern */ | ||
339 | contains_pattern = true; | ||
340 | in_repetition = true; | ||
341 | continue; | ||
342 | case '}': /* "\}/" */ | ||
343 | if (*filename != '/') | ||
344 | break; | ||
345 | if (!in_repetition) | ||
346 | break; | ||
347 | in_repetition = false; | ||
348 | continue; | ||
349 | case '0': /* "\ooo" */ | ||
350 | case '1': | ||
351 | case '2': | ||
352 | case '3': | ||
353 | d = *filename++; | ||
354 | if (d < '0' || d > '7') | ||
355 | break; | ||
356 | e = *filename++; | ||
357 | if (e < '0' || e > '7') | ||
358 | break; | ||
359 | c = tomoyo_make_byte(c, d, e); | ||
360 | if (tomoyo_is_invalid(c)) | ||
361 | continue; /* pattern is not \000 */ | ||
362 | } | ||
363 | goto out; | ||
364 | } else if (in_repetition && c == '/') { | ||
365 | goto out; | ||
366 | } else if (tomoyo_is_invalid(c)) { | ||
367 | goto out; | ||
368 | } | ||
369 | } | ||
370 | if (pattern_type == 1) { /* Must contain pattern */ | ||
371 | if (!contains_pattern) | ||
372 | goto out; | ||
373 | } | ||
374 | if (in_repetition) | ||
375 | goto out; | ||
376 | return true; | ||
377 | out: | ||
378 | return false; | ||
379 | } | ||
380 | |||
381 | /** | ||
382 | * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules. | ||
383 | * | ||
384 | * @domainname: The domainname to check. | ||
385 | * | ||
386 | * Returns true if @domainname follows the naming rules, false otherwise. | ||
387 | */ | ||
388 | bool tomoyo_is_correct_domain(const unsigned char *domainname) | ||
389 | { | ||
390 | unsigned char c; | ||
391 | unsigned char d; | ||
392 | unsigned char e; | ||
393 | |||
394 | if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, | ||
395 | TOMOYO_ROOT_NAME_LEN)) | ||
396 | goto out; | ||
397 | domainname += TOMOYO_ROOT_NAME_LEN; | ||
398 | if (!*domainname) | ||
399 | return true; | ||
400 | do { | ||
401 | if (*domainname++ != ' ') | ||
402 | goto out; | ||
403 | if (*domainname++ != '/') | ||
404 | goto out; | ||
405 | while ((c = *domainname) != '\0' && c != ' ') { | ||
406 | domainname++; | ||
407 | if (c == '\\') { | ||
408 | c = *domainname++; | ||
409 | switch ((c)) { | ||
410 | case '\\': /* "\\" */ | ||
411 | continue; | ||
412 | case '0': /* "\ooo" */ | ||
413 | case '1': | ||
414 | case '2': | ||
415 | case '3': | ||
416 | d = *domainname++; | ||
417 | if (d < '0' || d > '7') | ||
418 | break; | ||
419 | e = *domainname++; | ||
420 | if (e < '0' || e > '7') | ||
421 | break; | ||
422 | c = tomoyo_make_byte(c, d, e); | ||
423 | if (tomoyo_is_invalid(c)) | ||
424 | /* pattern is not \000 */ | ||
425 | continue; | ||
426 | } | ||
427 | goto out; | ||
428 | } else if (tomoyo_is_invalid(c)) { | ||
429 | goto out; | ||
430 | } | ||
431 | } | ||
432 | } while (*domainname); | ||
433 | return true; | ||
434 | out: | ||
435 | return false; | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * tomoyo_is_domain_def - Check whether the given token can be a domainname. | ||
440 | * | ||
441 | * @buffer: The token to check. | ||
442 | * | ||
443 | * Returns true if @buffer possibly be a domainname, false otherwise. | ||
444 | */ | ||
445 | bool tomoyo_is_domain_def(const unsigned char *buffer) | ||
446 | { | ||
447 | return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); | ||
448 | } | ||
449 | |||
450 | /** | ||
451 | * tomoyo_find_domain - Find a domain by the given name. | ||
452 | * | ||
453 | * @domainname: The domainname to find. | ||
454 | * | ||
455 | * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. | ||
456 | * | ||
457 | * Caller holds tomoyo_read_lock(). | ||
458 | */ | ||
459 | struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) | ||
460 | { | ||
461 | struct tomoyo_domain_info *domain; | ||
462 | struct tomoyo_path_info name; | ||
463 | |||
464 | name.name = domainname; | ||
465 | tomoyo_fill_path_info(&name); | ||
466 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | ||
467 | if (!domain->is_deleted && | ||
468 | !tomoyo_pathcmp(&name, domain->domainname)) | ||
469 | return domain; | ||
470 | } | ||
471 | return NULL; | ||
472 | } | ||
473 | |||
474 | /** | ||
475 | * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. | ||
476 | * | ||
477 | * @filename: The string to evaluate. | ||
478 | * | ||
479 | * Returns the initial length without a pattern in @filename. | ||
480 | */ | ||
481 | static int tomoyo_const_part_length(const char *filename) | ||
482 | { | ||
483 | char c; | ||
484 | int len = 0; | ||
485 | |||
486 | if (!filename) | ||
487 | return 0; | ||
488 | while ((c = *filename++) != '\0') { | ||
489 | if (c != '\\') { | ||
490 | len++; | ||
491 | continue; | ||
492 | } | ||
493 | c = *filename++; | ||
494 | switch (c) { | ||
495 | case '\\': /* "\\" */ | ||
496 | len += 2; | ||
497 | continue; | ||
498 | case '0': /* "\ooo" */ | ||
499 | case '1': | ||
500 | case '2': | ||
501 | case '3': | ||
502 | c = *filename++; | ||
503 | if (c < '0' || c > '7') | ||
504 | break; | ||
505 | c = *filename++; | ||
506 | if (c < '0' || c > '7') | ||
507 | break; | ||
508 | len += 4; | ||
509 | continue; | ||
510 | } | ||
511 | break; | ||
512 | } | ||
513 | return len; | ||
514 | } | ||
515 | |||
516 | /** | ||
517 | * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members. | ||
518 | * | ||
519 | * @ptr: Pointer to "struct tomoyo_path_info" to fill in. | ||
520 | * | ||
521 | * The caller sets "struct tomoyo_path_info"->name. | ||
522 | */ | ||
523 | void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) | ||
524 | { | ||
525 | const char *name = ptr->name; | ||
526 | const int len = strlen(name); | ||
527 | |||
528 | ptr->const_len = tomoyo_const_part_length(name); | ||
529 | ptr->is_dir = len && (name[len - 1] == '/'); | ||
530 | ptr->is_patterned = (ptr->const_len < len); | ||
531 | ptr->hash = full_name_hash(name, len); | ||
532 | } | ||
533 | |||
534 | /** | ||
535 | * tomoyo_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. | ||
536 | * | ||
537 | * @filename: The start of string to check. | ||
538 | * @filename_end: The end of string to check. | ||
539 | * @pattern: The start of pattern to compare. | ||
540 | * @pattern_end: The end of pattern to compare. | ||
541 | * | ||
542 | * Returns true if @filename matches @pattern, false otherwise. | ||
543 | */ | ||
544 | static bool tomoyo_file_matches_pattern2(const char *filename, | ||
545 | const char *filename_end, | ||
546 | const char *pattern, | ||
547 | const char *pattern_end) | ||
548 | { | ||
549 | while (filename < filename_end && pattern < pattern_end) { | ||
550 | char c; | ||
551 | if (*pattern != '\\') { | ||
552 | if (*filename++ != *pattern++) | ||
553 | return false; | ||
554 | continue; | ||
555 | } | ||
556 | c = *filename; | ||
557 | pattern++; | ||
558 | switch (*pattern) { | ||
559 | int i; | ||
560 | int j; | ||
561 | case '?': | ||
562 | if (c == '/') { | ||
563 | return false; | ||
564 | } else if (c == '\\') { | ||
565 | if (filename[1] == '\\') | ||
566 | filename++; | ||
567 | else if (tomoyo_is_byte_range(filename + 1)) | ||
568 | filename += 3; | ||
569 | else | ||
570 | return false; | ||
571 | } | ||
572 | break; | ||
573 | case '\\': | ||
574 | if (c != '\\') | ||
575 | return false; | ||
576 | if (*++filename != '\\') | ||
577 | return false; | ||
578 | break; | ||
579 | case '+': | ||
580 | if (!isdigit(c)) | ||
581 | return false; | ||
582 | break; | ||
583 | case 'x': | ||
584 | if (!isxdigit(c)) | ||
585 | return false; | ||
586 | break; | ||
587 | case 'a': | ||
588 | if (!tomoyo_is_alphabet_char(c)) | ||
589 | return false; | ||
590 | break; | ||
591 | case '0': | ||
592 | case '1': | ||
593 | case '2': | ||
594 | case '3': | ||
595 | if (c == '\\' && tomoyo_is_byte_range(filename + 1) | ||
596 | && strncmp(filename + 1, pattern, 3) == 0) { | ||
597 | filename += 3; | ||
598 | pattern += 2; | ||
599 | break; | ||
600 | } | ||
601 | return false; /* Not matched. */ | ||
602 | case '*': | ||
603 | case '@': | ||
604 | for (i = 0; i <= filename_end - filename; i++) { | ||
605 | if (tomoyo_file_matches_pattern2( | ||
606 | filename + i, filename_end, | ||
607 | pattern + 1, pattern_end)) | ||
608 | return true; | ||
609 | c = filename[i]; | ||
610 | if (c == '.' && *pattern == '@') | ||
611 | break; | ||
612 | if (c != '\\') | ||
613 | continue; | ||
614 | if (filename[i + 1] == '\\') | ||
615 | i++; | ||
616 | else if (tomoyo_is_byte_range(filename + i + 1)) | ||
617 | i += 3; | ||
618 | else | ||
619 | break; /* Bad pattern. */ | ||
620 | } | ||
621 | return false; /* Not matched. */ | ||
622 | default: | ||
623 | j = 0; | ||
624 | c = *pattern; | ||
625 | if (c == '$') { | ||
626 | while (isdigit(filename[j])) | ||
627 | j++; | ||
628 | } else if (c == 'X') { | ||
629 | while (isxdigit(filename[j])) | ||
630 | j++; | ||
631 | } else if (c == 'A') { | ||
632 | while (tomoyo_is_alphabet_char(filename[j])) | ||
633 | j++; | ||
634 | } | ||
635 | for (i = 1; i <= j; i++) { | ||
636 | if (tomoyo_file_matches_pattern2( | ||
637 | filename + i, filename_end, | ||
638 | pattern + 1, pattern_end)) | ||
639 | return true; | ||
640 | } | ||
641 | return false; /* Not matched or bad pattern. */ | ||
642 | } | ||
643 | filename++; | ||
644 | pattern++; | ||
645 | } | ||
646 | while (*pattern == '\\' && | ||
647 | (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | ||
648 | pattern += 2; | ||
649 | return filename == filename_end && pattern == pattern_end; | ||
650 | } | ||
651 | |||
652 | /** | ||
653 | * tomoyo_file_matches_pattern - Pattern matching without '/' character. | ||
654 | * | ||
655 | * @filename: The start of string to check. | ||
656 | * @filename_end: The end of string to check. | ||
657 | * @pattern: The start of pattern to compare. | ||
658 | * @pattern_end: The end of pattern to compare. | ||
659 | * | ||
660 | * Returns true if @filename matches @pattern, false otherwise. | ||
661 | */ | ||
662 | static bool tomoyo_file_matches_pattern(const char *filename, | ||
663 | const char *filename_end, | ||
664 | const char *pattern, | ||
665 | const char *pattern_end) | ||
666 | { | ||
667 | const char *pattern_start = pattern; | ||
668 | bool first = true; | ||
669 | bool result; | ||
670 | |||
671 | while (pattern < pattern_end - 1) { | ||
672 | /* Split at "\-" pattern. */ | ||
673 | if (*pattern++ != '\\' || *pattern++ != '-') | ||
674 | continue; | ||
675 | result = tomoyo_file_matches_pattern2(filename, | ||
676 | filename_end, | ||
677 | pattern_start, | ||
678 | pattern - 2); | ||
679 | if (first) | ||
680 | result = !result; | ||
681 | if (result) | ||
682 | return false; | ||
683 | first = false; | ||
684 | pattern_start = pattern; | ||
685 | } | ||
686 | result = tomoyo_file_matches_pattern2(filename, filename_end, | ||
687 | pattern_start, pattern_end); | ||
688 | return first ? result : !result; | ||
689 | } | ||
690 | |||
691 | /** | ||
692 | * tomoyo_path_matches_pattern2 - Do pathname pattern matching. | ||
693 | * | ||
694 | * @f: The start of string to check. | ||
695 | * @p: The start of pattern to compare. | ||
696 | * | ||
697 | * Returns true if @f matches @p, false otherwise. | ||
698 | */ | ||
699 | static bool tomoyo_path_matches_pattern2(const char *f, const char *p) | ||
700 | { | ||
701 | const char *f_delimiter; | ||
702 | const char *p_delimiter; | ||
703 | |||
704 | while (*f && *p) { | ||
705 | f_delimiter = strchr(f, '/'); | ||
706 | if (!f_delimiter) | ||
707 | f_delimiter = f + strlen(f); | ||
708 | p_delimiter = strchr(p, '/'); | ||
709 | if (!p_delimiter) | ||
710 | p_delimiter = p + strlen(p); | ||
711 | if (*p == '\\' && *(p + 1) == '{') | ||
712 | goto recursive; | ||
713 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p, | ||
714 | p_delimiter)) | ||
715 | return false; | ||
716 | f = f_delimiter; | ||
717 | if (*f) | ||
718 | f++; | ||
719 | p = p_delimiter; | ||
720 | if (*p) | ||
721 | p++; | ||
722 | } | ||
723 | /* Ignore trailing "\*" and "\@" in @pattern. */ | ||
724 | while (*p == '\\' && | ||
725 | (*(p + 1) == '*' || *(p + 1) == '@')) | ||
726 | p += 2; | ||
727 | return !*f && !*p; | ||
728 | recursive: | ||
729 | /* | ||
730 | * The "\{" pattern is permitted only after '/' character. | ||
731 | * This guarantees that below "*(p - 1)" is safe. | ||
732 | * Also, the "\}" pattern is permitted only before '/' character | ||
733 | * so that "\{" + "\}" pair will not break the "\-" operator. | ||
734 | */ | ||
735 | if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || | ||
736 | *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') | ||
737 | return false; /* Bad pattern. */ | ||
738 | do { | ||
739 | /* Compare current component with pattern. */ | ||
740 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2, | ||
741 | p_delimiter - 2)) | ||
742 | break; | ||
743 | /* Proceed to next component. */ | ||
744 | f = f_delimiter; | ||
745 | if (!*f) | ||
746 | break; | ||
747 | f++; | ||
748 | /* Continue comparison. */ | ||
749 | if (tomoyo_path_matches_pattern2(f, p_delimiter + 1)) | ||
750 | return true; | ||
751 | f_delimiter = strchr(f, '/'); | ||
752 | } while (f_delimiter); | ||
753 | return false; /* Not matched. */ | ||
754 | } | ||
755 | |||
756 | /** | ||
757 | * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern. | ||
758 | * | ||
759 | * @filename: The filename to check. | ||
760 | * @pattern: The pattern to compare. | ||
761 | * | ||
762 | * Returns true if matches, false otherwise. | ||
763 | * | ||
764 | * The following patterns are available. | ||
765 | * \\ \ itself. | ||
766 | * \ooo Octal representation of a byte. | ||
767 | * \* Zero or more repetitions of characters other than '/'. | ||
768 | * \@ Zero or more repetitions of characters other than '/' or '.'. | ||
769 | * \? 1 byte character other than '/'. | ||
770 | * \$ One or more repetitions of decimal digits. | ||
771 | * \+ 1 decimal digit. | ||
772 | * \X One or more repetitions of hexadecimal digits. | ||
773 | * \x 1 hexadecimal digit. | ||
774 | * \A One or more repetitions of alphabet characters. | ||
775 | * \a 1 alphabet character. | ||
776 | * | ||
777 | * \- Subtraction operator. | ||
778 | * | ||
779 | * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | ||
780 | * /dir/dir/dir/ ). | ||
781 | */ | ||
782 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, | ||
783 | const struct tomoyo_path_info *pattern) | ||
784 | { | ||
785 | const char *f = filename->name; | ||
786 | const char *p = pattern->name; | ||
787 | const int len = pattern->const_len; | ||
788 | |||
789 | /* If @pattern doesn't contain pattern, I can use strcmp(). */ | ||
790 | if (!pattern->is_patterned) | ||
791 | return !tomoyo_pathcmp(filename, pattern); | ||
792 | /* Don't compare directory and non-directory. */ | ||
793 | if (filename->is_dir != pattern->is_dir) | ||
794 | return false; | ||
795 | /* Compare the initial length without patterns. */ | ||
796 | if (strncmp(f, p, len)) | ||
797 | return false; | ||
798 | f += len; | ||
799 | p += len; | ||
800 | return tomoyo_path_matches_pattern2(f, p); | ||
801 | } | ||
802 | |||
803 | /** | ||
804 | * tomoyo_get_exe - Get tomoyo_realpath() of current process. | ||
805 | * | ||
806 | * Returns the tomoyo_realpath() of current process on success, NULL otherwise. | ||
807 | * | ||
808 | * This function uses kzalloc(), so the caller must call kfree() | ||
809 | * if this function didn't return NULL. | ||
810 | */ | ||
811 | const char *tomoyo_get_exe(void) | ||
812 | { | ||
813 | struct mm_struct *mm = current->mm; | ||
814 | struct vm_area_struct *vma; | ||
815 | const char *cp = NULL; | ||
816 | |||
817 | if (!mm) | ||
818 | return NULL; | ||
819 | down_read(&mm->mmap_sem); | ||
820 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | ||
821 | if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | ||
822 | cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); | ||
823 | break; | ||
824 | } | ||
825 | } | ||
826 | up_read(&mm->mmap_sem); | ||
827 | return cp; | ||
828 | } | ||
829 | |||
830 | /** | ||
831 | * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members. | ||
832 | * | ||
833 | * @r: Pointer to "struct tomoyo_request_info" to initialize. | ||
834 | * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain(). | ||
835 | * | ||
836 | * Returns mode. | ||
837 | */ | ||
838 | int tomoyo_init_request_info(struct tomoyo_request_info *r, | ||
839 | struct tomoyo_domain_info *domain) | ||
840 | { | ||
841 | memset(r, 0, sizeof(*r)); | ||
842 | if (!domain) | ||
843 | domain = tomoyo_domain(); | ||
844 | r->domain = domain; | ||
845 | r->mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); | ||
846 | return r->mode; | ||
847 | } | ||
848 | |||
849 | /** | ||
850 | * tomoyo_warn_log - Print warning or error message on console. | ||
851 | * | ||
852 | * @r: Pointer to "struct tomoyo_request_info". | ||
853 | * @fmt: The printf()'s format string, followed by parameters. | ||
854 | */ | ||
855 | void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) | ||
856 | { | ||
857 | int len = PAGE_SIZE; | ||
858 | va_list args; | ||
859 | char *buffer; | ||
860 | if (!tomoyo_verbose_mode(r->domain)) | ||
861 | return; | ||
862 | while (1) { | ||
863 | int len2; | ||
864 | buffer = kmalloc(len, GFP_NOFS); | ||
865 | if (!buffer) | ||
866 | return; | ||
867 | va_start(args, fmt); | ||
868 | len2 = vsnprintf(buffer, len - 1, fmt, args); | ||
869 | va_end(args); | ||
870 | if (len2 <= len - 1) { | ||
871 | buffer[len2] = '\0'; | ||
872 | break; | ||
873 | } | ||
874 | len = len2 + 1; | ||
875 | kfree(buffer); | ||
876 | } | ||
877 | printk(KERN_WARNING "TOMOYO-%s: Access %s denied for %s\n", | ||
878 | r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", | ||
879 | buffer, tomoyo_get_last_name(r->domain)); | ||
880 | kfree(buffer); | ||
881 | } | ||
882 | |||
883 | /** | ||
884 | * tomoyo_domain_quota_is_ok - Check for domain's quota. | ||
885 | * | ||
886 | * @r: Pointer to "struct tomoyo_request_info". | ||
887 | * | ||
888 | * Returns true if the domain is not exceeded quota, false otherwise. | ||
889 | * | ||
890 | * Caller holds tomoyo_read_lock(). | ||
891 | */ | ||
892 | bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) | ||
893 | { | ||
894 | unsigned int count = 0; | ||
895 | struct tomoyo_domain_info *domain = r->domain; | ||
896 | struct tomoyo_acl_info *ptr; | ||
897 | |||
898 | if (r->mode != TOMOYO_CONFIG_LEARNING) | ||
899 | return false; | ||
900 | if (!domain) | ||
901 | return true; | ||
902 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | ||
903 | switch (ptr->type) { | ||
904 | u16 perm; | ||
905 | u8 i; | ||
906 | case TOMOYO_TYPE_PATH_ACL: | ||
907 | perm = container_of(ptr, struct tomoyo_path_acl, head) | ||
908 | ->perm; | ||
909 | for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) | ||
910 | if (perm & (1 << i)) | ||
911 | count++; | ||
912 | if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) | ||
913 | count -= 2; | ||
914 | break; | ||
915 | case TOMOYO_TYPE_PATH2_ACL: | ||
916 | perm = container_of(ptr, struct tomoyo_path2_acl, head) | ||
917 | ->perm; | ||
918 | for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) | ||
919 | if (perm & (1 << i)) | ||
920 | count++; | ||
921 | break; | ||
922 | case TOMOYO_TYPE_PATH_NUMBER_ACL: | ||
923 | perm = container_of(ptr, struct tomoyo_path_number_acl, | ||
924 | head)->perm; | ||
925 | for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++) | ||
926 | if (perm & (1 << i)) | ||
927 | count++; | ||
928 | break; | ||
929 | case TOMOYO_TYPE_PATH_NUMBER3_ACL: | ||
930 | perm = container_of(ptr, struct tomoyo_path_number3_acl, | ||
931 | head)->perm; | ||
932 | for (i = 0; i < TOMOYO_MAX_PATH_NUMBER3_OPERATION; i++) | ||
933 | if (perm & (1 << i)) | ||
934 | count++; | ||
935 | break; | ||
936 | case TOMOYO_TYPE_MOUNT_ACL: | ||
937 | if (!container_of(ptr, struct tomoyo_mount_acl, head)-> | ||
938 | is_deleted) | ||
939 | count++; | ||
940 | } | ||
941 | } | ||
942 | if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) | ||
943 | return true; | ||
944 | if (!domain->quota_warned) { | ||
945 | domain->quota_warned = true; | ||
946 | printk(KERN_WARNING "TOMOYO-WARNING: " | ||
947 | "Domain '%s' has so many ACLs to hold. " | ||
948 | "Stopped learning mode.\n", domain->domainname->name); | ||
949 | } | ||
950 | return false; | ||
951 | } | ||