tomoyo-tools/0000755000000000000000000000000014116520000012225 5ustar rootroottomoyo-tools/README.tomoyo0000644000000000000000000000131014116520000014425 0ustar rootrootAbout this package: This package contains userland programs for TOMOYO Linux version 2.6. This package is released under the GPLv2. https://tomoyo.osdn.jp/ ChangeLog: Version 2.6.0 2019/03/05 Major update release. Adjust to TOMOYO 2.6. Version 2.6.0p1 2020/01/01 Bug fix release. /usr/lib/tomoyo/init_policy Remove "socket:[family=\\$:type=\\$:protocol=\\$]" from ANY_PATHNAME group. Version 2.6.0p2 2020/11/11 Bug fix release. Loosen pathname/domainname validation. Limit wildcard recursion depth. Version 2.6.1 2021/09/10 Bug fix release. Add -DNCURSES_WIDECHAR=0 to programs using ncurses library. ( https://lists.gnu.org/archive/html/bug-ncurses/2021-07/msg00021.html ) tomoyo-tools/usr_sbin/0000755000000000000000000000000014116520000014051 5ustar rootroottomoyo-tools/usr_sbin/tomoyo-setlevel.c0000644000000000000000000000401014116520000017357 0ustar rootroot/* * tomoyo-setlevel.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" int main(int argc, char *argv[]) { const char *policy_file = CCS_PROC_POLICY_PROFILE; int i; int fd; char c; ccs_mount_securityfs(); if (access(CCS_PROC_POLICY_DIR, F_OK)) { fprintf(stderr, "You can't use this command for this " "kernel.\n"); return 1; } fd = open(policy_file, O_RDWR); if (fd == EOF) { fprintf(stderr, "Can't open %s\n", policy_file); return 1; } else if (write(fd, "", 0) != 0) { fprintf(stderr, "You need to register this program to %s to " "run this program.\n", CCS_PROC_POLICY_MANAGER); return 1; } if (argc == 1) { printf("<<< Access Control Status >>>\n"); while (read(fd, &c, 1) == 1) putchar(c); } else { FILE *fp = fdopen(fd, "r+"); if (!fp) { fprintf(stderr, "Can't open %s\n", policy_file); close(fd); return 1; } for (i = 1; i < argc; i++) { char *cp = strchr(argv[i], '='); fprintf(fp, "%s\n", argv[i]); if (cp) *(cp + 1) = '\0'; } fflush(fp); ccs_get(); while (true) { char *line = ccs_freadline(fp); if (!line) break; for (i = 1; i < argc; i++) { if (strncmp(line, argv[i], strlen(argv[i]))) continue; printf("%s\n", line); break; } } ccs_put(); fclose(fp); } close(fd); return 0; } tomoyo-tools/usr_sbin/editpolicy_optimizer.c0000644000000000000000000002765614116520000020504 0ustar rootroot/* * editpolicy_optimizer.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" #include "editpolicy.h" static struct address_group *find_address_group(const char *group_name); static struct number_group *find_number_group(const char *group_name); static _Bool compare_address(const char *sarg, const char *darg); static _Bool compare_number(const char *sarg, const char *darg); static _Bool compare_path(const char *sarg, const char *darg); /** * find_address_group - Find an "address_group" by name. * * @group_name: Group name to find. * * Returns pointer to "struct address_group" if found, * NULL otherwise. */ static struct address_group *find_address_group(const char *group_name) { int i; for (i = 0; i < p.address_group_len; i++) if (!strcmp(group_name, p.address_group[i].group_name->name)) return &p.address_group[i]; return NULL; } /** * find_number_group - Find an "number_group" by name. * * @group_name: Group name to find. * * Returns pointer to "struct number_group" if found, * NULL otherwise. */ static struct number_group *find_number_group(const char *group_name) { int i; for (i = 0; i < p.number_group_len; i++) if (!strcmp(group_name, p.number_group[i].group_name->name)) return &p.number_group[i]; return NULL; } /** * compare_path - Compare two pathnames. * * @sarg: First pathname. Maybe wildcard. * @darg: Second pathname. * * Returns true if @darg is included in @sarg, false otherwise. */ static _Bool compare_path(const char *sarg, const char *darg) { int i; struct path_group *group; struct ccs_path_info s; struct ccs_path_info d; s.name = sarg; d.name = darg; ccs_fill_path_info(&s); ccs_fill_path_info(&d); if (!ccs_pathcmp(&s, &d)) return true; if (d.name[0] == '@') return false; if (s.name[0] != '@') /* Pathname component. */ return ccs_path_matches_pattern(&d, &s); /* path_group component. */ group = find_path_group_ns(current_ns, s.name + 1); if (!group) return false; for (i = 0; i < group->member_name_len; i++) { const struct ccs_path_info *member_name; member_name = group->member_name[i]; if (!ccs_pathcmp(member_name, &d)) return true; if (ccs_path_matches_pattern(&d, member_name)) return true; } return false; } /** * compare_address - Compare two IPv4/v6 addresses. * * @sarg: First address. * @darg: Second address. * * Returns true if @darg is included in @sarg, false otherwise. */ static _Bool compare_address(const char *sarg, const char *darg) { int i; struct ccs_ip_address_entry sentry; struct ccs_ip_address_entry dentry; struct address_group *group; if (ccs_parse_ip(darg, &dentry)) return false; if (sarg[0] != '@') { /* IP address component. */ if (ccs_parse_ip(sarg, &sentry)) return false; if (sentry.is_ipv6 != dentry.is_ipv6 || memcmp(dentry.min, sentry.min, 16) < 0 || memcmp(sentry.max, dentry.max, 16) < 0) return false; return true; } /* IP address group component. */ group = find_address_group(sarg + 1); if (!group) return false; for (i = 0; i < group->member_name_len; i++) { struct ccs_ip_address_entry *sentry = &group->member_name[i]; if (sentry->is_ipv6 == dentry.is_ipv6 && memcmp(sentry->min, dentry.min, 16) <= 0 && memcmp(dentry.max, sentry->max, 16) <= 0) return true; } return false; } /** * tokenize - Tokenize a line. * * @buffer: Line to tokenize. * @w: A "char *" array with 5 elements. * @index: One of values in "enum directive_type". * * Returns nothing. */ static void tokenize(char *buffer, char *w[5], enum directive_type index) { u8 i; u8 words; switch (index) { case DIRECTIVE_FILE_MKBLOCK: case DIRECTIVE_FILE_MKCHAR: case DIRECTIVE_FILE_MOUNT: case DIRECTIVE_NETWORK_INET: words = 4; break; case DIRECTIVE_NETWORK_UNIX: words = 3; break; case DIRECTIVE_FILE_CREATE: case DIRECTIVE_FILE_MKDIR: case DIRECTIVE_FILE_MKFIFO: case DIRECTIVE_FILE_MKSOCK: case DIRECTIVE_FILE_IOCTL: case DIRECTIVE_FILE_CHMOD: case DIRECTIVE_FILE_CHOWN: case DIRECTIVE_FILE_CHGRP: case DIRECTIVE_FILE_LINK: case DIRECTIVE_FILE_RENAME: case DIRECTIVE_FILE_PIVOT_ROOT: words = 2; break; case DIRECTIVE_FILE_EXECUTE: case DIRECTIVE_FILE_READ: case DIRECTIVE_FILE_WRITE: case DIRECTIVE_FILE_UNLINK: case DIRECTIVE_FILE_GETATTR: case DIRECTIVE_FILE_RMDIR: case DIRECTIVE_FILE_TRUNCATE: case DIRECTIVE_FILE_APPEND: case DIRECTIVE_FILE_UNMOUNT: case DIRECTIVE_FILE_CHROOT: case DIRECTIVE_FILE_SYMLINK: case DIRECTIVE_MISC_ENV: words = 1; break; default: words = 0; break; } for (i = 0; i < 5; i++) w[i] = ""; for (i = 0; i < words; i++) { char *cp = strchr(buffer, ' '); w[i] = buffer; if (!cp) return; *cp = '\0'; buffer = cp + 1; } w[4] = buffer; if (index != DIRECTIVE_FILE_EXECUTE) return; if (ccs_domain_def(buffer)) { char *cp = strchr(buffer, ' '); w[1] = buffer; w[4] = ""; if (!cp) return; while (*cp) { if (*cp++ != ' ' || *cp++ == '/') continue; cp -= 2; break; } if (!*cp) return; *cp = '\0'; w[4] = cp + 1; } else { char *cp = strchr(buffer, ' '); if (cp) *cp = '\0'; if (ccs_correct_path(buffer) || !strcmp(buffer, "keep") || !strcmp(buffer, "reset") || !strcmp(buffer, "initialize") || !strcmp(buffer, "child") || !strcmp(buffer, "parent")) { w[1] = buffer; if (cp) w[4] = cp + 1; else w[4] = ""; return; } if (cp) *cp = ' '; } } /** * compare_number - Compare two numeric values. * * @sarg: First number. * @darg: Second number. * * Returns true if @darg is included in @sarg, false otherwise. */ static _Bool compare_number(const char *sarg, const char *darg) { int i; struct ccs_number_entry sentry; struct ccs_number_entry dentry; struct number_group *group; if (ccs_parse_number(darg, &dentry)) return false; if (sarg[0] != '@') { /* Number component. */ if (ccs_parse_number(sarg, &sentry)) return false; if (sentry.min > dentry.min || sentry.max < dentry.max) return false; return true; } /* Number group component. */ group = find_number_group(sarg + 1); if (!group) return false; for (i = 0; i < group->member_name_len; i++) { struct ccs_number_entry *entry = &group->member_name[i]; if (entry->min > dentry.min || entry->max < dentry.max) continue; return true; } return false; } /** * editpolicy_do_optimize - Try to merge entries included in other entries. * * @cp: A line containing operand. * @s_index: Type of entry. * @s_index2: Type of entry. * @is_acl_group: True if optimizing acl_group, false otherwise. * * Returns nothing. */ static void editpolicy_do_optimize(char *cp, const int current, enum directive_type s_index, enum directive_type s_index2, const bool is_acl_group) { int index; char *s[5]; char *d[5]; tokenize(cp, s, s_index); ccs_get(); for (index = 0; index < p.generic_len; index++) { char *line; enum directive_type d_index = p.generic[index].directive; enum directive_type d_index2; if (index == current) /* Skip source. */ continue; if (p.generic[index].selected) /* Dest already selected. */ continue; else if (s_index == s_index2 && s_index != d_index) /* Source and dest have different directive. */ continue; else if (is_acl_group && s_index2 != d_index) /* Source and dest have different directive. */ continue; /* Source and dest have same directive. */ line = ccs_shprintf("%s", p.generic[index].operand); d_index2 = d_index; if (is_acl_group) d_index = find_directive(true, line); if (s_index != d_index || s_index2 != d_index2) /* Source and dest have different directive. */ continue; tokenize(line, d, d_index); /* Compare condition part. */ if (s[4][0] && strcmp(s[4], d[4])) continue; if (!s[4][0] && (!strncmp(d[4], "auto_domain_transition=", 23) || strstr(d[4], " auto_domain_transition=") || !strncmp(d[4], "grant_log=", 10) || strstr(d[4], " grant_log="))) continue; /* Compare non condition word. */ switch (d_index) { struct ccs_path_info sarg; struct ccs_path_info darg; case DIRECTIVE_FILE_EXECUTE: if (!compare_path(s[0], d[0])) continue; if (strcmp(s[1], d[1])) continue; break; case DIRECTIVE_FILE_MKBLOCK: case DIRECTIVE_FILE_MKCHAR: if (!compare_number(s[3], d[3]) || !compare_number(s[2], d[2])) continue; /* fall through */ case DIRECTIVE_FILE_CREATE: case DIRECTIVE_FILE_MKDIR: case DIRECTIVE_FILE_MKFIFO: case DIRECTIVE_FILE_MKSOCK: case DIRECTIVE_FILE_IOCTL: case DIRECTIVE_FILE_CHMOD: case DIRECTIVE_FILE_CHOWN: case DIRECTIVE_FILE_CHGRP: if (!compare_number(s[1], d[1])) continue; /* fall through */ case DIRECTIVE_FILE_READ: case DIRECTIVE_FILE_WRITE: case DIRECTIVE_FILE_UNLINK: case DIRECTIVE_FILE_GETATTR: case DIRECTIVE_FILE_RMDIR: case DIRECTIVE_FILE_TRUNCATE: case DIRECTIVE_FILE_APPEND: case DIRECTIVE_FILE_UNMOUNT: case DIRECTIVE_FILE_CHROOT: case DIRECTIVE_FILE_SYMLINK: if (!compare_path(s[0], d[0])) continue; break; case DIRECTIVE_FILE_MOUNT: if (!compare_number(s[3], d[3]) || !compare_path(s[2], d[2])) continue; /* fall through */ case DIRECTIVE_FILE_LINK: case DIRECTIVE_FILE_RENAME: case DIRECTIVE_FILE_PIVOT_ROOT: if (!compare_path(s[1], d[1]) || !compare_path(s[0], d[0])) continue; break; case DIRECTIVE_NETWORK_INET: if (strcmp(s[0], d[0]) || strcmp(s[1], d[1]) || !compare_address(s[2], d[2]) || !compare_number(s[3], d[3])) continue; break; case DIRECTIVE_NETWORK_UNIX: if (strcmp(s[0], d[0]) || strcmp(s[1], d[1]) || !compare_path(s[2], d[2])) continue; break; case DIRECTIVE_MISC_ENV: /* An environment variable name component. */ sarg.name = s[0]; ccs_fill_path_info(&sarg); darg.name = d[0]; ccs_fill_path_info(&darg); if (!ccs_pathcmp(&sarg, &darg)) break; /* "misc env" doesn't interpret leading @ as path_group. */ if (darg.is_patterned || !ccs_path_matches_pattern(&darg, &sarg)) continue; break; default: continue; } p.generic[index].selected = 1; } ccs_put(); } /** * editpolicy_optimize - Try to merge entries included in other entries. * * Returns nothing. */ void editpolicy_optimize(void) { char *cp; const bool is_acl_group = active == SCREEN_EXCEPTION_LIST; const int current = editpolicy_get_current(); enum directive_type s_index; enum directive_type s_index2; if (current < 0) return; s_index = p.generic[current].directive; if (s_index == DIRECTIVE_NONE) return; /* Allow acl_group lines to be optimized. */ if (is_acl_group && (s_index < DIRECTIVE_ACL_GROUP_000 || s_index > DIRECTIVE_ACL_GROUP_255)) return; if (s_index == DIRECTIVE_USE_GROUP) { unsigned int group = atoi(p.generic[current].operand); int i; if (group >= 256) return; for (i = 0; i < p.acl_group_len[group]; i++) { cp = strdup(p.acl_group[group][i]); if (!cp) return; s_index = find_directive(true, cp); if (s_index != DIRECTIVE_NONE) editpolicy_do_optimize(cp, -1, s_index, s_index, false); free(cp); } return; } cp = strdup(p.generic[current].operand); if (!cp) return; s_index2 = s_index; if (is_acl_group) s_index = find_directive(true, cp); editpolicy_do_optimize(cp, current, s_index, s_index2, is_acl_group); free(cp); } tomoyo-tools/usr_sbin/tomoyotools.c0000644000000000000000000015366514116520000016644 0ustar rootroot/* * tomoyotools.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2012 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" struct ccs_savename_entry { struct ccs_savename_entry *next; struct ccs_path_info entry; }; #define CCS_SAVENAME_MAX_HASH 256 /* Use tomoyo-editpolicy-agent process? */ _Bool ccs_network_mode = false; /* The IPv4 address of the remote host running the tomoyo-editpolicy-agent . */ u32 ccs_network_ip = INADDR_NONE; /* The port number of the remote host running the tomoyo-editpolicy-agent . */ u16 ccs_network_port = 0; /* The list of processes currently running. */ struct ccs_task_entry *ccs_task_list = NULL; /* The length of ccs_task_list . */ int ccs_task_list_len = 0; /* Read files without calling ccs_normalize_line() ? */ _Bool ccs_freadline_raw = false; /* Prototypes */ static _Bool ccs_byte_range(const char *str); static _Bool ccs_decimal(const char c); static _Bool ccs_hexadecimal(const char c); static _Bool ccs_alphabet_char(const char c); static u8 ccs_make_byte(const u8 c1, const u8 c2, const u8 c3); static int ccs_const_part_length(const char *filename); static int ccs_domainname_compare(const void *a, const void *b); static int ccs_path_info_compare(const void *a, const void *b); static void ccs_sort_domain_policy(struct ccs_domain_policy *dp); /* Utility functions */ /** * ccs_out_of_memory - Print error message and abort. * * This function does not return. */ static void ccs_out_of_memory(void) { fprintf(stderr, "Out of memory. Aborted.\n"); exit(1); } /** * ccs_strdup - strdup() with abort on error. * * @string: String to duplicate. * * Returns copy of @string on success, abort otherwise. */ char *ccs_strdup(const char *string) { char *cp = strdup(string); if (!cp) ccs_out_of_memory(); return cp; } /** * ccs_realloc - realloc() with abort on error. * * @ptr: Pointer to void. * @size: New size. * * Returns return value of realloc() on success, abort otherwise. */ void *ccs_realloc(void *ptr, const size_t size) { void *vp = realloc(ptr, size); if (!vp) ccs_out_of_memory(); return vp; } /** * ccs_realloc2 - realloc() with abort on error. * * @ptr: Pointer to void. * @size: New size. * * Returns return value of realloc() on success, abort otherwise. * * Allocated memory is cleared with 0. */ void *ccs_realloc2(void *ptr, const size_t size) { void *vp = ccs_realloc(ptr, size); memset(vp, 0, size); return vp; } /** * ccs_malloc - malloc() with abort on error. * * @size: Size to allocate. * * Returns return value of malloc() on success, abort otherwise. * * Allocated memory is cleared with 0. */ void *ccs_malloc(const size_t size) { void *vp = malloc(size); if (!vp) ccs_out_of_memory(); memset(vp, 0, size); return vp; } /** * ccs_str_starts - Check whether the given string starts with the given keyword. * * @str: Pointer to "char *". * @begin: Pointer to "const char *". * * Returns true if @str starts with @begin, false otherwise. * * Note that @begin will be removed from @str before returning true. Therefore, * @str must not be "const char *". * * Note that this function in kernel source has different arguments and behaves * differently. */ _Bool ccs_str_starts(char *str, const char *begin) { const int len = strlen(begin); if (strncmp(str, begin, len)) return false; memmove(str, str + len, strlen(str + len) + 1); return true; } /** * ccs_byte_range - Check whether the string is a \ooo style octal value. * * @str: Pointer to the string. * * Returns true if @str is a \ooo style octal value, false otherwise. */ static _Bool ccs_byte_range(const char *str) { return *str >= '0' && *str++ <= '3' && *str >= '0' && *str++ <= '7' && *str >= '0' && *str <= '7'; } /** * ccs_decimal - Check whether the character is a decimal character. * * @c: The character to check. * * Returns true if @c is a decimal character, false otherwise. */ static _Bool ccs_decimal(const char c) { return c >= '0' && c <= '9'; } /** * ccs_hexadecimal - Check whether the character is a hexadecimal character. * * @c: The character to check. * * Returns true if @c is a hexadecimal character, false otherwise. */ static _Bool ccs_hexadecimal(const char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } /** * ccs_alphabet_char - Check whether the character is an alphabet. * * @c: The character to check. * * Returns true if @c is an alphabet character, false otherwise. */ static _Bool ccs_alphabet_char(const char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } /** * ccs_make_byte - Make byte value from three octal characters. * * @c1: The first character. * @c2: The second character. * @c3: The third character. * * Returns byte value. */ static u8 ccs_make_byte(const u8 c1, const u8 c2, const u8 c3) { return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); } /** * ccs_normalize_line - Format string. * * @buffer: The line to normalize. * * Returns nothing. * * Leading and trailing whitespaces are removed. * Multiple whitespaces are packed into single space. */ void ccs_normalize_line(char *buffer) { unsigned char *sp = (unsigned char *) buffer; unsigned char *dp = (unsigned char *) buffer; _Bool first = true; while (*sp && (*sp <= ' ' || 127 <= *sp)) sp++; while (*sp) { if (!first) *dp++ = ' '; first = false; while (' ' < *sp && *sp < 127) *dp++ = *sp++; while (*sp && (*sp <= ' ' || 127 <= *sp)) sp++; } *dp = '\0'; } /** * ccs_partial_name_hash - Hash name. * * @c: A unsigned long value. * @prevhash: A previous hash value. * * Returns new hash value. * * This function is copied from partial_name_hash() in the kernel source. */ static inline unsigned long ccs_partial_name_hash(unsigned long c, unsigned long prevhash) { return (prevhash + (c << 4) + (c >> 4)) * 11; } /** * ccs_full_name_hash - Hash full name. * * @name: Pointer to "const unsigned char". * @len: Length of @name in byte. * * Returns hash value. * * This function is copied from full_name_hash() in the kernel source. */ static inline unsigned int ccs_full_name_hash(const unsigned char *name, unsigned int len) { unsigned long hash = 0; while (len--) hash = ccs_partial_name_hash(*name++, hash); return (unsigned int) hash; } /** * ccs_const_part_length - Evaluate the initial length without a pattern in a token. * * @filename: The string to evaluate. * * Returns the initial length without a pattern in @filename. */ static int ccs_const_part_length(const char *filename) { int len = 0; if (filename) { while (true) { char c = *filename++; if (!c) break; if (c != '\\') { len++; continue; } c = *filename++; switch (c) { case '\\': /* "\\" */ len += 2; continue; case '0': /* "\ooo" */ case '1': case '2': case '3': c = *filename++; if (c < '0' || c > '7') break; c = *filename++; if (c < '0' || c > '7') break; len += 4; continue; } break; } } return len; } /** * ccs_fprintf_encoded - fprintf() using TOMOYO's escape rules. * * @fp: Pointer to "FILE". * @pathname: String to print. * * Returns nothing. */ void ccs_fprintf_encoded(FILE *fp, const char *pathname) { while (true) { unsigned char c = *(const unsigned char *) pathname++; if (!c) break; if (c == '\\') { fputc('\\', fp); fputc('\\', fp); } else if (c > ' ' && c < 127) { fputc(c, fp); } else { fprintf(fp, "\\%c%c%c", (c >> 6) + '0', ((c >> 3) & 7) + '0', (c & 7) + '0'); } } } /** * ccs_decode - Decode a string in TOMOYO's rule to a string in C. * * @ascii: Pointer to "const char". * @bin: Pointer to "char". Must not contain wildcards nor '\000'. * * Returns true if @ascii was successfully decoded, false otherwise. * * Note that it is legal to pass @ascii == @bin if the caller want to decode * a string in a temporary buffer. */ _Bool ccs_decode(const char *ascii, char *bin) { while (true) { char c = *ascii++; *bin++ = c; if (!c) break; if (c == '\\') { char d; char e; u8 f; c = *ascii++; switch (c) { case '\\': /* "\\" */ continue; case '0': /* "\ooo" */ case '1': case '2': case '3': d = *ascii++; if (d < '0' || d > '7') break; e = *ascii++; if (e < '0' || e > '7') break; f = (u8) ((c - '0') << 6) + (((u8) (d - '0')) << 3) + (((u8) (e - '0'))); if (f <= ' ' || f >= 127) { *(bin - 1) = f; continue; } } return false; } else if (c <= ' ' || c >= 127) { return false; } } return true; } /** * ccs_correct_word2 - Check whether the given string follows the naming rules. * * @string: The byte sequence to check. Not '\0'-terminated. * @len: Length of @string. * * Returns true if @string follows the naming rules, false otherwise. */ static _Bool ccs_correct_word2(const char *string, size_t len) { u8 recursion = 20; const char *const start = string; _Bool in_repetition = false; if (!len) goto out; while (len--) { unsigned char c = *string++; if (c == '\\') { if (!len--) goto out; c = *string++; if (c >= '0' && c <= '3') { unsigned char d; unsigned char e; if (!len-- || !len--) goto out; d = *string++; e = *string++; if (d < '0' || d > '7' || e < '0' || e > '7') goto out; c = ccs_make_byte(c, d, e); if (c <= ' ' || c >= 127) continue; goto out; } switch (c) { case '\\': /* "\\" */ case '+': /* "\+" */ case '?': /* "\?" */ case 'x': /* "\x" */ case 'a': /* "\a" */ case '-': /* "\-" */ continue; } if (!recursion--) goto out; switch (c) { case '*': /* "\*" */ case '@': /* "\@" */ case '$': /* "\$" */ case 'X': /* "\X" */ case 'A': /* "\A" */ continue; case '{': /* "/\{" */ if (string - 3 < start || *(string - 3) != '/') goto out; in_repetition = true; continue; case '}': /* "\}/" */ if (*string != '/') goto out; if (!in_repetition) goto out; in_repetition = false; continue; } goto out; } else if (in_repetition && c == '/') { goto out; } else if (c <= ' ' || c >= 127) { goto out; } } if (in_repetition) goto out; return true; out: return false; } /** * ccs_correct_word - Check whether the given string follows the naming rules. * * @string: The string to check. * * Returns true if @string follows the naming rules, false otherwise. */ _Bool ccs_correct_word(const char *string) { return ccs_correct_word2(string, strlen(string)); } /** * ccs_correct_path2 - Check whether the given pathname follows the naming rules. * * @filename: The pathname to check. * @len: Length of @filename. * * Returns true if @filename follows the naming rules, false otherwise. */ _Bool ccs_correct_path2(const char *filename, const size_t len) { const char *cp1 = memchr(filename, '/', len); const char *cp2 = memchr(filename, '.', len); return cp1 && (!cp2 || (cp1 < cp2)) && ccs_correct_word2(filename, len); } /** * ccs_correct_path - Check whether the given pathname follows the naming rules. * * @filename: The pathname to check. * * Returns true if @filename follows the naming rules, false otherwise. */ _Bool ccs_correct_path(const char *filename) { return ccs_correct_path2(filename, strlen(filename)); } /** * ccs_domain_def - Check whether the given token can be a domainname. * * @buffer: The token to check. * * Returns true if @buffer possibly be a domainname, false otherwise. */ _Bool ccs_domain_def(const char *buffer) { const char *cp; int len; if (*buffer != '<') return false; cp = strchr(buffer, ' '); if (!cp) len = strlen(buffer); else len = cp - buffer; return buffer[len - 1] == '>' && ccs_correct_word2(buffer + 1, len - 2); } /** * ccs_correct_domain - Check whether the given domainname follows the naming rules. * * @domainname: The domainname to check. * * Returns true if @domainname follows the naming rules, false otherwise. */ _Bool ccs_correct_domain(const char *domainname) { if (!domainname || !ccs_domain_def(domainname)) return false; domainname = strchr(domainname, ' '); if (!domainname++) return true; while (1) { const char *cp = strchr(domainname, ' '); const int len = cp ? cp - domainname : strlen(domainname); if (len == 0) return true; if (!ccs_correct_path2(domainname, len)) return false; if (!cp) return true; domainname += len + 1; } } /** * ccs_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. * * @filename: The start of string to check. * @filename_end: The end of string to check. * @pattern: The start of pattern to compare. * @pattern_end: The end of pattern to compare. * * Returns true if @filename matches @pattern, false otherwise. */ static _Bool ccs_file_matches_pattern2(const char *filename, const char *filename_end, const char *pattern, const char *pattern_end) { while (filename < filename_end && pattern < pattern_end) { char c; if (*pattern != '\\') { if (*filename++ != *pattern++) return false; continue; } c = *filename; pattern++; switch (*pattern) { int i; int j; case '?': if (c == '/') { return false; } else if (c == '\\') { if (filename[1] == '\\') filename++; else if (ccs_byte_range(filename + 1)) filename += 3; else return false; } break; case '\\': if (c != '\\') return false; if (*++filename != '\\') return false; break; case '+': if (!ccs_decimal(c)) return false; break; case 'x': if (!ccs_hexadecimal(c)) return false; break; case 'a': if (!ccs_alphabet_char(c)) return false; break; case '0': case '1': case '2': case '3': if (c == '\\' && ccs_byte_range(filename + 1) && !strncmp(filename + 1, pattern, 3)) { filename += 3; pattern += 2; break; } return false; /* Not matched. */ case '*': case '@': for (i = 0; i <= filename_end - filename; i++) { if (ccs_file_matches_pattern2(filename + i, filename_end, pattern + 1, pattern_end)) return true; c = filename[i]; if (c == '.' && *pattern == '@') break; if (c != '\\') continue; if (filename[i + 1] == '\\') i++; else if (ccs_byte_range(filename + i + 1)) i += 3; else break; /* Bad pattern. */ } return false; /* Not matched. */ default: j = 0; c = *pattern; if (c == '$') { while (ccs_decimal(filename[j])) j++; } else if (c == 'X') { while (ccs_hexadecimal(filename[j])) j++; } else if (c == 'A') { while (ccs_alphabet_char(filename[j])) j++; } for (i = 1; i <= j; i++) { if (ccs_file_matches_pattern2(filename + i, filename_end, pattern + 1, pattern_end)) return true; } return false; /* Not matched or bad pattern. */ } filename++; pattern++; } while (*pattern == '\\' && (*(pattern + 1) == '*' || *(pattern + 1) == '@')) pattern += 2; return filename == filename_end && pattern == pattern_end; } /** * ccs_file_matches_pattern - Pattern matching without '/' character. * * @filename: The start of string to check. * @filename_end: The end of string to check. * @pattern: The start of pattern to compare. * @pattern_end: The end of pattern to compare. * * Returns true if @filename matches @pattern, false otherwise. */ static _Bool ccs_file_matches_pattern(const char *filename, const char *filename_end, const char *pattern, const char *pattern_end) { const char *pattern_start = pattern; _Bool first = true; _Bool result; while (pattern < pattern_end - 1) { /* Split at "\-" pattern. */ if (*pattern++ != '\\' || *pattern++ != '-') continue; result = ccs_file_matches_pattern2(filename, filename_end, pattern_start, pattern - 2); if (first) result = !result; if (result) return false; first = false; pattern_start = pattern; } result = ccs_file_matches_pattern2(filename, filename_end, pattern_start, pattern_end); return first ? result : !result; } /** * ccs_path_matches_pattern2 - Do pathname pattern matching. * * @f: The start of string to check. * @p: The start of pattern to compare. * * Returns true if @f matches @p, false otherwise. */ static _Bool ccs_path_matches_pattern2(const char *f, const char *p) { const char *f_delimiter; const char *p_delimiter; while (*f && *p) { f_delimiter = strchr(f, '/'); if (!f_delimiter) f_delimiter = f + strlen(f); p_delimiter = strchr(p, '/'); if (!p_delimiter) p_delimiter = p + strlen(p); if (*p == '\\' && *(p + 1) == '{') goto recursive; if (!ccs_file_matches_pattern(f, f_delimiter, p, p_delimiter)) return false; f = f_delimiter; if (*f) f++; p = p_delimiter; if (*p) p++; } /* Ignore trailing "\*" and "\@" in @pattern. */ while (*p == '\\' && (*(p + 1) == '*' || *(p + 1) == '@')) p += 2; return !*f && !*p; recursive: /* * The "\{" pattern is permitted only after '/' character. * This guarantees that below "*(p - 1)" is safe. * Also, the "\}" pattern is permitted only before '/' character * so that "\{" + "\}" pair will not break the "\-" operator. */ if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') return false; /* Bad pattern. */ do { /* Compare current component with pattern. */ if (!ccs_file_matches_pattern(f, f_delimiter, p + 2, p_delimiter - 2)) break; /* Proceed to next component. */ f = f_delimiter; if (!*f) break; f++; /* Continue comparison. */ if (ccs_path_matches_pattern2(f, p_delimiter + 1)) return true; f_delimiter = strchr(f, '/'); } while (f_delimiter); return false; /* Not matched. */ } /** * ccs_path_matches_pattern - Check whether the given filename matches the given pattern. * * @filename: The filename to check. * @pattern: The pattern to compare. * * Returns true if matches, false otherwise. * * The following patterns are available. * \\ \ itself. * \ooo Octal representation of a byte. * \* Zero or more repetitions of characters other than '/'. * \@ Zero or more repetitions of characters other than '/' or '.'. * \? 1 byte character other than '/'. * \$ One or more repetitions of decimal digits. * \+ 1 decimal digit. * \X One or more repetitions of hexadecimal digits. * \x 1 hexadecimal digit. * \A One or more repetitions of alphabet characters. * \a 1 alphabet character. * * \- Subtraction operator. * * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ * /dir/dir/dir/ ). */ _Bool ccs_path_matches_pattern(const struct ccs_path_info *filename, const struct ccs_path_info *pattern) { /* if (!filename || !pattern) return false; */ const char *f = filename->name; const char *p = pattern->name; const int len = pattern->const_len; /* If @pattern doesn't contain pattern, I can use strcmp(). */ if (!pattern->is_patterned) return !ccs_pathcmp(filename, pattern); /* Don't compare directory and non-directory. */ if (filename->is_dir != pattern->is_dir) return false; /* Compare the initial length without patterns. */ if (strncmp(f, p, len)) return false; f += len; p += len; return ccs_path_matches_pattern2(f, p); } /** * ccs_string_compare - strcmp() for qsort() callback. * * @a: Pointer to "void". * @b: Pointer to "void". * * Returns return value of strcmp(). */ int ccs_string_compare(const void *a, const void *b) { return strcmp(*(char **) a, *(char **) b); } /** * ccs_pathcmp - strcmp() for "struct ccs_path_info". * * @a: Pointer to "const struct ccs_path_info". * @b: Pointer to "const struct ccs_path_info". * * Returns true if @a != @b, false otherwise. */ _Bool ccs_pathcmp(const struct ccs_path_info *a, const struct ccs_path_info *b) { return a->hash != b->hash || strcmp(a->name, b->name); } /** * ccs_fill_path_info - Fill in "struct ccs_path_info" members. * * @ptr: Pointer to "struct ccs_path_info" to fill in. * * The caller sets "struct ccs_path_info"->name. */ void ccs_fill_path_info(struct ccs_path_info *ptr) { const char *name = ptr->name; const int len = strlen(name); ptr->total_len = len; ptr->const_len = ccs_const_part_length(name); ptr->is_dir = len && (name[len - 1] == '/'); ptr->is_patterned = (ptr->const_len < len); ptr->hash = ccs_full_name_hash((const unsigned char *) name, len); } /** * ccs_savename - Remember string data. * * @name: Pointer to "const char". * * Returns pointer to "const struct ccs_path_info" on success, abort otherwise. * * The returned pointer refers shared string. Thus, the caller must not free(). */ const struct ccs_path_info *ccs_savename(const char *name) { /* The list of names. */ static struct ccs_savename_entry name_list[CCS_SAVENAME_MAX_HASH]; struct ccs_savename_entry *ptr; struct ccs_savename_entry *prev = NULL; unsigned int hash; int len; static _Bool first_call = true; if (!name) ccs_out_of_memory(); len = strlen(name) + 1; hash = ccs_full_name_hash((const unsigned char *) name, len - 1); if (first_call) { int i; first_call = false; memset(&name_list, 0, sizeof(name_list)); for (i = 0; i < CCS_SAVENAME_MAX_HASH; i++) { name_list[i].entry.name = "/"; ccs_fill_path_info(&name_list[i].entry); } } for (ptr = &name_list[hash % CCS_SAVENAME_MAX_HASH]; ptr; ptr = ptr->next) { if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name)) return &ptr->entry; prev = ptr; } ptr = ccs_malloc(sizeof(*ptr) + len); ptr->next = NULL; ptr->entry.name = ((char *) ptr) + sizeof(*ptr); memmove((void *) ptr->entry.name, name, len); ccs_fill_path_info(&ptr->entry); prev->next = ptr; /* prev != NULL because name_list is not empty. */ return &ptr->entry; } /** * ccs_parse_number - Parse a ccs_number_entry. * * @number: Number or number range. * @entry: Pointer to "struct ccs_number_entry". * * Returns 0 on success, -EINVAL otherwise. */ int ccs_parse_number(const char *number, struct ccs_number_entry *entry) { unsigned long min; unsigned long max; char *cp; memset(entry, 0, sizeof(*entry)); if (number[0] != '0') { if (sscanf(number, "%lu", &min) != 1) return -EINVAL; } else if (number[1] == 'x' || number[1] == 'X') { if (sscanf(number + 2, "%lX", &min) != 1) return -EINVAL; } else if (sscanf(number, "%lo", &min) != 1) return -EINVAL; cp = strchr(number, '-'); if (cp) number = cp + 1; if (number[0] != '0') { if (sscanf(number, "%lu", &max) != 1) return -EINVAL; } else if (number[1] == 'x' || number[1] == 'X') { if (sscanf(number + 2, "%lX", &max) != 1) return -EINVAL; } else if (sscanf(number, "%lo", &max) != 1) return -EINVAL; entry->min = min; entry->max = max; return 0; } /* * Routines for parsing IPv4 or IPv6 address. * These are copied from lib/hexdump.c net/core/utils.c . */ #include static int hex_to_bin(char ch) { if ((ch >= '0') && (ch <= '9')) return ch - '0'; ch = tolower(ch); if ((ch >= 'a') && (ch <= 'f')) return ch - 'a' + 10; return -1; } #define IN6PTON_XDIGIT 0x00010000 #define IN6PTON_DIGIT 0x00020000 #define IN6PTON_COLON_MASK 0x00700000 #define IN6PTON_COLON_1 0x00100000 /* single : requested */ #define IN6PTON_COLON_2 0x00200000 /* second : requested */ #define IN6PTON_COLON_1_2 0x00400000 /* :: requested */ #define IN6PTON_DOT 0x00800000 /* . */ #define IN6PTON_DELIM 0x10000000 #define IN6PTON_NULL 0x20000000 /* first/tail */ #define IN6PTON_UNKNOWN 0x40000000 static inline int xdigit2bin(char c, int delim) { int val; if (c == delim || c == '\0') return IN6PTON_DELIM; if (c == ':') return IN6PTON_COLON_MASK; if (c == '.') return IN6PTON_DOT; val = hex_to_bin(c); if (val >= 0) return val | IN6PTON_XDIGIT | (val < 10 ? IN6PTON_DIGIT : 0); if (delim == -1) return IN6PTON_DELIM; return IN6PTON_UNKNOWN; } static int in4_pton(const char *src, int srclen, u8 *dst, int delim, const char **end) { const char *s; u8 *d; u8 dbuf[4]; int ret = 0; int i; int w = 0; if (srclen < 0) srclen = strlen(src); s = src; d = dbuf; i = 0; while(1) { int c; c = xdigit2bin(srclen > 0 ? *s : '\0', delim); if (!(c & (IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK))) { goto out; } if (c & (IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK)) { if (w == 0) goto out; *d++ = w & 0xff; w = 0; i++; if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { if (i != 4) goto out; break; } goto cont; } w = (w * 10) + c; if ((w & 0xffff) > 255) { goto out; } cont: if (i >= 4) goto out; s++; srclen--; } ret = 1; memcpy(dst, dbuf, sizeof(dbuf)); out: if (end) *end = s; return ret; } static int in6_pton(const char *src, int srclen, u8 *dst, int delim, const char **end) { const char *s, *tok = NULL; u8 *d, *dc = NULL; u8 dbuf[16]; int ret = 0; int i; int state = IN6PTON_COLON_1_2 | IN6PTON_XDIGIT | IN6PTON_NULL; int w = 0; memset(dbuf, 0, sizeof(dbuf)); s = src; d = dbuf; if (srclen < 0) srclen = strlen(src); while (1) { int c; c = xdigit2bin(srclen > 0 ? *s : '\0', delim); if (!(c & state)) goto out; if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { /* process one 16-bit word */ if (!(state & IN6PTON_NULL)) { *d++ = (w >> 8) & 0xff; *d++ = w & 0xff; } w = 0; if (c & IN6PTON_DELIM) { /* We've processed last word */ break; } /* * COLON_1 => XDIGIT * COLON_2 => XDIGIT|DELIM * COLON_1_2 => COLON_2 */ switch (state & IN6PTON_COLON_MASK) { case IN6PTON_COLON_2: dc = d; state = IN6PTON_XDIGIT | IN6PTON_DELIM; if (dc - dbuf >= sizeof(dbuf)) state |= IN6PTON_NULL; break; case IN6PTON_COLON_1|IN6PTON_COLON_1_2: state = IN6PTON_XDIGIT | IN6PTON_COLON_2; break; case IN6PTON_COLON_1: state = IN6PTON_XDIGIT; break; case IN6PTON_COLON_1_2: state = IN6PTON_COLON_2; break; default: state = 0; } tok = s + 1; goto cont; } if (c & IN6PTON_DOT) { ret = in4_pton(tok ? tok : s, srclen + (int)(s - tok), d, delim, &s); if (ret > 0) { d += 4; break; } goto out; } w = (w << 4) | (0xff & c); state = IN6PTON_COLON_1 | IN6PTON_DELIM; if (!(w & 0xf000)) { state |= IN6PTON_XDIGIT; } if (!dc && d + 2 < dbuf + sizeof(dbuf)) { state |= IN6PTON_COLON_1_2; state &= ~IN6PTON_DELIM; } if (d + 2 >= dbuf + sizeof(dbuf)) { state &= ~(IN6PTON_COLON_1|IN6PTON_COLON_1_2); } cont: if ((dc && d + 4 < dbuf + sizeof(dbuf)) || d + 4 == dbuf + sizeof(dbuf)) { state |= IN6PTON_DOT; } if (d >= dbuf + sizeof(dbuf)) { state &= ~(IN6PTON_XDIGIT|IN6PTON_COLON_MASK); } s++; srclen--; } i = 15; d--; if (dc) { while(d >= dc) dst[i--] = *d--; while(i >= dc - dbuf) dst[i--] = 0; while(i >= 0) dst[i--] = *d--; } else memcpy(dst, dbuf, sizeof(dbuf)); ret = 1; out: if (end) *end = s; return ret; } /** * ccs_parse_ip - Parse a ccs_ip_address_entry. * * @address: IP address or IP range. * @entry: Pointer to "struct ccs_address_entry". * * Returns 0 on success, -EINVAL otherwise. */ int ccs_parse_ip(const char *address, struct ccs_ip_address_entry *entry) { u8 * const min = entry->min; u8 * const max = entry->max; const char *end; memset(entry, 0, sizeof(*entry)); if (!strchr(address, ':') && in4_pton(address, -1, min, '-', &end) > 0) { entry->is_ipv6 = false; if (!*end) memmove(max, min, 4); else if (*end++ != '-' || in4_pton(end, -1, max, '\0', &end) <= 0 || *end) return -EINVAL; return 0; } if (in6_pton(address, -1, min, '-', &end) > 0) { entry->is_ipv6 = true; if (!*end) memmove(max, min, 16); else if (*end++ != '-' || in6_pton(end, -1, max, '\0', &end) <= 0 || *end) return -EINVAL; return 0; } return -EINVAL; } /** * ccs_open_stream - Establish IP connection. * * @filename: String to send to remote tomoyo-editpolicy-agent program. * * Returns file descriptor on success, EOF otherwise. */ int ccs_open_stream(const char *filename) { const int fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; char c; int len = strlen(filename) + 1; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = ccs_network_ip; addr.sin_port = ccs_network_port; if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) || write(fd, filename, len) != len || read(fd, &c, 1) != 1 || c) { close(fd); return EOF; } return fd; } /** * ccs_find_domain - Find a domain by name and other attributes. * * @dp: Pointer to "const struct ccs_domain_policy". * @domainname0: Name of domain to find. * * Returns index number (>= 0) in the @dp array if found, EOF otherwise. */ int ccs_find_domain(const struct ccs_domain_policy *dp, const char *domainname0) { int i; struct ccs_path_info domainname; domainname.name = domainname0; ccs_fill_path_info(&domainname); for (i = 0; i < dp->list_len; i++) { if (!ccs_pathcmp(&domainname, dp->list[i].domainname)) return i; } return EOF; } /** * ccs_assign_domain - Create a domain by name and other attributes. * * @dp: Pointer to "struct ccs_domain_policy". * @domainname: Name of domain to find. * * Returns index number (>= 0) in the @dp array if created or already exists, * abort otherwise. */ int ccs_assign_domain(struct ccs_domain_policy *dp, const char *domainname) { int index = ccs_find_domain(dp, domainname); if (index >= 0) return index; if (!ccs_correct_domain(domainname)) { fprintf(stderr, "Invalid domainname '%s'\n", domainname); ccs_out_of_memory(); } index = dp->list_len++; dp->list = ccs_realloc(dp->list, dp->list_len * sizeof(struct ccs_domain_info)); memset(&dp->list[index], 0, sizeof(struct ccs_domain_info)); dp->list[index].domainname = ccs_savename(domainname); return index; } /** * ccs_get_ppid - Get PPID of the given PID. * * @pid: A pid_t value. * * Returns PPID value. */ static pid_t ccs_get_ppid(const pid_t pid) { char buffer[1024]; FILE *fp; pid_t ppid = 1; memset(buffer, 0, sizeof(buffer)); snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/status", pid); fp = fopen(buffer, "r"); if (fp) { while (memset(buffer, 0, sizeof(buffer)) && fgets(buffer, sizeof(buffer) - 1, fp)) { if (sscanf(buffer, "PPid: %u", &ppid) == 1) break; } fclose(fp); } return ppid; } /** * ccs_get_name - Get comm name of the given PID. * * @pid: A pid_t value. * * Returns comm name using on success, NULL otherwise. * * The caller must free() the returned pointer. */ static char *ccs_get_name(const pid_t pid) { char buffer[1024]; FILE *fp; memset(buffer, 0, sizeof(buffer)); snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/status", pid); fp = fopen(buffer, "r"); if (fp) { static const int offset = sizeof(buffer) / 6; while (memset(buffer, 0, sizeof(buffer)) && fgets(buffer, sizeof(buffer) - 1, fp)) { if (!strncmp(buffer, "Name:\t", 6)) { char *cp = buffer + 6; memmove(buffer, cp, strlen(cp) + 1); cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; break; } } fclose(fp); if (buffer[0] && strlen(buffer) < offset - 1) { const char *src = buffer; char *dest = buffer + offset; while (1) { unsigned char c = *src++; if (!c) { *dest = '\0'; break; } if (c == '\\') { c = *src++; if (c == '\\') { memmove(dest, "\\\\", 2); dest += 2; } else if (c == 'n') { memmove(dest, "\\012", 4); dest += 4; } else { break; } } else if (c > ' ' && c <= 126) { *dest++ = c; } else { *dest++ = '\\'; *dest++ = (c >> 6) + '0'; *dest++ = ((c >> 3) & 7) + '0'; *dest++ = (c & 7) + '0'; } } return strdup(buffer + offset); } } return NULL; } /* Serial number for sorting ccs_task_list . */ static int ccs_dump_index = 0; /** * ccs_sort_process_entry - Sort ccs_tasklist list. * * @pid: Pid to search. * @depth: Depth of the process for printing like pstree command. * * Returns nothing. */ static void ccs_sort_process_entry(const pid_t pid, const int depth) { int i; for (i = 0; i < ccs_task_list_len; i++) { if (pid != ccs_task_list[i].pid) continue; ccs_task_list[i].index = ccs_dump_index++; ccs_task_list[i].depth = depth; ccs_task_list[i].selected = true; } for (i = 0; i < ccs_task_list_len; i++) { if (pid != ccs_task_list[i].ppid) continue; ccs_sort_process_entry(ccs_task_list[i].pid, depth + 1); } } /** * ccs_task_entry_compare - Compare routine for qsort() callback. * * @a: Pointer to "void". * @b: Pointer to "void". * * Returns index diff value. */ static int ccs_task_entry_compare(const void *a, const void *b) { const struct ccs_task_entry *a0 = (struct ccs_task_entry *) a; const struct ccs_task_entry *b0 = (struct ccs_task_entry *) b; return a0->index - b0->index; } /** * ccs_add_process_entry - Add entry for running processes. * * @line: A line containing PID and profile and domainname. * @ppid: Parent PID. * @name: Comm name (allocated by strdup()). * * Returns nothing. * * @name is free()d on failure. */ static void ccs_add_process_entry(const char *line, const pid_t ppid, char *name) { int index; unsigned int pid = 0; int profile = -1; char *domain; if (!line || sscanf(line, "%u %u", &pid, &profile) != 2) { free(name); return; } domain = strchr(line, '<'); if (domain) domain = ccs_strdup(domain); else domain = ccs_strdup(""); index = ccs_task_list_len++; ccs_task_list = ccs_realloc(ccs_task_list, ccs_task_list_len * sizeof(struct ccs_task_entry)); memset(&ccs_task_list[index], 0, sizeof(ccs_task_list[0])); ccs_task_list[index].pid = pid; ccs_task_list[index].ppid = ppid; ccs_task_list[index].profile = profile; ccs_task_list[index].name = name; ccs_task_list[index].domain = domain; } /** * ccs_read_process_list - Read all process's information. * * @show_all: True if kernel threads should be included, false otherwise. * * Returns nothing. */ void ccs_read_process_list(_Bool show_all) { int i; while (ccs_task_list_len) { ccs_task_list_len--; free((void *) ccs_task_list[ccs_task_list_len].name); free((void *) ccs_task_list[ccs_task_list_len].domain); } ccs_dump_index = 0; if (ccs_network_mode) { FILE *fp = ccs_open_write(show_all ? "proc:all_process_status" : "proc:process_status"); if (!fp) return; ccs_get(); while (true) { char *line = ccs_freadline(fp); unsigned int pid = 0; unsigned int ppid = 0; char *name; if (!line) break; sscanf(line, "PID=%u PPID=%u", &pid, &ppid); name = strstr(line, "NAME="); if (name) name = ccs_strdup(name + 5); else name = ccs_strdup(""); line = ccs_freadline(fp); ccs_add_process_entry(line, ppid, name); } ccs_put(); fclose(fp); } else { static const int line_len = 8192; char *line; int status_fd = open(CCS_PROC_POLICY_PROCESS_STATUS, O_RDWR); DIR *dir = opendir("/proc/"); if (status_fd == EOF || !dir) { if (status_fd != EOF) close(status_fd); if (dir) closedir(dir); return; } line = ccs_malloc(line_len); while (1) { char *name; int ret_ignored; unsigned int pid = 0; char buffer[128]; char test[16]; struct dirent *dent = readdir(dir); if (!dent) break; if (dent->d_type != DT_DIR || sscanf(dent->d_name, "%u", &pid) != 1 || !pid) continue; memset(buffer, 0, sizeof(buffer)); if (!show_all) { snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/exe", pid); if (readlink(buffer, test, sizeof(test)) <= 0) continue; } name = ccs_get_name(pid); if (!name) name = ccs_strdup(""); snprintf(buffer, sizeof(buffer) - 1, "%u\n", pid); ret_ignored = write(status_fd, buffer, strlen(buffer)); memset(line, 0, line_len); ret_ignored = read(status_fd, line, line_len - 1); ccs_add_process_entry(line, ccs_get_ppid(pid), name); } free(line); closedir(dir); close(status_fd); } ccs_sort_process_entry(1, 0); for (i = 0; i < ccs_task_list_len; i++) { if (ccs_task_list[i].selected) { ccs_task_list[i].selected = false; continue; } ccs_task_list[i].index = ccs_dump_index++; ccs_task_list[i].depth = 0; } qsort(ccs_task_list, ccs_task_list_len, sizeof(struct ccs_task_entry), ccs_task_entry_compare); } /** * ccs_open_write - Open a file for writing. * * @filename: String to send to remote tomoyo-editpolicy-agent program if using * network mode, file to open for writing otherwise. * * Returns pointer to "FILE" on success, NULL otherwise. */ FILE *ccs_open_write(const char *filename) { if (ccs_network_mode) { const int fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; FILE *fp; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = ccs_network_ip; addr.sin_port = ccs_network_port; if (connect(fd, (struct sockaddr *) &addr, sizeof(addr))) { close(fd); return NULL; } fp = fdopen(fd, "r+"); /* setbuf(fp, NULL); */ fprintf(fp, "%s", filename); fputc(0, fp); fflush(fp); if (fgetc(fp) != 0) { fclose(fp); return NULL; } return fp; } else { return fdopen(open(filename, O_WRONLY), "w"); } } /** * ccs_close_write - Close stream opened by ccs_open_write(). * * @fp: Pointer to "FILE". * * Returns true on success, false otherwise. */ _Bool ccs_close_write(FILE *fp) { _Bool result = true; if (ccs_network_mode) { if (fputc(0, fp) == EOF) result = false; if (fflush(fp) == EOF) result = false; if (fgetc(fp) == EOF) result = false; } if (fclose(fp) == EOF) result = false; return result; } /** * ccs_open_read - Open a file for reading. * * @filename: String to send to remote tomoyo-editpolicy-agent program if using * network mode, file to open for reading otherwise. * * Returns pointer to "FILE" on success, NULL otherwise. */ FILE *ccs_open_read(const char *filename) { if (ccs_network_mode) { FILE *fp = ccs_open_write(filename); if (fp) { fputc(0, fp); fflush(fp); } return fp; } else { return fopen(filename, "r"); } } /** * ccs_move_proc_to_file - Save /sys/kernel/security/tomoyo/ to /etc/tomoyo/ . * * @src: Filename to save from. * @dest: Filename to save to. * * Returns true on success, false otherwise. */ _Bool ccs_move_proc_to_file(const char *src, const char *dest) { FILE *proc_fp = ccs_open_read(src); FILE *file_fp; _Bool result = true; if (!proc_fp) { fprintf(stderr, "Can't open %s for reading.\n", src); return false; } file_fp = dest ? fopen(dest, "w") : stdout; if (!file_fp) { fprintf(stderr, "Can't open %s for writing.\n", dest); fclose(proc_fp); return false; } while (true) { const int c = fgetc(proc_fp); if (ccs_network_mode && !c) break; if (c == EOF) break; if (fputc(c, file_fp) == EOF) result = false; } fclose(proc_fp); if (file_fp != stdout) if (fclose(file_fp) == EOF) result = false; return result; } /** * ccs_clear_domain_policy - Clean up domain policy. * * @dp: Pointer to "struct ccs_domain_policy". * * Returns nothing. */ void ccs_clear_domain_policy(struct ccs_domain_policy *dp) { int index; for (index = 0; index < dp->list_len; index++) { free(dp->list[index].string_ptr); dp->list[index].string_ptr = NULL; dp->list[index].string_count = 0; } free(dp->list); dp->list = NULL; dp->list_len = 0; } /** * ccs_find_domain_by_ptr - Find a domain by memory address. * * @dp: Pointer to "struct ccs_domain_policy". * @domainname: Pointer to "const struct ccs_path_info". * * Returns index number (>= 0) in the @dp array if found, EOF otherwise. * * This function is faster than faster than ccs_find_domain() because * this function searches for a domain by address (i.e. avoid strcmp()). */ int ccs_find_domain_by_ptr(struct ccs_domain_policy *dp, const struct ccs_path_info *domainname) { int i; for (i = 0; i < dp->list_len; i++) { if (dp->list[i].domainname == domainname) return i; } return EOF; } /** * ccs_domain_name - Return domainname. * * @dp: Pointer to "const struct ccs_domain_policy". * @index: Index in the @dp array. * * Returns domainname. * * Note that this function does not validate @index value. */ const char *ccs_domain_name(const struct ccs_domain_policy *dp, const int index) { return dp->list[index].domainname->name; } /** * ccs_domainname_compare - strcmp() for qsort() callback. * * @a: Pointer to "void". * @b: Pointer to "void". * * Returns return value of strcmp(). */ static int ccs_domainname_compare(const void *a, const void *b) { return strcmp(((struct ccs_domain_info *) a)->domainname->name, ((struct ccs_domain_info *) b)->domainname->name); } /** * ccs_path_info_compare - strcmp() for qsort() callback. * * @a: Pointer to "void". * @b: Pointer to "void". * * Returns return value of strcmp(). */ static int ccs_path_info_compare(const void *a, const void *b) { const char *a0 = (*(struct ccs_path_info **) a)->name; const char *b0 = (*(struct ccs_path_info **) b)->name; return strcmp(a0, b0); } /** * ccs_sort_domain_policy - Sort domain policy. * * @dp: Pointer to "struct ccs_domain_policy". * * Returns nothing. */ static void ccs_sort_domain_policy(struct ccs_domain_policy *dp) { int i; qsort(dp->list, dp->list_len, sizeof(struct ccs_domain_info), ccs_domainname_compare); for (i = 0; i < dp->list_len; i++) qsort(dp->list[i].string_ptr, dp->list[i].string_count, sizeof(struct ccs_path_info *), ccs_path_info_compare); } /** * ccs_read_domain_policy - Read domain policy from file or network or stdin. * * @dp: Pointer to "struct ccs_domain_policy". * @filename: Domain policy's pathname. * * Returns nothing. */ void ccs_read_domain_policy(struct ccs_domain_policy *dp, const char *filename) { FILE *fp = stdin; if (filename) { fp = ccs_open_read(filename); if (!fp) { fprintf(stderr, "Can't open %s\n", filename); return; } } ccs_get(); ccs_handle_domain_policy(dp, fp, true); ccs_put(); if (fp != stdin) fclose(fp); ccs_sort_domain_policy(dp); } /** * ccs_write_domain_policy - Write domain policy to file descriptor. * * @dp: Pointer to "struct ccs_domain_policy". * @fd: File descriptor. * * Returns 0. */ int ccs_write_domain_policy(struct ccs_domain_policy *dp, const int fd) { int i; int j; for (i = 0; i < dp->list_len; i++) { const struct ccs_path_info **string_ptr = dp->list[i].string_ptr; const int string_count = dp->list[i].string_count; int ret_ignored; ret_ignored = write(fd, dp->list[i].domainname->name, dp->list[i].domainname->total_len); ret_ignored = write(fd, "\n", 1); if (dp->list[i].profile_assigned) { char buf[128]; memset(buf, 0, sizeof(buf)); snprintf(buf, sizeof(buf) - 1, "use_profile %u\n\n", dp->list[i].profile); ret_ignored = write(fd, buf, strlen(buf)); } else ret_ignored = write(fd, "\n", 1); for (j = 0; j < string_count; j++) { ret_ignored = write(fd, string_ptr[j]->name, string_ptr[j]->total_len); ret_ignored = write(fd, "\n", 1); } ret_ignored = write(fd, "\n", 1); } return 0; } /** * ccs_delete_domain - Delete a domain from domain policy. * * @dp: Pointer to "struct ccs_domain_policy". * @index: Index in the @dp array. * * Returns nothing. */ void ccs_delete_domain(struct ccs_domain_policy *dp, const int index) { if (index >= 0 && index < dp->list_len) { int i; free(dp->list[index].string_ptr); for (i = index; i < dp->list_len - 1; i++) dp->list[i] = dp->list[i + 1]; dp->list_len--; } } /** * ccs_add_string_entry - Add string entry to a domain. * * @dp: Pointer to "struct ccs_domain_policy". * @entry: String to add. * @index: Index in the @dp array. * * Returns 0 if successfully added or already exists, -EINVAL otherwise. */ int ccs_add_string_entry(struct ccs_domain_policy *dp, const char *entry, const int index) { const struct ccs_path_info **acl_ptr; int acl_count; const struct ccs_path_info *cp; int i; if (index < 0 || index >= dp->list_len) { fprintf(stderr, "ERROR: domain is out of range.\n"); return -EINVAL; } if (!entry || !*entry) return -EINVAL; cp = ccs_savename(entry); acl_ptr = dp->list[index].string_ptr; acl_count = dp->list[index].string_count; /* Check for the same entry. */ for (i = 0; i < acl_count; i++) /* Faster comparison, for they are ccs_savename'd. */ if (cp == acl_ptr[i]) return 0; acl_ptr = ccs_realloc(acl_ptr, (acl_count + 1) * sizeof(const struct ccs_path_info *)); acl_ptr[acl_count++] = cp; dp->list[index].string_ptr = acl_ptr; dp->list[index].string_count = acl_count; return 0; } /** * ccs_del_string_entry - Delete string entry from a domain. * * @dp: Pointer to "struct ccs_domain_policy". * @entry: String to remove. * @index: Index in the @dp array. * * Returns 0 if successfully removed, -ENOENT if not found, * -EINVAL otherwise. */ int ccs_del_string_entry(struct ccs_domain_policy *dp, const char *entry, const int index) { const struct ccs_path_info **acl_ptr; int acl_count; const struct ccs_path_info *cp; int i; if (index < 0 || index >= dp->list_len) { fprintf(stderr, "ERROR: domain is out of range.\n"); return -EINVAL; } if (!entry || !*entry) return -EINVAL; cp = ccs_savename(entry); acl_ptr = dp->list[index].string_ptr; acl_count = dp->list[index].string_count; for (i = 0; i < acl_count; i++) { /* Faster comparison, for they are ccs_savename'd. */ if (cp != acl_ptr[i]) continue; dp->list[index].string_count--; for (; i < acl_count - 1; i++) acl_ptr[i] = acl_ptr[i + 1]; return 0; } return -ENOENT; } /** * ccs_handle_domain_policy - Parse domain policy. * * @dp: Pointer to "struct ccs_domain_policy". * @fp: Pointer to "FILE". * @is_write: True if input, false if output. * * Returns nothing. */ void ccs_handle_domain_policy(struct ccs_domain_policy *dp, FILE *fp, _Bool is_write) { int i; int index = EOF; if (!is_write) goto read_policy; while (true) { char *line = ccs_freadline_unpack(fp); _Bool is_delete = false; _Bool is_select = false; unsigned int profile; if (!line) break; if (ccs_str_starts(line, "delete ")) is_delete = true; else if (ccs_str_starts(line, "select ")) is_select = true; ccs_str_starts(line, "domain="); if (ccs_domain_def(line)) { if (is_delete) { index = ccs_find_domain(dp, line); if (index >= 0) ccs_delete_domain(dp, index); index = EOF; continue; } if (is_select) { index = ccs_find_domain(dp, line); continue; } index = ccs_assign_domain(dp, line); continue; } if (index == EOF || !line[0]) continue; if (sscanf(line, "use_profile %u", &profile) == 1) { dp->list[index].profile = (u8) profile; dp->list[index].profile_assigned = 1; } else if (is_delete) ccs_del_string_entry(dp, line, index); else ccs_add_string_entry(dp, line, index); } return; read_policy: for (i = 0; i < dp->list_len; i++) { int j; const struct ccs_path_info **string_ptr = dp->list[i].string_ptr; const int string_count = dp->list[i].string_count; fprintf(fp, "%s\n", ccs_domain_name(dp, i)); if (dp->list[i].profile_assigned) fprintf(fp, "use_profile %u\n", dp->list[i].profile); fprintf(fp, "\n"); for (j = 0; j < string_count; j++) fprintf(fp, "%s\n", string_ptr[j]->name); fprintf(fp, "\n"); } } /* Is the shared buffer for ccs_freadline() and ccs_shprintf() owned? */ static _Bool ccs_buffer_locked = false; /** * ccs_get - Mark the shared buffer for ccs_freadline() and ccs_shprintf() owned. * * Returns nothing. * * This is for avoiding accidental overwriting. * ccs_freadline() and ccs_shprintf() have their own memory buffer. */ void ccs_get(void) { if (ccs_buffer_locked) ccs_out_of_memory(); ccs_buffer_locked = true; } /** * ccs_put - Mark the shared buffer for ccs_freadline() and ccs_shprintf() no longer owned. * * Returns nothing. * * This is for avoiding accidental overwriting. * ccs_freadline() and ccs_shprintf() have their own memory buffer. */ void ccs_put(void) { if (!ccs_buffer_locked) ccs_out_of_memory(); ccs_buffer_locked = false; } /** * ccs_shprintf - sprintf() to dynamically allocated buffer. * * @fmt: The printf()'s format string, followed by parameters. * * Returns pointer to dynamically allocated buffer. * * The caller must not free() the returned pointer. */ char *ccs_shprintf(const char *fmt, ...) { while (true) { static char *policy = NULL; static int max_policy_len = 0; va_list args; int len; va_start(args, fmt); len = vsnprintf(policy, max_policy_len, fmt, args); va_end(args); if (len < 0) ccs_out_of_memory(); if (len >= max_policy_len) { max_policy_len = len + 1; policy = ccs_realloc(policy, max_policy_len); } else return policy; } } /** * ccs_freadline - Read a line from file to dynamically allocated buffer. * * @fp: Pointer to "FILE". * * Returns pointer to dynamically allocated buffer on success, NULL otherwise. * * The caller must not free() the returned pointer. */ char *ccs_freadline(FILE *fp) { static char *policy = NULL; int pos = 0; while (true) { static int max_policy_len = 0; const int c = fgetc(fp); if (c == EOF) return NULL; if (ccs_network_mode && !c) return NULL; if (pos == max_policy_len) { max_policy_len += 4096; policy = ccs_realloc(policy, max_policy_len); } policy[pos++] = (char) c; if (c == '\n') { policy[--pos] = '\0'; break; } } if (!ccs_freadline_raw) ccs_normalize_line(policy); return policy; } /** * ccs_freadline_unpack - Read a line from file to dynamically allocated buffer. * * @fp: Pointer to "FILE". Maybe NULL. * * Returns pointer to dynamically allocated buffer on success, NULL otherwise. * * The caller must not free() the returned pointer. * * The caller must repeat calling this function without changing @fp (or with * changing @fp to NULL) until this function returns NULL, for this function * caches a line if the line is packed. Otherwise, some garbage lines might be * returned to the caller. */ char *ccs_freadline_unpack(FILE *fp) { static char *previous_line = NULL; static char *cached_line = NULL; static int pack_start = 0; static int pack_len = 0; if (cached_line) goto unpack; if (!fp) return NULL; { char *pos; unsigned int offset; unsigned int len; char *line = ccs_freadline(fp); if (!line) return NULL; /* * Skip * <$namespace> * prefix unless this line represents a domainname. */ if (ccs_domain_def(line) && !ccs_correct_domain(line)) { pos = strchr(line, ' '); if (!pos++) pos = line; } else pos = line; /* * Skip * acl_group $group * prefix if this line is a line of exception policy. */ if (sscanf(pos, "acl_group %u", &offset) == 1 && offset < 256) pos = strchr(pos + 11, ' '); else pos = NULL; if (pos++) offset = pos - line; else offset = 0; /* * Only "file " and "network " are subjected to unpacking. */ if (!strncmp(line + offset, "file ", 5)) { char *cp = line + offset + 5; char *cp2 = strchr(cp + 1, ' '); len = cp2 - cp; if (cp2 && memchr(cp, '/', len)) { pack_start = cp - line; goto prepare; } } else if (!strncmp(line + offset, "network ", 8)) { char *cp = strchr(line + offset + 8, ' '); char *cp2 = NULL; if (cp) cp = strchr(cp + 1, ' '); if (cp) cp2 = strchr(cp + 1, ' '); cp++; len = cp2 - cp; if (cp2 && memchr(cp, '/', len)) { pack_start = cp - line; goto prepare; } } return line; prepare: pack_len = len; cached_line = ccs_strdup(line); } unpack: { char *line = NULL; char *pos = cached_line + pack_start; char *cp = memchr(pos, '/', pack_len); unsigned int len = cp - pos; free(previous_line); previous_line = NULL; if (!cp) { previous_line = cached_line; cached_line = NULL; line = previous_line; } else if (pack_len == 1) { /* Ignore trailing empty word. */ free(cached_line); cached_line = NULL; } else { /* Current string is "abc d/e/f ghi". */ line = ccs_strdup(cached_line); previous_line = line; /* Overwrite "abc d/e/f ghi" with "abc d ghi". */ memmove(line + pack_start + len, pos + pack_len, strlen(pos + pack_len) + 1); /* Overwrite "abc d/e/f ghi" with "abc e/f ghi". */ cp++; memmove(pos, cp, strlen(cp) + 1); /* Forget "d/" component. */ pack_len -= len + 1; /* Ignore leading and middle empty word. */ if (!len) goto unpack; } return line; } } /** * ccs_check_remote_host - Check whether the remote host is running with the TOMOYO 2.6 kernel or not. * * Returns true if running with TOMOYO 2.6 kernel, false otherwise. */ _Bool ccs_check_remote_host(void) { int major = 0; int minor = 0; int rev = 0; FILE *fp = ccs_open_read("version"); if (!fp || fscanf(fp, "%u.%u.%u", &major, &minor, &rev) < 2 || major != 2 || minor != 6) { const u32 ip = ntohl(ccs_network_ip); fprintf(stderr, "Can't connect to %u.%u.%u.%u:%u\n", (u8) (ip >> 24), (u8) (ip >> 16), (u8) (ip >> 8), (u8) ip, ntohs(ccs_network_port)); if (fp) fclose(fp); return false; } fclose(fp); return true; } void ccs_mount_securityfs(void) { if (access("/sys/kernel/security/tomoyo/", X_OK)) { if (unshare(CLONE_NEWNS) || mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) || mount("none", "/sys/kernel/security/", "securityfs", 0, NULL)) { if (errno != EBUSY) fprintf(stderr, "Please mount securityfs on " "/sys/kernel/security/ .\n"); } } } tomoyo-tools/usr_sbin/editpolicy_offline.c0000644000000000000000000036733614116520000020106 0ustar rootroot/* * editpolicy_offline.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2012 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" #include "editpolicy.h" #include struct list_head { struct list_head *next; struct list_head *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) #ifndef offsetof #ifdef __compiler_offsetof #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) #else #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif #endif #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define list_entry(ptr, type, member) container_of(ptr, type, member) #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } static inline int list_empty(const struct list_head *head) { return head->next == head; } /* Enumeration definition for internal use. */ /* Index numbers for Access Controls. */ enum ccs_acl_entry_type_index { CCS_TYPE_PATH_ACL, CCS_TYPE_PATH2_ACL, CCS_TYPE_PATH_NUMBER_ACL, CCS_TYPE_MKDEV_ACL, CCS_TYPE_MOUNT_ACL, CCS_TYPE_ENV_ACL, CCS_TYPE_INET_ACL, CCS_TYPE_UNIX_ACL, CCS_TYPE_MANUAL_TASK_ACL, }; /* Index numbers for "struct ccs_condition". */ enum ccs_conditions_index { CCS_TASK_UID, /* current_uid() */ CCS_TASK_EUID, /* current_euid() */ CCS_TASK_SUID, /* current_suid() */ CCS_TASK_FSUID, /* current_fsuid() */ CCS_TASK_GID, /* current_gid() */ CCS_TASK_EGID, /* current_egid() */ CCS_TASK_SGID, /* current_sgid() */ CCS_TASK_FSGID, /* current_fsgid() */ CCS_TASK_PID, /* sys_getpid() */ CCS_TASK_PPID, /* sys_getppid() */ CCS_EXEC_ARGC, /* "struct linux_binprm *"->argc */ CCS_EXEC_ENVC, /* "struct linux_binprm *"->envc */ CCS_TYPE_IS_SOCKET, /* S_IFSOCK */ CCS_TYPE_IS_SYMLINK, /* S_IFLNK */ CCS_TYPE_IS_FILE, /* S_IFREG */ CCS_TYPE_IS_BLOCK_DEV, /* S_IFBLK */ CCS_TYPE_IS_DIRECTORY, /* S_IFDIR */ CCS_TYPE_IS_CHAR_DEV, /* S_IFCHR */ CCS_TYPE_IS_FIFO, /* S_IFIFO */ CCS_MODE_SETUID, /* S_ISUID */ CCS_MODE_SETGID, /* S_ISGID */ CCS_MODE_STICKY, /* S_ISVTX */ CCS_MODE_OWNER_READ, /* S_IRUSR */ CCS_MODE_OWNER_WRITE, /* S_IWUSR */ CCS_MODE_OWNER_EXECUTE, /* S_IXUSR */ CCS_MODE_GROUP_READ, /* S_IRGRP */ CCS_MODE_GROUP_WRITE, /* S_IWGRP */ CCS_MODE_GROUP_EXECUTE, /* S_IXGRP */ CCS_MODE_OTHERS_READ, /* S_IROTH */ CCS_MODE_OTHERS_WRITE, /* S_IWOTH */ CCS_MODE_OTHERS_EXECUTE, /* S_IXOTH */ CCS_EXEC_REALPATH, CCS_SYMLINK_TARGET, CCS_PATH1_UID, CCS_PATH1_GID, CCS_PATH1_INO, CCS_PATH1_MAJOR, CCS_PATH1_MINOR, CCS_PATH1_PERM, CCS_PATH1_TYPE, CCS_PATH1_DEV_MAJOR, CCS_PATH1_DEV_MINOR, CCS_PATH2_UID, CCS_PATH2_GID, CCS_PATH2_INO, CCS_PATH2_MAJOR, CCS_PATH2_MINOR, CCS_PATH2_PERM, CCS_PATH2_TYPE, CCS_PATH2_DEV_MAJOR, CCS_PATH2_DEV_MINOR, CCS_PATH1_PARENT_UID, CCS_PATH1_PARENT_GID, CCS_PATH1_PARENT_INO, CCS_PATH1_PARENT_PERM, CCS_PATH2_PARENT_UID, CCS_PATH2_PARENT_GID, CCS_PATH2_PARENT_INO, CCS_PATH2_PARENT_PERM, CCS_MAX_CONDITION_KEYWORD, CCS_NUMBER_UNION, CCS_NAME_UNION, CCS_ARGV_ENTRY, CCS_ENVP_ENTRY, }; /* Index numbers for domain's attributes. */ enum ccs_domain_info_flags_index { /* Quota warnning flag. */ CCS_DIF_QUOTA_WARNED, /* * This domain was unable to create a new domain at * ccs_find_next_domain() because the name of the domain to be created * was too long or it could not allocate memory. * More than one process continued execve() without domain transition. */ CCS_DIF_TRANSITION_FAILED, CCS_MAX_DOMAIN_INFO_FLAGS }; /* Index numbers for audit type. */ enum ccs_grant_log { /* Follow profile's configuration. */ CCS_GRANTLOG_AUTO, /* Do not generate grant log. */ CCS_GRANTLOG_NO, /* Generate grant_log. */ CCS_GRANTLOG_YES, }; /* Index numbers for group entries. */ enum ccs_group_id { CCS_PATH_GROUP, CCS_NUMBER_GROUP, CCS_ADDRESS_GROUP, CCS_MAX_GROUP }; /* Index numbers for category of functionality. */ enum ccs_mac_category_index { CCS_MAC_CATEGORY_FILE, CCS_MAC_CATEGORY_NETWORK, CCS_MAC_CATEGORY_MISC, CCS_MAX_MAC_CATEGORY_INDEX }; /* Index numbers for functionality. */ enum ccs_mac_index { CCS_MAC_FILE_EXECUTE, CCS_MAC_FILE_OPEN, CCS_MAC_FILE_CREATE, CCS_MAC_FILE_UNLINK, CCS_MAC_FILE_GETATTR, CCS_MAC_FILE_MKDIR, CCS_MAC_FILE_RMDIR, CCS_MAC_FILE_MKFIFO, CCS_MAC_FILE_MKSOCK, CCS_MAC_FILE_TRUNCATE, CCS_MAC_FILE_SYMLINK, CCS_MAC_FILE_MKBLOCK, CCS_MAC_FILE_MKCHAR, CCS_MAC_FILE_LINK, CCS_MAC_FILE_RENAME, CCS_MAC_FILE_CHMOD, CCS_MAC_FILE_CHOWN, CCS_MAC_FILE_CHGRP, CCS_MAC_FILE_IOCTL, CCS_MAC_FILE_CHROOT, CCS_MAC_FILE_MOUNT, CCS_MAC_FILE_UMOUNT, CCS_MAC_FILE_PIVOT_ROOT, CCS_MAC_NETWORK_INET_STREAM_BIND, CCS_MAC_NETWORK_INET_STREAM_LISTEN, CCS_MAC_NETWORK_INET_STREAM_CONNECT, CCS_MAC_NETWORK_INET_STREAM_ACCEPT, CCS_MAC_NETWORK_INET_DGRAM_BIND, CCS_MAC_NETWORK_INET_DGRAM_SEND, CCS_MAC_NETWORK_INET_RAW_BIND, CCS_MAC_NETWORK_INET_RAW_SEND, CCS_MAC_NETWORK_UNIX_STREAM_BIND, CCS_MAC_NETWORK_UNIX_STREAM_LISTEN, CCS_MAC_NETWORK_UNIX_STREAM_CONNECT, CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT, CCS_MAC_NETWORK_UNIX_DGRAM_BIND, CCS_MAC_NETWORK_UNIX_DGRAM_SEND, CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND, CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN, CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT, CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT, CCS_MAC_ENVIRON, CCS_MAX_MAC_INDEX }; /* Index numbers for /sys/kernel/security/tomoyo/stat interface. */ enum ccs_memory_stat_type { CCS_MEMORY_POLICY, CCS_MEMORY_AUDIT, CCS_MEMORY_QUERY, CCS_MAX_MEMORY_STAT }; /* Index numbers for access controls with one pathname and three numbers. */ enum ccs_mkdev_acl_index { CCS_TYPE_MKBLOCK, CCS_TYPE_MKCHAR, CCS_MAX_MKDEV_OPERATION }; /* Index numbers for operation mode. */ enum ccs_mode_value { CCS_CONFIG_DISABLED, CCS_CONFIG_LEARNING, CCS_CONFIG_PERMISSIVE, CCS_CONFIG_ENFORCING, CCS_CONFIG_MAX_MODE, CCS_CONFIG_WANT_REJECT_LOG = 64, CCS_CONFIG_WANT_GRANT_LOG = 128, CCS_CONFIG_USE_DEFAULT = 255, }; /* Index numbers for socket operations. */ enum ccs_network_acl_index { CCS_NETWORK_BIND, /* bind() operation. */ CCS_NETWORK_LISTEN, /* listen() operation. */ CCS_NETWORK_CONNECT, /* connect() operation. */ CCS_NETWORK_ACCEPT, /* accept() operation. */ CCS_NETWORK_SEND, /* send() operation. */ CCS_MAX_NETWORK_OPERATION }; /* Index numbers for access controls with two pathnames. */ enum ccs_path2_acl_index { CCS_TYPE_LINK, CCS_TYPE_RENAME, CCS_TYPE_PIVOT_ROOT, CCS_MAX_PATH2_OPERATION }; /* Index numbers for access controls with one pathname. */ enum ccs_path_acl_index { CCS_TYPE_EXECUTE, CCS_TYPE_READ, CCS_TYPE_WRITE, CCS_TYPE_APPEND, CCS_TYPE_UNLINK, CCS_TYPE_GETATTR, CCS_TYPE_RMDIR, CCS_TYPE_TRUNCATE, CCS_TYPE_SYMLINK, CCS_TYPE_CHROOT, CCS_TYPE_UMOUNT, CCS_MAX_PATH_OPERATION }; /* Index numbers for access controls with one pathname and one number. */ enum ccs_path_number_acl_index { CCS_TYPE_CREATE, CCS_TYPE_MKDIR, CCS_TYPE_MKFIFO, CCS_TYPE_MKSOCK, CCS_TYPE_IOCTL, CCS_TYPE_CHMOD, CCS_TYPE_CHOWN, CCS_TYPE_CHGRP, CCS_MAX_PATH_NUMBER_OPERATION }; /* Index numbers for stat(). */ enum ccs_path_stat_index { /* Do not change this order. */ CCS_PATH1, CCS_PATH1_PARENT, CCS_PATH2, CCS_PATH2_PARENT, CCS_MAX_PATH_STAT }; /* Index numbers for /sys/kernel/security/tomoyo/stat interface. */ enum ccs_policy_stat_type { /* Do not change this order. */ CCS_STAT_POLICY_UPDATES, CCS_STAT_POLICY_LEARNING, /* == CCS_CONFIG_LEARNING */ CCS_STAT_POLICY_PERMISSIVE, /* == CCS_CONFIG_PERMISSIVE */ CCS_STAT_POLICY_ENFORCING, /* == CCS_CONFIG_ENFORCING */ CCS_MAX_POLICY_STAT }; /* Index numbers for profile's PREFERENCE values. */ enum ccs_pref_index { CCS_PREF_MAX_AUDIT_LOG, CCS_PREF_MAX_LEARNING_ENTRY, CCS_MAX_PREF }; /* Index numbers for /sys/kernel/security/tomoyo/ interfaces. */ enum ccs_proc_interface_index { CCS_DOMAINPOLICY, CCS_EXCEPTIONPOLICY, CCS_PROCESS_STATUS, CCS_STAT, CCS_AUDIT, CCS_VERSION, CCS_PROFILE, CCS_QUERY, CCS_MANAGER, }; /* Index numbers for special mount operations. */ enum ccs_special_mount { CCS_MOUNT_BIND, /* mount --bind /source /dest */ CCS_MOUNT_MOVE, /* mount --move /old /new */ CCS_MOUNT_REMOUNT, /* mount -o remount /dir */ CCS_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */ CCS_MOUNT_MAKE_PRIVATE, /* mount --make-private /dir */ CCS_MOUNT_MAKE_SLAVE, /* mount --make-slave /dir */ CCS_MOUNT_MAKE_SHARED, /* mount --make-shared /dir */ CCS_MAX_SPECIAL_MOUNT }; /* Index numbers for type of numeric values. */ enum ccs_value_type { CCS_VALUE_TYPE_INVALID, CCS_VALUE_TYPE_DECIMAL, CCS_VALUE_TYPE_OCTAL, CCS_VALUE_TYPE_HEXADECIMAL, }; /* Constants definition for internal use. */ /* * TOMOYO uses this hash only when appending a string into the string table. * Frequency of appending strings is very low. So we don't need large (e.g. * 64k) hash size. 256 will be sufficient. */ #define CCS_HASH_BITS 8 #define CCS_MAX_HASH (1u << CCS_HASH_BITS) /* * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET. * Therefore, we don't need SOCK_MAX. */ #define CCS_SOCK_MAX 6 /* Size of temporary buffer for execve() operation. */ #define CCS_EXEC_TMPSIZE 4096 /* Profile number is an integer between 0 and 255. */ #define CCS_MAX_PROFILES 256 /* Group number is an integer between 0 and 255. */ #define CCS_MAX_ACL_GROUPS 256 /* Structure definition for internal use. */ struct ccs_policy_namespace; /* Common header for holding ACL entries. */ struct ccs_acl_head { struct list_head list; bool is_deleted; } __attribute__((__packed__)); /* Common header for shared entries. */ struct ccs_shared_acl_head { struct list_head list; unsigned int users; } __attribute__((__packed__)); /* Common header for individual entries. */ struct ccs_acl_info { struct list_head list; struct ccs_condition *cond; /* Maybe NULL. */ bool is_deleted; u8 type; /* One of values in "enum ccs_acl_entry_type_index". */ } __attribute__((__packed__)); /* Structure for holding a word. */ struct ccs_name_union { /* Either @filename or @group is NULL. */ const struct ccs_path_info *filename; struct ccs_group *group; }; /* Structure for holding a number. */ struct ccs_number_union { unsigned long values[2]; struct ccs_group *group; /* Maybe NULL. */ /* One of values in "enum ccs_value_type". */ u8 value_type[2]; }; /* Structure for holding an IP address. */ struct ccs_ipaddr_union { struct in6_addr ip[2]; /* Big endian. */ struct ccs_group *group; /* Pointer to address group. */ bool is_ipv6; /* Valid only if @group == NULL. */ }; /* Structure for "path_group"/"number_group"/"address_group" directive. */ struct ccs_group { struct ccs_shared_acl_head head; struct ccs_policy_namespace *ns; /* Name of group (without leading '@'). */ const struct ccs_path_info *group_name; /* * List of "struct ccs_path_group" or "struct ccs_number_group" or * "struct ccs_address_group". */ struct list_head member_list; }; /* Structure for "path_group" directive. */ struct ccs_path_group { struct ccs_acl_head head; const struct ccs_path_info *member_name; }; /* Structure for "number_group" directive. */ struct ccs_number_group { struct ccs_acl_head head; struct ccs_number_union number; }; /* Structure for "address_group" directive. */ struct ccs_address_group { struct ccs_acl_head head; /* Structure for holding an IP address. */ struct ccs_ipaddr_union address; }; /* Structure for entries which follows "struct ccs_condition". */ struct ccs_condition_element { /* * Left hand operand. A "struct ccs_argv" for CCS_ARGV_ENTRY, a * "struct ccs_envp" for CCS_ENVP_ENTRY is attached to the tail * of the array of this struct. */ u8 left; /* * Right hand operand. A "struct ccs_number_union" for * CCS_NUMBER_UNION, a "struct ccs_name_union" for CCS_NAME_UNION is * attached to the tail of the array of this struct. */ u8 right; /* Equation operator. True if equals or overlaps, false otherwise. */ bool equals; }; /* Structure for optional arguments. */ struct ccs_condition { struct ccs_shared_acl_head head; u32 size; /* Memory size allocated for this entry. */ u16 condc; /* Number of conditions in this struct. */ u16 numbers_count; /* Number of "struct ccs_number_union values". */ u16 names_count; /* Number of "struct ccs_name_union names". */ u16 argc; /* Number of "struct ccs_argv". */ u16 envc; /* Number of "struct ccs_envp". */ u8 grant_log; /* One of values in "enum ccs_grant_log". */ bool exec_transit; /* True if transit is for "file execute". */ const struct ccs_path_info *transit; /* Maybe NULL. */ /* * struct ccs_condition_element condition[condc]; * struct ccs_number_union values[numbers_count]; * struct ccs_name_union names[names_count]; * struct ccs_argv argv[argc]; * struct ccs_envp envp[envc]; */ }; /* * Structure for "reset_domain"/"no_reset_domain"/"initialize_domain"/ * "no_initialize_domain"/"keep_domain"/"no_keep_domain" keyword. */ struct ccs_transition_control { struct ccs_acl_head head; u8 type; /* One of values in "enum ccs_transition_type" */ bool is_last_name; /* True if the domainname is ccs_last_word(). */ const struct ccs_path_info *domainname; /* Maybe NULL */ const struct ccs_path_info *program; /* Maybe NULL */ struct ccs_policy_namespace *ns; }; /* Structure for "aggregator" keyword. */ struct ccs_aggregator { struct ccs_acl_head head; const struct ccs_path_info *original_name; const struct ccs_path_info *aggregated_name; struct ccs_policy_namespace *ns; }; /* Structure for "deny_autobind" keyword. */ struct ccs_reserved { struct ccs_acl_head head; struct ccs_number_union port; struct ccs_policy_namespace *ns; }; /* Structure for policy manager. */ struct ccs_manager { struct ccs_acl_head head; bool is_domain; /* True if manager is a domainname. */ /* A path to program or a domainname. */ const struct ccs_path_info *manager; }; /* Structure for argv[]. */ struct ccs_argv { unsigned long index; const struct ccs_path_info *value; bool is_not; }; /* Structure for envp[]. */ struct ccs_envp { const struct ccs_path_info *name; const struct ccs_path_info *value; bool is_not; }; /* * Structure for * "task manual_domain_transition" directive. */ struct ccs_task_acl { struct ccs_acl_info head; /* type = CCS_TYPE_*_TASK_ACL */ /* Pointer to domainname. */ const struct ccs_path_info *domainname; }; /* * Structure for "file execute", "file read", "file write", "file append", * "file unlink", "file getattr", "file rmdir", "file truncate", * "file symlink", "file chroot" and "file unmount" directive. */ struct ccs_path_acl { struct ccs_acl_info head; /* type = CCS_TYPE_PATH_ACL */ u16 perm; /* Bitmask of values in "enum ccs_path_acl_index". */ struct ccs_name_union name; }; /* * Structure for "file rename", "file link" and "file pivot_root" directive. */ struct ccs_path2_acl { struct ccs_acl_info head; /* type = CCS_TYPE_PATH2_ACL */ u8 perm; /* Bitmask of values in "enum ccs_path2_acl_index". */ struct ccs_name_union name1; struct ccs_name_union name2; }; /* * Structure for "file create", "file mkdir", "file mkfifo", "file mksock", * "file ioctl", "file chmod", "file chown" and "file chgrp" directive. */ struct ccs_path_number_acl { struct ccs_acl_info head; /* type = CCS_TYPE_PATH_NUMBER_ACL */ u8 perm; /* Bitmask of values in "enum ccs_path_number_acl_index". */ struct ccs_name_union name; struct ccs_number_union number; }; /* Structure for "file mkblock" and "file mkchar" directive. */ struct ccs_mkdev_acl { struct ccs_acl_info head; /* type = CCS_TYPE_MKDEV_ACL */ u8 perm; /* Bitmask of values in "enum ccs_mkdev_acl_index". */ struct ccs_name_union name; struct ccs_number_union mode; struct ccs_number_union major; struct ccs_number_union minor; }; /* Structure for "file mount" directive. */ struct ccs_mount_acl { struct ccs_acl_info head; /* type = CCS_TYPE_MOUNT_ACL */ struct ccs_name_union dev_name; struct ccs_name_union dir_name; struct ccs_name_union fs_type; struct ccs_number_union flags; }; /* Structure for "misc env" directive in domain policy. */ struct ccs_env_acl { struct ccs_acl_info head; /* type = CCS_TYPE_ENV_ACL */ const struct ccs_path_info *env; /* environment variable */ }; /* Structure for "network inet" directive. */ struct ccs_inet_acl { struct ccs_acl_info head; /* type = CCS_TYPE_INET_ACL */ u8 protocol; u8 perm; /* Bitmask of values in "enum ccs_network_acl_index" */ struct ccs_ipaddr_union address; struct ccs_number_union port; }; /* Structure for "network unix" directive. */ struct ccs_unix_acl { struct ccs_acl_info head; /* type = CCS_TYPE_UNIX_ACL */ u8 protocol; u8 perm; /* Bitmask of values in "enum ccs_network_acl_index" */ struct ccs_name_union name; }; /* Structure for holding string data. */ struct ccs_name { struct ccs_shared_acl_head head; int size; /* Memory size allocated for this entry. */ struct ccs_path_info entry; }; /* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */ struct ccs_acl_param { char *namespace; char *data; /* Unprocessed data. */ struct list_head *list; /* List to add or remove. */ struct ccs_policy_namespace *ns; /* Namespace to use. */ bool is_delete; /* True if it is a delete request. */ }; /* Structure for /sys/kernel/security/tomoyo/profile interface. */ struct ccs_profile { const struct ccs_path_info *comment; u8 default_config; u8 config[CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX]; unsigned int pref[CCS_MAX_PREF]; }; /* Structure for representing YYYY/MM/DD hh/mm/ss. */ struct ccs_time { u16 year; u8 month; u8 day; u8 hour; u8 min; u8 sec; }; /* Structure for policy namespace. */ struct ccs_policy_namespace { /* Profile table. Memory is allocated as needed. */ struct ccs_profile *profile_ptr[CCS_MAX_PROFILES]; /* The global ACL referred by "use_group" keyword. */ struct list_head acl_group[CCS_MAX_ACL_GROUPS]; /* List for connecting to ccs_namespace_list list. */ struct list_head namespace_list; /* Profile version. Currently only 20150505 is supported. */ unsigned int profile_version; /* Name of this namespace (e.g. "", "" ). */ const char *name; }; struct ccs_domain2_info { struct list_head list; struct list_head acl_info_list; /* Name of this domain. Never NULL. */ const struct ccs_path_info *domainname; /* Group numbers to use. */ bool group[CCS_MAX_ACL_GROUPS]; u8 profile; /* Profile number to use. */ bool is_deleted; /* Delete flag. */ bool flags[CCS_MAX_DOMAIN_INFO_FLAGS]; }; struct ccs_io_buffer { char *data; struct ccs_policy_namespace *ns; struct ccs_domain2_info *domain; struct ccs_domain2_info *print_this_domain_only; bool is_delete; bool print_transition_related_only; bool eof; bool reset; u8 type; u8 acl_group_index; }; /* String table for operation mode. */ static const char * const ccs_mode[CCS_CONFIG_MAX_MODE] = { [CCS_CONFIG_DISABLED] = "disabled", [CCS_CONFIG_LEARNING] = "learning", [CCS_CONFIG_PERMISSIVE] = "permissive", [CCS_CONFIG_ENFORCING] = "enforcing" }; /* String table for /sys/kernel/security/tomoyo/profile interface. */ static const char * const ccs_mac_keywords[CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX] = { /* CONFIG::file group */ [CCS_MAC_FILE_EXECUTE] = "execute", [CCS_MAC_FILE_OPEN] = "open", [CCS_MAC_FILE_CREATE] = "create", [CCS_MAC_FILE_UNLINK] = "unlink", [CCS_MAC_FILE_GETATTR] = "getattr", [CCS_MAC_FILE_MKDIR] = "mkdir", [CCS_MAC_FILE_RMDIR] = "rmdir", [CCS_MAC_FILE_MKFIFO] = "mkfifo", [CCS_MAC_FILE_MKSOCK] = "mksock", [CCS_MAC_FILE_TRUNCATE] = "truncate", [CCS_MAC_FILE_SYMLINK] = "symlink", [CCS_MAC_FILE_MKBLOCK] = "mkblock", [CCS_MAC_FILE_MKCHAR] = "mkchar", [CCS_MAC_FILE_LINK] = "link", [CCS_MAC_FILE_RENAME] = "rename", [CCS_MAC_FILE_CHMOD] = "chmod", [CCS_MAC_FILE_CHOWN] = "chown", [CCS_MAC_FILE_CHGRP] = "chgrp", [CCS_MAC_FILE_IOCTL] = "ioctl", [CCS_MAC_FILE_CHROOT] = "chroot", [CCS_MAC_FILE_MOUNT] = "mount", [CCS_MAC_FILE_UMOUNT] = "unmount", [CCS_MAC_FILE_PIVOT_ROOT] = "pivot_root", /* CONFIG::misc group */ [CCS_MAC_ENVIRON] = "env", /* CONFIG::network group */ [CCS_MAC_NETWORK_INET_STREAM_BIND] = "inet_stream_bind", [CCS_MAC_NETWORK_INET_STREAM_LISTEN] = "inet_stream_listen", [CCS_MAC_NETWORK_INET_STREAM_CONNECT] = "inet_stream_connect", [CCS_MAC_NETWORK_INET_STREAM_ACCEPT] = "inet_stream_accept", [CCS_MAC_NETWORK_INET_DGRAM_BIND] = "inet_dgram_bind", [CCS_MAC_NETWORK_INET_DGRAM_SEND] = "inet_dgram_send", [CCS_MAC_NETWORK_INET_RAW_BIND] = "inet_raw_bind", [CCS_MAC_NETWORK_INET_RAW_SEND] = "inet_raw_send", [CCS_MAC_NETWORK_UNIX_STREAM_BIND] = "unix_stream_bind", [CCS_MAC_NETWORK_UNIX_STREAM_LISTEN] = "unix_stream_listen", [CCS_MAC_NETWORK_UNIX_STREAM_CONNECT] = "unix_stream_connect", [CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT] = "unix_stream_accept", [CCS_MAC_NETWORK_UNIX_DGRAM_BIND] = "unix_dgram_bind", [CCS_MAC_NETWORK_UNIX_DGRAM_SEND] = "unix_dgram_send", [CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind", [CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen", [CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", [CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT] = "unix_seqpacket_accept", /* CONFIG group */ [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_FILE] = "file", [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_NETWORK] = "network", [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_MISC] = "misc", }; /* String table for path operation. */ static const char * const ccs_path_keyword[CCS_MAX_PATH_OPERATION] = { [CCS_TYPE_EXECUTE] = "execute", [CCS_TYPE_READ] = "read", [CCS_TYPE_WRITE] = "write", [CCS_TYPE_APPEND] = "append", [CCS_TYPE_UNLINK] = "unlink", [CCS_TYPE_GETATTR] = "getattr", [CCS_TYPE_RMDIR] = "rmdir", [CCS_TYPE_TRUNCATE] = "truncate", [CCS_TYPE_SYMLINK] = "symlink", [CCS_TYPE_CHROOT] = "chroot", [CCS_TYPE_UMOUNT] = "unmount", }; /* String table for socket's operation. */ static const char * const ccs_socket_keyword[CCS_MAX_NETWORK_OPERATION] = { [CCS_NETWORK_BIND] = "bind", [CCS_NETWORK_LISTEN] = "listen", [CCS_NETWORK_CONNECT] = "connect", [CCS_NETWORK_ACCEPT] = "accept", [CCS_NETWORK_SEND] = "send", }; /* String table for categories. */ static const char * const ccs_category_keywords[CCS_MAX_MAC_CATEGORY_INDEX] = { [CCS_MAC_CATEGORY_FILE] = "file", [CCS_MAC_CATEGORY_NETWORK] = "network", [CCS_MAC_CATEGORY_MISC] = "misc", }; /* String table for conditions. */ static const char * const ccs_condition_keyword[CCS_MAX_CONDITION_KEYWORD] = { [CCS_TASK_UID] = "task.uid", [CCS_TASK_EUID] = "task.euid", [CCS_TASK_SUID] = "task.suid", [CCS_TASK_FSUID] = "task.fsuid", [CCS_TASK_GID] = "task.gid", [CCS_TASK_EGID] = "task.egid", [CCS_TASK_SGID] = "task.sgid", [CCS_TASK_FSGID] = "task.fsgid", [CCS_TASK_PID] = "task.pid", [CCS_TASK_PPID] = "task.ppid", [CCS_EXEC_ARGC] = "exec.argc", [CCS_EXEC_ENVC] = "exec.envc", [CCS_TYPE_IS_SOCKET] = "socket", [CCS_TYPE_IS_SYMLINK] = "symlink", [CCS_TYPE_IS_FILE] = "file", [CCS_TYPE_IS_BLOCK_DEV] = "block", [CCS_TYPE_IS_DIRECTORY] = "directory", [CCS_TYPE_IS_CHAR_DEV] = "char", [CCS_TYPE_IS_FIFO] = "fifo", [CCS_MODE_SETUID] = "setuid", [CCS_MODE_SETGID] = "setgid", [CCS_MODE_STICKY] = "sticky", [CCS_MODE_OWNER_READ] = "owner_read", [CCS_MODE_OWNER_WRITE] = "owner_write", [CCS_MODE_OWNER_EXECUTE] = "owner_execute", [CCS_MODE_GROUP_READ] = "group_read", [CCS_MODE_GROUP_WRITE] = "group_write", [CCS_MODE_GROUP_EXECUTE] = "group_execute", [CCS_MODE_OTHERS_READ] = "others_read", [CCS_MODE_OTHERS_WRITE] = "others_write", [CCS_MODE_OTHERS_EXECUTE] = "others_execute", [CCS_EXEC_REALPATH] = "exec.realpath", [CCS_SYMLINK_TARGET] = "symlink.target", [CCS_PATH1_UID] = "path1.uid", [CCS_PATH1_GID] = "path1.gid", [CCS_PATH1_INO] = "path1.ino", [CCS_PATH1_MAJOR] = "path1.major", [CCS_PATH1_MINOR] = "path1.minor", [CCS_PATH1_PERM] = "path1.perm", [CCS_PATH1_TYPE] = "path1.type", [CCS_PATH1_DEV_MAJOR] = "path1.dev_major", [CCS_PATH1_DEV_MINOR] = "path1.dev_minor", [CCS_PATH2_UID] = "path2.uid", [CCS_PATH2_GID] = "path2.gid", [CCS_PATH2_INO] = "path2.ino", [CCS_PATH2_MAJOR] = "path2.major", [CCS_PATH2_MINOR] = "path2.minor", [CCS_PATH2_PERM] = "path2.perm", [CCS_PATH2_TYPE] = "path2.type", [CCS_PATH2_DEV_MAJOR] = "path2.dev_major", [CCS_PATH2_DEV_MINOR] = "path2.dev_minor", [CCS_PATH1_PARENT_UID] = "path1.parent.uid", [CCS_PATH1_PARENT_GID] = "path1.parent.gid", [CCS_PATH1_PARENT_INO] = "path1.parent.ino", [CCS_PATH1_PARENT_PERM] = "path1.parent.perm", [CCS_PATH2_PARENT_UID] = "path2.parent.uid", [CCS_PATH2_PARENT_GID] = "path2.parent.gid", [CCS_PATH2_PARENT_INO] = "path2.parent.ino", [CCS_PATH2_PARENT_PERM] = "path2.parent.perm", }; /* String table for PREFERENCE keyword. */ static const char * const ccs_pref_keywords[CCS_MAX_PREF] = { [CCS_PREF_MAX_AUDIT_LOG] = "max_audit_log", [CCS_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry", }; /* Mapping table from "enum ccs_path_acl_index" to "enum ccs_mac_index". */ static const u8 ccs_p2mac[CCS_MAX_PATH_OPERATION] = { [CCS_TYPE_EXECUTE] = CCS_MAC_FILE_EXECUTE, [CCS_TYPE_READ] = CCS_MAC_FILE_OPEN, [CCS_TYPE_WRITE] = CCS_MAC_FILE_OPEN, [CCS_TYPE_APPEND] = CCS_MAC_FILE_OPEN, [CCS_TYPE_UNLINK] = CCS_MAC_FILE_UNLINK, [CCS_TYPE_GETATTR] = CCS_MAC_FILE_GETATTR, [CCS_TYPE_RMDIR] = CCS_MAC_FILE_RMDIR, [CCS_TYPE_TRUNCATE] = CCS_MAC_FILE_TRUNCATE, [CCS_TYPE_SYMLINK] = CCS_MAC_FILE_SYMLINK, [CCS_TYPE_CHROOT] = CCS_MAC_FILE_CHROOT, [CCS_TYPE_UMOUNT] = CCS_MAC_FILE_UMOUNT, }; /* Mapping table from "enum ccs_mkdev_acl_index" to "enum ccs_mac_index". */ static const u8 ccs_pnnn2mac[CCS_MAX_MKDEV_OPERATION] = { [CCS_TYPE_MKBLOCK] = CCS_MAC_FILE_MKBLOCK, [CCS_TYPE_MKCHAR] = CCS_MAC_FILE_MKCHAR, }; /* Mapping table from "enum ccs_path2_acl_index" to "enum ccs_mac_index". */ static const u8 ccs_pp2mac[CCS_MAX_PATH2_OPERATION] = { [CCS_TYPE_LINK] = CCS_MAC_FILE_LINK, [CCS_TYPE_RENAME] = CCS_MAC_FILE_RENAME, [CCS_TYPE_PIVOT_ROOT] = CCS_MAC_FILE_PIVOT_ROOT, }; /* * Mapping table from "enum ccs_path_number_acl_index" to "enum ccs_mac_index". */ static const u8 ccs_pn2mac[CCS_MAX_PATH_NUMBER_OPERATION] = { [CCS_TYPE_CREATE] = CCS_MAC_FILE_CREATE, [CCS_TYPE_MKDIR] = CCS_MAC_FILE_MKDIR, [CCS_TYPE_MKFIFO] = CCS_MAC_FILE_MKFIFO, [CCS_TYPE_MKSOCK] = CCS_MAC_FILE_MKSOCK, [CCS_TYPE_IOCTL] = CCS_MAC_FILE_IOCTL, [CCS_TYPE_CHMOD] = CCS_MAC_FILE_CHMOD, [CCS_TYPE_CHOWN] = CCS_MAC_FILE_CHOWN, [CCS_TYPE_CHGRP] = CCS_MAC_FILE_CHGRP, }; /* * Mapping table from "enum ccs_network_acl_index" to "enum ccs_mac_index" for * inet domain socket. */ static const u8 ccs_inet2mac[CCS_SOCK_MAX][CCS_MAX_NETWORK_OPERATION] = { [SOCK_STREAM] = { [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_STREAM_BIND, [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_INET_STREAM_LISTEN, [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_INET_STREAM_CONNECT, [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_INET_STREAM_ACCEPT, }, [SOCK_DGRAM] = { [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_DGRAM_BIND, [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_INET_DGRAM_SEND, }, [SOCK_RAW] = { [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_RAW_BIND, [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_INET_RAW_SEND, }, }; /* * Mapping table from "enum ccs_network_acl_index" to "enum ccs_mac_index" for * unix domain socket. */ static const u8 ccs_unix2mac[CCS_SOCK_MAX][CCS_MAX_NETWORK_OPERATION] = { [SOCK_STREAM] = { [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_STREAM_BIND, [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_UNIX_STREAM_LISTEN, [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_UNIX_STREAM_CONNECT, [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT, }, [SOCK_DGRAM] = { [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_DGRAM_BIND, [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_UNIX_DGRAM_SEND, }, [SOCK_SEQPACKET] = { [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND, [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN, [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT, [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT, }, }; /* String table for socket's protocols. */ static const char * const ccs_proto_keyword[CCS_SOCK_MAX] = { [SOCK_STREAM] = "stream", [SOCK_DGRAM] = "dgram", [SOCK_RAW] = "raw", [SOCK_SEQPACKET] = "seqpacket", [0] = " ", /* Dummy for avoiding NULL pointer dereference. */ [4] = " ", /* Dummy for avoiding NULL pointer dereference. */ }; /* String table for /sys/kernel/security/tomoyo/stat interface. */ static const char * const ccs_memory_headers[CCS_MAX_MEMORY_STAT] = { [CCS_MEMORY_POLICY] = "policy:", [CCS_MEMORY_AUDIT] = "audit log:", [CCS_MEMORY_QUERY] = "query message:", }; /* String table for domain transition control keywords. */ static const char * const ccs_transition_type[MAX_TRANSITION_TYPE] = { [TRANSITION_NO_RESET] = "no_reset_domain ", [TRANSITION_RESET] = "reset_domain ", [TRANSITION_NO_INITIALIZE] = "no_initialize_domain ", [TRANSITION_INITIALIZE] = "initialize_domain ", [TRANSITION_NO_KEEP] = "no_keep_domain ", [TRANSITION_KEEP] = "keep_domain ", }; /* String table for grouping keywords. */ static const char * const ccs_group_name[CCS_MAX_GROUP] = { [CCS_PATH_GROUP] = "path_group ", [CCS_NUMBER_GROUP] = "number_group ", [CCS_ADDRESS_GROUP] = "address_group ", }; /* String table for domain flags. */ static const char * const ccs_dif[CCS_MAX_DOMAIN_INFO_FLAGS] = { [CCS_DIF_QUOTA_WARNED] = "quota_exceeded\n", [CCS_DIF_TRANSITION_FAILED] = "transition_failed\n", }; /* Mapping table from "enum ccs_mac_index" to "enum ccs_mac_category_index". */ static const u8 ccs_index2category[CCS_MAX_MAC_INDEX] = { /* CONFIG::file group */ [CCS_MAC_FILE_EXECUTE] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_OPEN] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_CREATE] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_UNLINK] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_GETATTR] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_MKDIR] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_RMDIR] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_MKFIFO] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_MKSOCK] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_TRUNCATE] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_SYMLINK] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_MKBLOCK] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_MKCHAR] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_LINK] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_RENAME] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_CHMOD] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_CHOWN] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_CHGRP] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_IOCTL] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_CHROOT] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_MOUNT] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_UMOUNT] = CCS_MAC_CATEGORY_FILE, [CCS_MAC_FILE_PIVOT_ROOT] = CCS_MAC_CATEGORY_FILE, /* CONFIG::misc group */ [CCS_MAC_ENVIRON] = CCS_MAC_CATEGORY_MISC, /* CONFIG::network group */ [CCS_MAC_NETWORK_INET_STREAM_BIND] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_INET_STREAM_LISTEN] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_INET_STREAM_CONNECT] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_INET_STREAM_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_INET_DGRAM_BIND] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_INET_DGRAM_SEND] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_INET_RAW_BIND] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_INET_RAW_SEND] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_UNIX_STREAM_BIND] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_UNIX_STREAM_LISTEN] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_UNIX_STREAM_CONNECT] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_UNIX_DGRAM_BIND] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_UNIX_DGRAM_SEND] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = CCS_MAC_CATEGORY_NETWORK, [CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, }; static struct ccs_io_buffer head; static struct ccs_domain2_info ccs_kernel_domain; static struct ccs_policy_namespace ccs_kernel_namespace; static LIST_HEAD(ccs_domain_list); static LIST_HEAD(ccs_manager_list); static LIST_HEAD(ccs_path_group); static LIST_HEAD(ccs_number_group); static LIST_HEAD(ccs_address_group); static LIST_HEAD(ccs_transition_list); static LIST_HEAD(ccs_aggregator_list); static LIST_HEAD(ccs_reserved_list); static LIST_HEAD(ccs_namespace_list); static bool ccs_namespace_enabled; static struct list_head ccs_name_list[CCS_MAX_HASH]; static unsigned int ccs_memory_quota[CCS_MAX_MEMORY_STAT]; /** * ccs_put_condition - Drop reference on "struct ccs_condition". * * @cond: Pointer to "struct ccs_condition". Maybe NULL. * * Returns nothing. */ static inline void ccs_put_condition(struct ccs_condition *cond) { if (cond) cond->head.users--; } /** * ccs_put_group - Drop reference on "struct ccs_group". * * @group: Pointer to "struct ccs_group". Maybe NULL. * * Returns nothing. */ static inline void ccs_put_group(struct ccs_group *group) { if (group) group->head.users--; } /** * ccs_put_name - Drop reference on "struct ccs_name". * * @name: Pointer to "struct ccs_path_info". Maybe NULL. * * Returns nothing. */ static inline void ccs_put_name(const struct ccs_path_info *name) { if (name) container_of(name, struct ccs_name, entry)->head.users--; } /** * ccs_put_name_union - Drop reference on "struct ccs_name_union". * * @ptr: Pointer to "struct ccs_name_union". * * Returns nothing. */ static void ccs_put_name_union(struct ccs_name_union *ptr) { ccs_put_group(ptr->group); ccs_put_name(ptr->filename); } /** * ccs_put_number_union - Drop reference on "struct ccs_number_union". * * @ptr: Pointer to "struct ccs_number_union". * * Returns nothing. */ static void ccs_put_number_union(struct ccs_number_union *ptr) { ccs_put_group(ptr->group); } /** * ccs_del_condition - Delete members in "struct ccs_condition". * * @element: Pointer to "struct list_head". * * Returns nothing. */ static void ccs_del_condition(struct list_head *element) { struct ccs_condition *cond = container_of(element, typeof(*cond), head.list); const u16 condc = cond->condc; const u16 numbers_count = cond->numbers_count; const u16 names_count = cond->names_count; const u16 argc = cond->argc; const u16 envc = cond->envc; unsigned int i; const struct ccs_condition_element *condp = (const struct ccs_condition_element *) (cond + 1); struct ccs_number_union *numbers_p = (struct ccs_number_union *) (condp + condc); struct ccs_name_union *names_p = (struct ccs_name_union *) (numbers_p + numbers_count); const struct ccs_argv *argv = (const struct ccs_argv *) (names_p + names_count); const struct ccs_envp *envp = (const struct ccs_envp *) (argv + argc); for (i = 0; i < numbers_count; i++) ccs_put_number_union(numbers_p++); for (i = 0; i < names_count; i++) ccs_put_name_union(names_p++); for (i = 0; i < argc; argv++, i++) ccs_put_name(argv->value); for (i = 0; i < envc; envp++, i++) { ccs_put_name(envp->name); ccs_put_name(envp->value); } ccs_put_name(cond->transit); } /** * ccs_yesno - Return "yes" or "no". * * @value: Bool value. * * Returns "yes" if @value is not 0, "no" otherwise. */ static const char *ccs_yesno(const unsigned int value) { return value ? "yes" : "no"; } /** * ccs_same_name_union - Check for duplicated "struct ccs_name_union" entry. * * @a: Pointer to "struct ccs_name_union". * @b: Pointer to "struct ccs_name_union". * * Returns true if @a == @b, false otherwise. */ static inline bool ccs_same_name_union(const struct ccs_name_union *a, const struct ccs_name_union *b) { return a->filename == b->filename && a->group == b->group; } /** * ccs_same_number_union - Check for duplicated "struct ccs_number_union" entry. * * @a: Pointer to "struct ccs_number_union". * @b: Pointer to "struct ccs_number_union". * * Returns true if @a == @b, false otherwise. */ static inline bool ccs_same_number_union(const struct ccs_number_union *a, const struct ccs_number_union *b) { return a->values[0] == b->values[0] && a->values[1] == b->values[1] && a->group == b->group && a->value_type[0] == b->value_type[0] && a->value_type[1] == b->value_type[1]; } /** * ccs_same_ipaddr_union - Check for duplicated "struct ccs_ipaddr_union" entry. * * @a: Pointer to "struct ccs_ipaddr_union". * @b: Pointer to "struct ccs_ipaddr_union". * * Returns true if @a == @b, false otherwise. */ static inline bool ccs_same_ipaddr_union(const struct ccs_ipaddr_union *a, const struct ccs_ipaddr_union *b) { return !memcmp(a->ip, b->ip, sizeof(a->ip)) && a->group == b->group && a->is_ipv6 == b->is_ipv6; } /** * ccs_partial_name_hash - Hash name. * * @c: A unsigned long value. * @prevhash: A previous hash value. * * Returns new hash value. * * This function is copied from partial_name_hash() in the kernel source. */ static inline unsigned long ccs_partial_name_hash(unsigned long c, unsigned long prevhash) { return (prevhash + (c << 4) + (c >> 4)) * 11; } /** * ccs_full_name_hash - Hash full name. * * @name: Pointer to "const unsigned char". * @len: Length of @name in byte. * * Returns hash value. * * This function is copied from full_name_hash() in the kernel source. */ static inline unsigned int ccs_full_name_hash(const unsigned char *name, unsigned int len) { unsigned long hash = 0; while (len--) hash = ccs_partial_name_hash(*name++, hash); return (unsigned int) hash; } /** * ccs_get_name - Allocate memory for string data. * * @name: The string to store into the permernent memory. * * Returns pointer to "struct ccs_path_info" on success, abort otherwise. */ static const struct ccs_path_info *ccs_get_name(const char *name) { struct ccs_name *ptr; unsigned int hash; int len; int allocated_len; struct list_head *head; if (!name) name = ""; len = strlen(name) + 1; hash = ccs_full_name_hash((const unsigned char *) name, len - 1); head = &ccs_name_list[hash % CCS_MAX_HASH]; list_for_each_entry(ptr, head, head.list) { if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) continue; ptr->head.users++; goto out; } allocated_len = sizeof(*ptr) + len; ptr = ccs_malloc(allocated_len); ptr->entry.name = ((char *) ptr) + sizeof(*ptr); memmove((char *) ptr->entry.name, name, len); ptr->head.users = 1; ccs_fill_path_info(&ptr->entry); ptr->size = allocated_len; list_add_tail(&ptr->head.list, head); out: return &ptr->entry; } /** * ccs_commit_ok - Allocate memory and check memory quota. * * @data: Data to copy from. * @size: Size in byte. * * Returns pointer to allocated memory on success, abort otherwise. * @data is zero-cleared on success. */ static void *ccs_commit_ok(void *data, const unsigned int size) { void *ptr = ccs_malloc(size); memmove(ptr, data, size); memset(data, 0, size); return ptr; } /** * ccs_permstr - Find permission keywords. * * @string: String representation for permissions in foo/bar/buz format. * @keyword: Keyword to find from @string/ * * Returns true if @keyword was found in @string, false otherwise. * * This function assumes that strncmp(w1, w2, strlen(w1)) != 0 if w1 != w2. */ static bool ccs_permstr(const char *string, const char *keyword) { const char *cp = strstr(string, keyword); if (cp) return cp == string || *(cp - 1) == '/'; return false; } /** * ccs_read_token - Read a word from a line. * * @param: Pointer to "struct ccs_acl_param". * * Returns a word on success, "" otherwise. * * To allow the caller to skip NULL check, this function returns "" rather than * NULL if there is no more words to read. */ static char *ccs_read_token(struct ccs_acl_param *param) { char *pos = param->data; char *del = strchr(pos, ' '); if (del) *del++ = '\0'; else del = pos + strlen(pos); param->data = del; return pos; } /** * ccs_get_domainname - Read a domainname from a line. * * @param: Pointer to "struct ccs_acl_param". * * Returns a domainname on success, NULL otherwise. */ static const struct ccs_path_info *ccs_get_domainname (struct ccs_acl_param *param) { char *start = param->data; char *pos = start; while (*pos) { if (*pos++ != ' ' || *pos++ == '/') continue; pos -= 2; *pos++ = '\0'; break; } param->data = pos; if (ccs_correct_domain(start)) return ccs_get_name(start); return NULL; } /** * ccs_get_group - Allocate memory for "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group". * * @param: Pointer to "struct ccs_acl_param". * @list: List to use. * * Returns pointer to "struct ccs_group" on success, NULL otherwise. */ static struct ccs_group *ccs_get_group(struct ccs_acl_param *param, struct list_head *list) { struct ccs_group e = { }; struct ccs_group *group = NULL; const char *group_name = ccs_read_token(param); bool found = false; if (!ccs_correct_word(group_name)) return NULL; e.ns = param->ns; e.group_name = ccs_get_name(group_name); list_for_each_entry(group, list, head.list) { if (e.ns != group->ns || e.group_name != group->group_name) continue; group->head.users++; found = true; break; } if (!found) { struct ccs_group *entry = ccs_commit_ok(&e, sizeof(e)); INIT_LIST_HEAD(&entry->member_list); entry->head.users = 1; list_add_tail(&entry->head.list, list); group = entry; found = true; } ccs_put_name(e.group_name); return found ? group : NULL; } /** * ccs_parse_ulong - Parse an "unsigned long" value. * * @result: Pointer to "unsigned long". * @str: Pointer to string to parse. * * Returns one of values in "enum ccs_value_type". * * The @src is updated to point the first character after the value * on success. */ static u8 ccs_parse_ulong(unsigned long *result, char **str) { const char *cp = *str; char *ep; int base = 10; if (*cp == '0') { char c = *(cp + 1); if (c == 'x' || c == 'X') { base = 16; cp += 2; } else if (c >= '0' && c <= '7') { base = 8; cp++; } } *result = strtoul(cp, &ep, base); if (cp == ep) return CCS_VALUE_TYPE_INVALID; *str = ep; switch (base) { case 16: return CCS_VALUE_TYPE_HEXADECIMAL; case 8: return CCS_VALUE_TYPE_OCTAL; default: return CCS_VALUE_TYPE_DECIMAL; } } /** * ccs_parse_name_union - Parse a ccs_name_union. * * @param: Pointer to "struct ccs_acl_param". * @ptr: Pointer to "struct ccs_name_union". * * Returns true on success, false otherwise. */ static bool ccs_parse_name_union(struct ccs_acl_param *param, struct ccs_name_union *ptr) { char *filename; if (param->data[0] == '@') { param->data++; ptr->group = ccs_get_group(param, &ccs_path_group); return ptr->group != NULL; } filename = ccs_read_token(param); if (!ccs_correct_word(filename)) return false; ptr->filename = ccs_get_name(filename); return true; } /** * ccs_parse_number_union - Parse a ccs_number_union. * * @param: Pointer to "struct ccs_acl_param". * @ptr: Pointer to "struct ccs_number_union". * * Returns true on success, false otherwise. */ static bool ccs_parse_number_union(struct ccs_acl_param *param, struct ccs_number_union *ptr) { char *data; u8 type; unsigned long v; memset(ptr, 0, sizeof(*ptr)); if (param->data[0] == '@') { param->data++; ptr->group = ccs_get_group(param, &ccs_number_group); return ptr->group != NULL; } data = ccs_read_token(param); type = ccs_parse_ulong(&v, &data); if (type == CCS_VALUE_TYPE_INVALID) return false; ptr->values[0] = v; ptr->value_type[0] = type; if (!*data) { ptr->values[1] = v; ptr->value_type[1] = type; return true; } if (*data++ != '-') return false; type = ccs_parse_ulong(&v, &data); if (type == CCS_VALUE_TYPE_INVALID || *data || ptr->values[0] > v) return false; ptr->values[1] = v; ptr->value_type[1] = type; return true; } /** * ccs_find_domain2 - Find a domain by the given name. * * @domainname: The domainname to find. * * Returns pointer to "struct ccs_domain2_info" if found, NULL otherwise. */ static struct ccs_domain2_info *ccs_find_domain2(const char *domainname) { struct ccs_domain2_info *domain; struct ccs_path_info name; name.name = domainname; ccs_fill_path_info(&name); list_for_each_entry(domain, &ccs_domain_list, list) { if (!domain->is_deleted && !ccs_pathcmp(&name, domain->domainname)) return domain; } return NULL; } static int client_fd = EOF; static void cprintf(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); /** * cprintf - printf() over socket. * * @fmt: The printf()'s format string, followed by parameters. * * Returns nothing. */ static void cprintf(const char *fmt, ...) { va_list args; static char *buffer = NULL; static unsigned int buffer_len = 0; static unsigned int buffer_pos = 0; int len; if (head.reset) { head.reset = false; buffer_pos = 0; } while (1) { va_start(args, fmt); len = vsnprintf(buffer + buffer_pos, buffer_len - buffer_pos, fmt, args); va_end(args); if (len < 0) _exit(1); if (buffer_pos + len < buffer_len) { buffer_pos += len; break; } buffer_len = buffer_pos + len + 4096; buffer = ccs_realloc(buffer, buffer_len); } if (len && buffer_pos < 1048576) return; /* * Reader might close connection without reading until EOF. * In that case, we should not call _exit() because offline daemon does * not call fork() for each accept()ed socket connection. */ if (write(client_fd, buffer, buffer_pos) != buffer_pos) { close(client_fd); client_fd = EOF; } buffer_pos = 0; } /** * ccs_update_policy - Update an entry for exception policy. * * @new_entry: Pointer to "struct ccs_acl_info". * @size: Size of @new_entry in bytes. * @param: Pointer to "struct ccs_acl_param". * @check_duplicate: Callback function to find duplicated entry. * * Returns 0 on success, negative value otherwise. */ static int ccs_update_policy(struct ccs_acl_head *new_entry, const int size, struct ccs_acl_param *param, bool (*check_duplicate) (const struct ccs_acl_head *, const struct ccs_acl_head *)) { int error = param->is_delete ? -ENOENT : -ENOMEM; struct ccs_acl_head *entry; struct list_head *list = param->list; list_for_each_entry(entry, list, list) { if (!check_duplicate(entry, new_entry)) continue; entry->is_deleted = param->is_delete; error = 0; break; } if (error && !param->is_delete) { entry = ccs_commit_ok(new_entry, size); list_add_tail(&entry->list, list); error = 0; } return error; } /* List of "struct ccs_condition". */ static LIST_HEAD(ccs_condition_list); /** * ccs_get_dqword - ccs_get_name() for a quoted string. * * @start: String to save. * * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. */ static const struct ccs_path_info *ccs_get_dqword(char *start) { char *cp = start + strlen(start) - 1; if (cp == start || *start++ != '"' || *cp != '"') return NULL; *cp = '\0'; if (*start && !ccs_correct_word(start)) return NULL; return ccs_get_name(start); } /** * ccs_parse_name_union_quoted - Parse a quoted word. * * @param: Pointer to "struct ccs_acl_param". * @ptr: Pointer to "struct ccs_name_union". * * Returns true on success, false otherwise. */ static bool ccs_parse_name_union_quoted(struct ccs_acl_param *param, struct ccs_name_union *ptr) { char *filename = param->data; if (*filename == '@') return ccs_parse_name_union(param, ptr); ptr->filename = ccs_get_dqword(filename); return ptr->filename != NULL; } /** * ccs_parse_argv - Parse an argv[] condition part. * * @left: Lefthand value. * @right: Righthand value. * @argv: Pointer to "struct ccs_argv". * * Returns true on success, false otherwise. */ static bool ccs_parse_argv(char *left, char *right, struct ccs_argv *argv) { if (ccs_parse_ulong(&argv->index, &left) != CCS_VALUE_TYPE_DECIMAL || *left++ != ']' || *left) return false; argv->value = ccs_get_dqword(right); return argv->value != NULL; } /** * ccs_parse_envp - Parse an envp[] condition part. * * @left: Lefthand value. * @right: Righthand value. * @envp: Pointer to "struct ccs_envp". * * Returns true on success, false otherwise. */ static bool ccs_parse_envp(char *left, char *right, struct ccs_envp *envp) { const struct ccs_path_info *name; const struct ccs_path_info *value; char *cp = left + strlen(left) - 1; if (*cp-- != ']' || *cp != '"') goto out; *cp = '\0'; if (!ccs_correct_word(left)) goto out; name = ccs_get_name(left); if (!strcmp(right, "NULL")) { value = NULL; } else { value = ccs_get_dqword(right); if (!value) { ccs_put_name(name); goto out; } } envp->name = name; envp->value = value; return true; out: return false; } /** * ccs_same_condition - Check for duplicated "struct ccs_condition" entry. * * @a: Pointer to "struct ccs_condition". * @b: Pointer to "struct ccs_condition". * * Returns true if @a == @b, false otherwise. */ static inline bool ccs_same_condition(const struct ccs_condition *a, const struct ccs_condition *b) { return a->size == b->size && a->condc == b->condc && a->numbers_count == b->numbers_count && a->names_count == b->names_count && a->argc == b->argc && a->envc == b->envc && a->grant_log == b->grant_log && a->exec_transit == b->exec_transit && a->transit == b->transit && !memcmp(a + 1, b + 1, a->size - sizeof(*a)); } /** * ccs_condition_type - Get condition type. * * @word: Keyword string. * * Returns one of values in "enum ccs_conditions_index" on success, * CCS_MAX_CONDITION_KEYWORD otherwise. */ static u8 ccs_condition_type(const char *word) { u8 i; for (i = 0; i < CCS_MAX_CONDITION_KEYWORD; i++) { if (!strcmp(word, ccs_condition_keyword[i])) break; } return i; } /* Define this to enable debug mode. */ /* #define DEBUG_CONDITION */ #ifdef DEBUG_CONDITION #define dprintk printk #else #define dprintk(...) do { } while (0) #endif /** * ccs_commit_condition - Commit "struct ccs_condition". * * @entry: Pointer to "struct ccs_condition". * * Returns pointer to "struct ccs_condition" on success, NULL otherwise. * * This function merges duplicated entries. This function returns NULL if * @entry is not duplicated but memory quota for policy has exceeded. */ static struct ccs_condition *ccs_commit_condition(struct ccs_condition *entry) { struct ccs_condition *ptr; bool found = false; list_for_each_entry(ptr, &ccs_condition_list, head.list) { if (!ccs_same_condition(ptr, entry)) continue; /* Same entry found. Share this entry. */ ptr->head.users++; found = true; break; } if (!found) { if (entry) { entry->head.users = 1; list_add(&entry->head.list, &ccs_condition_list); } else { found = true; ptr = NULL; } } if (found) { ccs_del_condition(&entry->head.list); free(entry); entry = ptr; } return entry; } /** * ccs_get_transit_preference - Parse domain transition preference for execve(). * * @param: Pointer to "struct ccs_acl_param". * @e: Pointer to "struct ccs_condition". * * Returns the condition string part. */ static char *ccs_get_transit_preference(struct ccs_acl_param *param, struct ccs_condition *e) { char * const pos = param->data; bool flag; if (*pos == '<') { e->transit = ccs_get_domainname(param); goto done; } { char *cp = strchr(pos, ' '); if (cp) *cp = '\0'; flag = ccs_correct_path(pos) || !strcmp(pos, "keep") || !strcmp(pos, "initialize") || !strcmp(pos, "reset") || !strcmp(pos, "child") || !strcmp(pos, "parent"); if (cp) *cp = ' '; } if (!flag) return pos; e->transit = ccs_get_name(ccs_read_token(param)); done: if (e->transit) { e->exec_transit = true; return param->data; } /* * Return a bad read-only condition string that will let * ccs_get_condition() return NULL. */ return "/"; } /** * ccs_get_condition - Parse condition part. * * @param: Pointer to "struct ccs_acl_param". * * Returns pointer to "struct ccs_condition" on success, NULL otherwise. */ static struct ccs_condition *ccs_get_condition(struct ccs_acl_param *param) { struct ccs_condition *entry = NULL; struct ccs_condition_element *condp = NULL; struct ccs_number_union *numbers_p = NULL; struct ccs_name_union *names_p = NULL; struct ccs_argv *argv = NULL; struct ccs_envp *envp = NULL; struct ccs_condition e = { }; char * const start_of_string = ccs_get_transit_preference(param, &e); char * const end_of_string = start_of_string + strlen(start_of_string); char *pos; rerun: pos = start_of_string; while (1) { u8 left = -1; u8 right = -1; char *left_word = pos; char *cp; char *right_word; bool is_not; if (!*left_word) break; /* * Since left-hand condition does not allow use of "path_group" * or "number_group" and environment variable's names do not * accept '=', it is guaranteed that the original line consists * of one or more repetition of $left$operator$right blocks * where "$left is free from '=' and ' '" and "$operator is * either '=' or '!='" and "$right is free from ' '". * Therefore, we can reconstruct the original line at the end * of dry run even if we overwrite $operator with '\0'. */ cp = strchr(pos, ' '); if (cp) { *cp = '\0'; /* Will restore later. */ pos = cp + 1; } else { pos = ""; } right_word = strchr(left_word, '='); if (!right_word || right_word == left_word) goto out; is_not = *(right_word - 1) == '!'; if (is_not) *(right_word++ - 1) = '\0'; /* Will restore later. */ else if (*(right_word + 1) != '=') *right_word++ = '\0'; /* Will restore later. */ else goto out; dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word, is_not ? "!" : "", right_word); if (!strcmp(left_word, "grant_log")) { if (entry) { if (is_not || entry->grant_log != CCS_GRANTLOG_AUTO) goto out; else if (!strcmp(right_word, "yes")) entry->grant_log = CCS_GRANTLOG_YES; else if (!strcmp(right_word, "no")) entry->grant_log = CCS_GRANTLOG_NO; else goto out; } continue; } if (!strcmp(left_word, "auto_domain_transition")) { if (entry) { if (is_not || entry->transit) goto out; entry->transit = ccs_get_dqword(right_word); if (!entry->transit || (entry->transit->name[0] != '/' && !ccs_domain_def(entry->transit->name))) goto out; } continue; } if (!strncmp(left_word, "exec.argv[", 10)) { if (!argv) { e.argc++; e.condc++; } else { e.argc--; e.condc--; left = CCS_ARGV_ENTRY; argv->is_not = is_not; if (!ccs_parse_argv(left_word + 10, right_word, argv++)) goto out; } goto store_value; } if (!strncmp(left_word, "exec.envp[\"", 11)) { if (!envp) { e.envc++; e.condc++; } else { e.envc--; e.condc--; left = CCS_ENVP_ENTRY; envp->is_not = is_not; if (!ccs_parse_envp(left_word + 11, right_word, envp++)) goto out; } goto store_value; } left = ccs_condition_type(left_word); dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__, left_word, left); if (left == CCS_MAX_CONDITION_KEYWORD) { if (!numbers_p) { e.numbers_count++; } else { e.numbers_count--; left = CCS_NUMBER_UNION; param->data = left_word; if (*left_word == '@' || !ccs_parse_number_union(param, numbers_p++)) goto out; } } if (!condp) e.condc++; else e.condc--; if (left == CCS_EXEC_REALPATH || left == CCS_SYMLINK_TARGET) { if (!names_p) { e.names_count++; } else { e.names_count--; right = CCS_NAME_UNION; param->data = right_word; if (!ccs_parse_name_union_quoted(param, names_p++)) goto out; } goto store_value; } right = ccs_condition_type(right_word); if (right == CCS_MAX_CONDITION_KEYWORD) { if (!numbers_p) { e.numbers_count++; } else { e.numbers_count--; right = CCS_NUMBER_UNION; param->data = right_word; if (!ccs_parse_number_union(param, numbers_p++)) goto out; } } store_value: if (!condp) { dprintk(KERN_WARNING "%u: dry_run left=%u right=%u " "match=%u\n", __LINE__, left, right, !is_not); continue; } condp->left = left; condp->right = right; condp->equals = !is_not; dprintk(KERN_WARNING "%u: left=%u right=%u match=%u\n", __LINE__, condp->left, condp->right, condp->equals); condp++; } dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u ac=%u ec=%u\n", __LINE__, e.condc, e.numbers_count, e.names_count, e.argc, e.envc); if (entry) return ccs_commit_condition(entry); e.size = sizeof(*entry) + e.condc * sizeof(struct ccs_condition_element) + e.numbers_count * sizeof(struct ccs_number_union) + e.names_count * sizeof(struct ccs_name_union) + e.argc * sizeof(struct ccs_argv) + e.envc * sizeof(struct ccs_envp); entry = ccs_malloc(e.size); *entry = e; e.transit = NULL; condp = (struct ccs_condition_element *) (entry + 1); numbers_p = (struct ccs_number_union *) (condp + e.condc); names_p = (struct ccs_name_union *) (numbers_p + e.numbers_count); argv = (struct ccs_argv *) (names_p + e.names_count); envp = (struct ccs_envp *) (argv + e.argc); { bool flag = false; for (pos = start_of_string; pos < end_of_string; pos++) { if (*pos) continue; if (flag) /* Restore " ". */ *pos = ' '; else if (*(pos + 1) == '=') /* Restore "!=". */ *pos = '!'; else /* Restore "=". */ *pos = '='; flag = !flag; } } goto rerun; out: dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); if (entry) { ccs_del_condition(&entry->head.list); free(entry); } ccs_put_name(e.transit); return NULL; } /** * ccs_same_acl_head - Check for duplicated "struct ccs_acl_info" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * * Returns true if @a == @b, false otherwise. */ static inline bool ccs_same_acl_head(const struct ccs_acl_info *a, const struct ccs_acl_info *b) { return a->type == b->type && a->cond == b->cond; } /** * ccs_update_domain - Update an entry for domain policy. * * @new_entry: Pointer to "struct ccs_acl_info". * @size: Size of @new_entry in bytes. * @param: Pointer to "struct ccs_acl_param". * @check_duplicate: Callback function to find duplicated entry. * @merge_duplicate: Callback function to merge duplicated entry. Maybe NULL. * * Returns 0 on success, negative value otherwise. */ static int ccs_update_domain(struct ccs_acl_info *new_entry, const int size, struct ccs_acl_param *param, bool (*check_duplicate) (const struct ccs_acl_info *, const struct ccs_acl_info *), bool (*merge_duplicate) (struct ccs_acl_info *, struct ccs_acl_info *, const bool)) { const bool is_delete = param->is_delete; int error = is_delete ? -ENOENT : -ENOMEM; struct ccs_acl_info *entry; struct list_head * const list = param->list; if (param->data[0]) { new_entry->cond = ccs_get_condition(param); if (!new_entry->cond) return -EINVAL; /* * Domain transition preference is allowed for only * "file execute" entries. */ if (new_entry->cond->exec_transit && !(new_entry->type == CCS_TYPE_PATH_ACL && container_of(new_entry, struct ccs_path_acl, head)->perm == 1 << CCS_TYPE_EXECUTE)) goto out; } list_for_each_entry(entry, list, list) { if (!ccs_same_acl_head(entry, new_entry) || !check_duplicate(entry, new_entry)) continue; if (merge_duplicate) entry->is_deleted = merge_duplicate(entry, new_entry, is_delete); else entry->is_deleted = is_delete; error = 0; break; } if (error && !is_delete) { entry = ccs_commit_ok(new_entry, size); list_add_tail(&entry->list, list); error = 0; } out: ccs_put_condition(new_entry->cond); return error; } /** * ccs_same_transition_control - Check for duplicated "struct ccs_transition_control" entry. * * @a: Pointer to "struct ccs_acl_head". * @b: Pointer to "struct ccs_acl_head". * * Returns true if @a == @b, false otherwise. */ static bool ccs_same_transition_control(const struct ccs_acl_head *a, const struct ccs_acl_head *b) { const struct ccs_transition_control *p1 = container_of(a, typeof(*p1), head); const struct ccs_transition_control *p2 = container_of(b, typeof(*p2), head); return p1->type == p2->type && p1->is_last_name == p2->is_last_name && p1->domainname == p2->domainname && p1->program == p2->program && p1->ns == p2->ns; } /** * ccs_write_transition_control - Write "struct ccs_transition_control" list. * * @param: Pointer to "struct ccs_acl_param". * @type: Type of this entry. * * Returns 0 on success, negative value otherwise. */ static int ccs_write_transition_control(struct ccs_acl_param *param, const u8 type) { struct ccs_transition_control e = { .type = type, .ns = param->ns }; int error = param->is_delete ? -ENOENT : -ENOMEM; char *program = param->data; char *domainname = strstr(program, " from "); if (domainname) { *domainname = '\0'; domainname += 6; } else if (type == TRANSITION_NO_KEEP || type == TRANSITION_KEEP) { domainname = program; program = NULL; } if (program && strcmp(program, "any")) { if (!ccs_correct_path(program)) return -EINVAL; e.program = ccs_get_name(program); } if (domainname && strcmp(domainname, "any")) { if (!ccs_correct_domain(domainname)) { if (!ccs_correct_path(domainname)) goto out; e.is_last_name = true; } e.domainname = ccs_get_name(domainname); } param->list = &ccs_transition_list; error = ccs_update_policy(&e.head, sizeof(e), param, ccs_same_transition_control); out: ccs_put_name(e.domainname); ccs_put_name(e.program); return error; } /** * ccs_same_aggregator - Check for duplicated "struct ccs_aggregator" entry. * * @a: Pointer to "struct ccs_acl_head". * @b: Pointer to "struct ccs_acl_head". * * Returns true if @a == @b, false otherwise. */ static bool ccs_same_aggregator(const struct ccs_acl_head *a, const struct ccs_acl_head *b) { const struct ccs_aggregator *p1 = container_of(a, typeof(*p1), head); const struct ccs_aggregator *p2 = container_of(b, typeof(*p2), head); return p1->original_name == p2->original_name && p1->aggregated_name == p2->aggregated_name && p1->ns == p2->ns; } /** * ccs_write_aggregator - Write "struct ccs_aggregator" list. * * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_write_aggregator(struct ccs_acl_param *param) { struct ccs_aggregator e = { .ns = param->ns }; int error = param->is_delete ? -ENOENT : -ENOMEM; const char *original_name = ccs_read_token(param); const char *aggregated_name = ccs_read_token(param); if (!ccs_correct_word(original_name) || !ccs_correct_path(aggregated_name)) return -EINVAL; e.original_name = ccs_get_name(original_name); e.aggregated_name = ccs_get_name(aggregated_name); if (e.aggregated_name->is_patterned) /* No patterns allowed. */ goto out; param->list = &ccs_aggregator_list; error = ccs_update_policy(&e.head, sizeof(e), param, ccs_same_aggregator); out: ccs_put_name(e.original_name); ccs_put_name(e.aggregated_name); return error; } /* Domain create handler. */ /** * ccs_find_namespace - Find specified namespace. * * @name: Name of namespace to find. * @len: Length of @name. * * Returns pointer to "struct ccs_policy_namespace" if found, NULL otherwise. */ static struct ccs_policy_namespace *ccs_find_namespace(const char *name, const unsigned int len) { struct ccs_policy_namespace *ns; list_for_each_entry(ns, &ccs_namespace_list, namespace_list) { if (strncmp(name, ns->name, len) || (name[len] && name[len] != ' ')) continue; return ns; } return NULL; } /** * ccs_assign_namespace - Create a new namespace. * * @domainname: Name of namespace to create. * * Returns pointer to "struct ccs_policy_namespace" on success, NULL otherwise. */ static struct ccs_policy_namespace *ccs_assign_namespace(const char *domainname) { struct ccs_policy_namespace *ptr; struct ccs_policy_namespace *entry; const char *cp = domainname; unsigned int len = 0; while (*cp && *cp++ != ' ') len++; ptr = ccs_find_namespace(domainname, len); if (ptr) return ptr; if (len >= CCS_EXEC_TMPSIZE - 10 || !ccs_domain_def(domainname)) return NULL; entry = ccs_malloc(sizeof(*entry) + len + 1); { char *name = (char *) (entry + 1); memmove(name, domainname, len); name[len] = '\0'; entry->name = name; } entry->profile_version = 20150505; for (len = 0; len < CCS_MAX_ACL_GROUPS; len++) INIT_LIST_HEAD(&entry->acl_group[len]); ccs_namespace_enabled = !list_empty(&ccs_namespace_list); list_add_tail(&entry->namespace_list, &ccs_namespace_list); return entry; } /** * ccs_assign_domain2 - Create a domain or a namespace. * * @domainname: The name of domain. * * Returns pointer to "struct ccs_domain2_info" on success, NULL otherwise. */ static struct ccs_domain2_info *ccs_assign_domain2(const char *domainname) { struct ccs_domain2_info e = { }; struct ccs_domain2_info *entry = ccs_find_domain2(domainname); if (entry) return entry; /* Requested domain does not exist. */ /* Don't create requested domain if domainname is invalid. */ if (strlen(domainname) >= CCS_EXEC_TMPSIZE - 10 || !ccs_correct_domain(domainname)) return NULL; /* Create namespace if requested namespace does not exist. */ if (!ccs_assign_namespace(domainname)) return NULL; e.domainname = ccs_get_name(domainname); entry = ccs_commit_ok(&e, sizeof(e)); INIT_LIST_HEAD(&entry->acl_info_list); list_add_tail(&entry->list, &ccs_domain_list); ccs_put_name(e.domainname); return entry; } /** * ccs_same_path_acl - Check for duplicated "struct ccs_path_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * * Returns true if @a == @b except permission bits, false otherwise. */ static bool ccs_same_path_acl(const struct ccs_acl_info *a, const struct ccs_acl_info *b) { const struct ccs_path_acl *p1 = container_of(a, typeof(*p1), head); const struct ccs_path_acl *p2 = container_of(b, typeof(*p2), head); return ccs_same_name_union(&p1->name, &p2->name); } /** * ccs_merge_path_acl - Merge duplicated "struct ccs_path_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * @is_delete: True for @a &= ~@b, false for @a |= @b. * * Returns true if @a is empty, false otherwise. */ static bool ccs_merge_path_acl(struct ccs_acl_info *a, struct ccs_acl_info *b, const bool is_delete) { u16 * const a_perm = &container_of(a, struct ccs_path_acl, head)->perm; u16 perm = *a_perm; const u16 b_perm = container_of(b, struct ccs_path_acl, head)->perm; if (is_delete) perm &= ~b_perm; else perm |= b_perm; *a_perm = perm; return !perm; } /** * ccs_update_path_acl - Update "struct ccs_path_acl" list. * * @perm: Permission. * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_update_path_acl(const u16 perm, struct ccs_acl_param *param) { struct ccs_path_acl e = { .head.type = CCS_TYPE_PATH_ACL, .perm = perm }; int error; if (!ccs_parse_name_union(param, &e.name)) error = -EINVAL; else error = ccs_update_domain(&e.head, sizeof(e), param, ccs_same_path_acl, ccs_merge_path_acl); ccs_put_name_union(&e.name); return error; } /** * ccs_same_mkdev_acl - Check for duplicated "struct ccs_mkdev_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * * Returns true if @a == @b except permission bits, false otherwise. */ static bool ccs_same_mkdev_acl(const struct ccs_acl_info *a, const struct ccs_acl_info *b) { const struct ccs_mkdev_acl *p1 = container_of(a, typeof(*p1), head); const struct ccs_mkdev_acl *p2 = container_of(b, typeof(*p2), head); return ccs_same_name_union(&p1->name, &p2->name) && ccs_same_number_union(&p1->mode, &p2->mode) && ccs_same_number_union(&p1->major, &p2->major) && ccs_same_number_union(&p1->minor, &p2->minor); } /** * ccs_merge_mkdev_acl - Merge duplicated "struct ccs_mkdev_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * @is_delete: True for @a &= ~@b, false for @a |= @b. * * Returns true if @a is empty, false otherwise. */ static bool ccs_merge_mkdev_acl(struct ccs_acl_info *a, struct ccs_acl_info *b, const bool is_delete) { u8 *const a_perm = &container_of(a, struct ccs_mkdev_acl, head)->perm; u8 perm = *a_perm; const u8 b_perm = container_of(b, struct ccs_mkdev_acl, head)->perm; if (is_delete) perm &= ~b_perm; else perm |= b_perm; *a_perm = perm; return !perm; } /** * ccs_update_mkdev_acl - Update "struct ccs_mkdev_acl" list. * * @perm: Permission. * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_update_mkdev_acl(const u8 perm, struct ccs_acl_param *param) { struct ccs_mkdev_acl e = { .head.type = CCS_TYPE_MKDEV_ACL, .perm = perm }; int error; if (!ccs_parse_name_union(param, &e.name) || !ccs_parse_number_union(param, &e.mode) || !ccs_parse_number_union(param, &e.major) || !ccs_parse_number_union(param, &e.minor)) error = -EINVAL; else error = ccs_update_domain(&e.head, sizeof(e), param, ccs_same_mkdev_acl, ccs_merge_mkdev_acl); ccs_put_name_union(&e.name); ccs_put_number_union(&e.mode); ccs_put_number_union(&e.major); ccs_put_number_union(&e.minor); return error; } /** * ccs_same_path2_acl - Check for duplicated "struct ccs_path2_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * * Returns true if @a == @b except permission bits, false otherwise. */ static bool ccs_same_path2_acl(const struct ccs_acl_info *a, const struct ccs_acl_info *b) { const struct ccs_path2_acl *p1 = container_of(a, typeof(*p1), head); const struct ccs_path2_acl *p2 = container_of(b, typeof(*p2), head); return ccs_same_name_union(&p1->name1, &p2->name1) && ccs_same_name_union(&p1->name2, &p2->name2); } /** * ccs_merge_path2_acl - Merge duplicated "struct ccs_path2_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * @is_delete: True for @a &= ~@b, false for @a |= @b. * * Returns true if @a is empty, false otherwise. */ static bool ccs_merge_path2_acl(struct ccs_acl_info *a, struct ccs_acl_info *b, const bool is_delete) { u8 * const a_perm = &container_of(a, struct ccs_path2_acl, head)->perm; u8 perm = *a_perm; const u8 b_perm = container_of(b, struct ccs_path2_acl, head)->perm; if (is_delete) perm &= ~b_perm; else perm |= b_perm; *a_perm = perm; return !perm; } /** * ccs_update_path2_acl - Update "struct ccs_path2_acl" list. * * @perm: Permission. * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_update_path2_acl(const u8 perm, struct ccs_acl_param *param) { struct ccs_path2_acl e = { .head.type = CCS_TYPE_PATH2_ACL, .perm = perm }; int error; if (!ccs_parse_name_union(param, &e.name1) || !ccs_parse_name_union(param, &e.name2)) error = -EINVAL; else error = ccs_update_domain(&e.head, sizeof(e), param, ccs_same_path2_acl, ccs_merge_path2_acl); ccs_put_name_union(&e.name1); ccs_put_name_union(&e.name2); return error; } /** * ccs_same_path_number_acl - Check for duplicated "struct ccs_path_number_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * * Returns true if @a == @b except permission bits, false otherwise. */ static bool ccs_same_path_number_acl(const struct ccs_acl_info *a, const struct ccs_acl_info *b) { const struct ccs_path_number_acl *p1 = container_of(a, typeof(*p1), head); const struct ccs_path_number_acl *p2 = container_of(b, typeof(*p2), head); return ccs_same_name_union(&p1->name, &p2->name) && ccs_same_number_union(&p1->number, &p2->number); } /** * ccs_merge_path_number_acl - Merge duplicated "struct ccs_path_number_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * @is_delete: True for @a &= ~@b, false for @a |= @b. * * Returns true if @a is empty, false otherwise. */ static bool ccs_merge_path_number_acl(struct ccs_acl_info *a, struct ccs_acl_info *b, const bool is_delete) { u8 * const a_perm = &container_of(a, struct ccs_path_number_acl, head) ->perm; u8 perm = *a_perm; const u8 b_perm = container_of(b, struct ccs_path_number_acl, head) ->perm; if (is_delete) perm &= ~b_perm; else perm |= b_perm; *a_perm = perm; return !perm; } /** * ccs_update_path_number_acl - Update create/mkdir/mkfifo/mksock/ioctl/chmod/chown/chgrp ACL. * * @perm: Permission. * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_update_path_number_acl(const u8 perm, struct ccs_acl_param *param) { struct ccs_path_number_acl e = { .head.type = CCS_TYPE_PATH_NUMBER_ACL, .perm = perm }; int error; if (!ccs_parse_name_union(param, &e.name) || !ccs_parse_number_union(param, &e.number)) error = -EINVAL; else error = ccs_update_domain(&e.head, sizeof(e), param, ccs_same_path_number_acl, ccs_merge_path_number_acl); ccs_put_name_union(&e.name); ccs_put_number_union(&e.number); return error; } /** * ccs_same_mount_acl - Check for duplicated "struct ccs_mount_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * * Returns true if @a == @b, false otherwise. */ static bool ccs_same_mount_acl(const struct ccs_acl_info *a, const struct ccs_acl_info *b) { const struct ccs_mount_acl *p1 = container_of(a, typeof(*p1), head); const struct ccs_mount_acl *p2 = container_of(b, typeof(*p2), head); return ccs_same_name_union(&p1->dev_name, &p2->dev_name) && ccs_same_name_union(&p1->dir_name, &p2->dir_name) && ccs_same_name_union(&p1->fs_type, &p2->fs_type) && ccs_same_number_union(&p1->flags, &p2->flags); } /** * ccs_update_mount_acl - Write "struct ccs_mount_acl" list. * * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_update_mount_acl(struct ccs_acl_param *param) { struct ccs_mount_acl e = { .head.type = CCS_TYPE_MOUNT_ACL }; int error; if (!ccs_parse_name_union(param, &e.dev_name) || !ccs_parse_name_union(param, &e.dir_name) || !ccs_parse_name_union(param, &e.fs_type) || !ccs_parse_number_union(param, &e.flags)) error = -EINVAL; else error = ccs_update_domain(&e.head, sizeof(e), param, ccs_same_mount_acl, NULL); ccs_put_name_union(&e.dev_name); ccs_put_name_union(&e.dir_name); ccs_put_name_union(&e.fs_type); ccs_put_number_union(&e.flags); return error; } /** * ccs_write_file - Update file related list. * * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_write_file(struct ccs_acl_param *param) { u16 perm = 0; u8 type; const char *operation = ccs_read_token(param); for (type = 0; type < CCS_MAX_PATH_OPERATION; type++) if (ccs_permstr(operation, ccs_path_keyword[type])) perm |= 1 << type; if (perm) return ccs_update_path_acl(perm, param); for (type = 0; type < CCS_MAX_PATH2_OPERATION; type++) if (ccs_permstr(operation, ccs_mac_keywords[ccs_pp2mac[type]])) perm |= 1 << type; if (perm) return ccs_update_path2_acl(perm, param); for (type = 0; type < CCS_MAX_PATH_NUMBER_OPERATION; type++) if (ccs_permstr(operation, ccs_mac_keywords[ccs_pn2mac[type]])) perm |= 1 << type; if (perm) return ccs_update_path_number_acl(perm, param); for (type = 0; type < CCS_MAX_MKDEV_OPERATION; type++) if (ccs_permstr(operation, ccs_mac_keywords[ccs_pnnn2mac[type]])) perm |= 1 << type; if (perm) return ccs_update_mkdev_acl(perm, param); if (ccs_permstr(operation, ccs_mac_keywords[CCS_MAC_FILE_MOUNT])) return ccs_update_mount_acl(param); return -EINVAL; } /** * ccs_parse_ipaddr_union - Parse an IP address. * * @param: Pointer to "struct ccs_acl_param". * @ptr: Pointer to "struct ccs_ipaddr_union". * * Returns true on success, false otherwise. */ static bool ccs_parse_ipaddr_union(struct ccs_acl_param *param, struct ccs_ipaddr_union *ptr) { struct ccs_ip_address_entry e; memset(ptr, 0, sizeof(*ptr)); if (ccs_parse_ip(ccs_read_token(param), &e) == 0) { memmove(&ptr->ip[0], e.min, sizeof(ptr->ip[0])); memmove(&ptr->ip[1], e.max, sizeof(ptr->ip[1])); ptr->is_ipv6 = e.is_ipv6; return true; } return false; } /* * Routines for printing IPv4 or IPv6 address. * These are copied from include/linux/kernel.h include/net/ipv6.h * include/net/addrconf.h lib/hexdump.c lib/vsprintf.c and simplified. */ static const char hex_asc[] = "0123456789abcdef"; #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] #define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] static inline char *pack_hex_byte(char *buf, u8 byte) { *buf++ = hex_asc_hi(byte); *buf++ = hex_asc_lo(byte); return buf; } static inline int ipv6_addr_v4mapped(const struct in6_addr *a) { return (a->s6_addr32[0] | a->s6_addr32[1] | (a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0; } static inline int ipv6_addr_is_isatap(const struct in6_addr *addr) { return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); } static char *ip4_string(char *p, const u8 *addr) { /* * Since this function is called outside vsnprintf(), I can use * sprintf() here. */ return p + sprintf(p, "%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3]); } static char *ip6_compressed_string(char *p, const char *addr) { int i, j, range; unsigned char zerolength[8]; int longest = 1; int colonpos = -1; u16 word; u8 hi, lo; bool needcolon = false; bool useIPv4; struct in6_addr in6; memcpy(&in6, addr, sizeof(struct in6_addr)); useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6); memset(zerolength, 0, sizeof(zerolength)); if (useIPv4) range = 6; else range = 8; /* find position of longest 0 run */ for (i = 0; i < range; i++) { for (j = i; j < range; j++) { if (in6.s6_addr16[j] != 0) break; zerolength[i]++; } } for (i = 0; i < range; i++) { if (zerolength[i] > longest) { longest = zerolength[i]; colonpos = i; } } if (longest == 1) /* don't compress a single 0 */ colonpos = -1; /* emit address */ for (i = 0; i < range; i++) { if (i == colonpos) { if (needcolon || i == 0) *p++ = ':'; *p++ = ':'; needcolon = false; i += longest - 1; continue; } if (needcolon) { *p++ = ':'; needcolon = false; } /* hex u16 without leading 0s */ word = ntohs(in6.s6_addr16[i]); hi = word >> 8; lo = word & 0xff; if (hi) { if (hi > 0x0f) p = pack_hex_byte(p, hi); else *p++ = hex_asc_lo(hi); p = pack_hex_byte(p, lo); } else if (lo > 0x0f) p = pack_hex_byte(p, lo); else *p++ = hex_asc_lo(lo); needcolon = true; } if (useIPv4) { if (needcolon) *p++ = ':'; p = ip4_string(p, &in6.s6_addr[12]); } *p = '\0'; return p; } /** * ccs_print_ipv4 - Print an IPv4 address. * * @min_ip: Pointer to "u32 in network byte order". * @max_ip: Pointer to "u32 in network byte order". * * Returns nothing. */ static void ccs_print_ipv4(const u32 *min_ip, const u32 *max_ip) { char addr[sizeof("255.255.255.255")]; ip4_string(addr, (const u8 *) min_ip); cprintf("%s", addr); if (*min_ip == *max_ip) return; ip4_string(addr, (const u8 *) max_ip); cprintf("-%s", addr); } /** * ccs_print_ipv6 - Print an IPv6 address. * * @min_ip: Pointer to "struct in6_addr". * @max_ip: Pointer to "struct in6_addr". * * Returns nothing. */ static void ccs_print_ipv6(const struct in6_addr *min_ip, const struct in6_addr *max_ip) { char addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")]; ip6_compressed_string(addr, (const char *) min_ip); cprintf("%s", addr); if (!memcmp(min_ip, max_ip, 16)) return; ip6_compressed_string(addr, (const char *) max_ip); cprintf("-%s", addr); } /** * ccs_print_ip - Print an IP address. * * @ptr: Pointer to "struct ccs_ipaddr_union". * * Returns nothing. */ static void ccs_print_ip(const struct ccs_ipaddr_union *ptr) { if (ptr->is_ipv6) ccs_print_ipv6(&ptr->ip[0], &ptr->ip[1]); else ccs_print_ipv4(&ptr->ip[0].s6_addr32[0], &ptr->ip[1].s6_addr32[0]); } /** * ccs_same_inet_acl - Check for duplicated "struct ccs_inet_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * * Returns true if @a == @b except permission bits, false otherwise. */ static bool ccs_same_inet_acl(const struct ccs_acl_info *a, const struct ccs_acl_info *b) { const struct ccs_inet_acl *p1 = container_of(a, typeof(*p1), head); const struct ccs_inet_acl *p2 = container_of(b, typeof(*p2), head); return p1->protocol == p2->protocol && ccs_same_ipaddr_union(&p1->address, &p2->address) && ccs_same_number_union(&p1->port, &p2->port); } /** * ccs_same_unix_acl - Check for duplicated "struct ccs_unix_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * * Returns true if @a == @b except permission bits, false otherwise. */ static bool ccs_same_unix_acl(const struct ccs_acl_info *a, const struct ccs_acl_info *b) { const struct ccs_unix_acl *p1 = container_of(a, typeof(*p1), head); const struct ccs_unix_acl *p2 = container_of(b, typeof(*p2), head); return p1->protocol == p2->protocol && ccs_same_name_union(&p1->name, &p2->name); } /** * ccs_merge_inet_acl - Merge duplicated "struct ccs_inet_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * @is_delete: True for @a &= ~@b, false for @a |= @b. * * Returns true if @a is empty, false otherwise. */ static bool ccs_merge_inet_acl(struct ccs_acl_info *a, struct ccs_acl_info *b, const bool is_delete) { u8 * const a_perm = &container_of(a, struct ccs_inet_acl, head)->perm; u8 perm = *a_perm; const u8 b_perm = container_of(b, struct ccs_inet_acl, head)->perm; if (is_delete) perm &= ~b_perm; else perm |= b_perm; *a_perm = perm; return !perm; } /** * ccs_merge_unix_acl - Merge duplicated "struct ccs_unix_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * @is_delete: True for @a &= ~@b, false for @a |= @b. * * Returns true if @a is empty, false otherwise. */ static bool ccs_merge_unix_acl(struct ccs_acl_info *a, struct ccs_acl_info *b, const bool is_delete) { u8 * const a_perm = &container_of(a, struct ccs_unix_acl, head)->perm; u8 perm = *a_perm; const u8 b_perm = container_of(b, struct ccs_unix_acl, head)->perm; if (is_delete) perm &= ~b_perm; else perm |= b_perm; *a_perm = perm; return !perm; } /** * ccs_write_inet_network - Write "struct ccs_inet_acl" list. * * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_write_inet_network(struct ccs_acl_param *param) { struct ccs_inet_acl e = { .head.type = CCS_TYPE_INET_ACL }; int error = -EINVAL; u8 type; const char *protocol = ccs_read_token(param); const char *operation = ccs_read_token(param); for (e.protocol = 0; e.protocol < CCS_SOCK_MAX; e.protocol++) if (!strcmp(protocol, ccs_proto_keyword[e.protocol])) break; for (type = 0; type < CCS_MAX_NETWORK_OPERATION; type++) if (ccs_permstr(operation, ccs_socket_keyword[type])) e.perm |= 1 << type; if (e.protocol == CCS_SOCK_MAX || !e.perm) return -EINVAL; if (param->data[0] == '@') { param->data++; e.address.group = ccs_get_group(param, &ccs_address_group); if (!e.address.group) return -ENOMEM; } else { if (!ccs_parse_ipaddr_union(param, &e.address)) goto out; } if (!ccs_parse_number_union(param, &e.port) || e.port.values[1] > 65535) goto out; error = ccs_update_domain(&e.head, sizeof(e), param, ccs_same_inet_acl, ccs_merge_inet_acl); out: ccs_put_group(e.address.group); ccs_put_number_union(&e.port); return error; } /** * ccs_write_unix_network - Write "struct ccs_unix_acl" list. * * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_write_unix_network(struct ccs_acl_param *param) { struct ccs_unix_acl e = { .head.type = CCS_TYPE_UNIX_ACL }; int error; u8 type; const char *protocol = ccs_read_token(param); const char *operation = ccs_read_token(param); for (e.protocol = 0; e.protocol < CCS_SOCK_MAX; e.protocol++) if (!strcmp(protocol, ccs_proto_keyword[e.protocol])) break; for (type = 0; type < CCS_MAX_NETWORK_OPERATION; type++) if (ccs_permstr(operation, ccs_socket_keyword[type])) e.perm |= 1 << type; if (e.protocol == CCS_SOCK_MAX || !e.perm) return -EINVAL; if (!ccs_parse_name_union(param, &e.name)) return -EINVAL; error = ccs_update_domain(&e.head, sizeof(e), param, ccs_same_unix_acl, ccs_merge_unix_acl); ccs_put_name_union(&e.name); return error; } /** * ccs_same_env_acl - Check for duplicated "struct ccs_env_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * * Returns true if @a == @b, false otherwise. */ static bool ccs_same_env_acl(const struct ccs_acl_info *a, const struct ccs_acl_info *b) { const struct ccs_env_acl *p1 = container_of(a, typeof(*p1), head); const struct ccs_env_acl *p2 = container_of(b, typeof(*p2), head); return p1->env == p2->env; } /** * ccs_write_env - Write "struct ccs_env_acl" list. * * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_write_env(struct ccs_acl_param *param) { struct ccs_env_acl e = { .head.type = CCS_TYPE_ENV_ACL }; int error = -ENOMEM; const char *data = ccs_read_token(param); if (!ccs_correct_word(data) || strchr(data, '=')) return -EINVAL; e.env = ccs_get_name(data); error = ccs_update_domain(&e.head, sizeof(e), param, ccs_same_env_acl, NULL); ccs_put_name(e.env); return error; } /** * ccs_write_misc - Update environment variable list. * * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_write_misc(struct ccs_acl_param *param) { if (ccs_str_starts(param->data, "env ")) return ccs_write_env(param); return -EINVAL; } /** * ccs_print_namespace - Print namespace header. * * @ns: Pointer to "struct ccs_policy_namespace". * * Returns nothing. */ static void ccs_print_namespace(const struct ccs_policy_namespace *ns) { if (!ccs_namespace_enabled) return; cprintf("%s ", ns->name); } /** * ccs_assign_profile - Create a new profile. * * @ns: Pointer to "struct ccs_policy_namespace". * @profile: Profile number to create. * * Returns pointer to "struct ccs_profile" on success, NULL otherwise. */ static struct ccs_profile *ccs_assign_profile(struct ccs_policy_namespace *ns, const unsigned int profile) { struct ccs_profile *ptr; if (profile >= CCS_MAX_PROFILES) return NULL; ptr = ns->profile_ptr[profile]; if (ptr) return ptr; ptr = ccs_malloc(sizeof(*ptr)); ptr->default_config = CCS_CONFIG_DISABLED | CCS_CONFIG_WANT_GRANT_LOG | CCS_CONFIG_WANT_REJECT_LOG; memset(ptr->config, CCS_CONFIG_USE_DEFAULT, sizeof(ptr->config)); ptr->pref[CCS_PREF_MAX_AUDIT_LOG] = 1024; ptr->pref[CCS_PREF_MAX_LEARNING_ENTRY] = 2048; ns->profile_ptr[profile] = ptr; return ptr; } /** * ccs_find_yesno - Find values for specified keyword. * * @string: String to check. * @find: Name of keyword. * * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise. */ static s8 ccs_find_yesno(const char *string, const char *find) { const char *cp = strstr(string, find); if (cp) { cp += strlen(find); if (!strncmp(cp, "=yes", 4)) return 1; else if (!strncmp(cp, "=no", 3)) return 0; } return -1; } /** * ccs_set_uint - Set value for specified preference. * * @i: Pointer to "unsigned int". * @string: String to check. * @find: Name of keyword. * * Returns nothing. */ static void ccs_set_uint(unsigned int *i, const char *string, const char *find) { const char *cp = strstr(string, find); if (cp) sscanf(cp + strlen(find), "=%u", i); } /** * ccs_set_mode - Set mode for specified profile. * * @name: Name of functionality. * @value: Mode for @name. * @profile: Pointer to "struct ccs_profile". * * Returns 0 on success, negative value otherwise. */ static int ccs_set_mode(char *name, const char *value, struct ccs_profile *profile) { u8 i; u8 config; if (!strcmp(name, "CONFIG")) { i = CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX; config = profile->default_config; } else if (ccs_str_starts(name, "CONFIG::")) { config = 0; for (i = 0; i < CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX; i++) { int len = 0; if (i < CCS_MAX_MAC_INDEX) { const u8 c = ccs_index2category[i]; const char *category = ccs_category_keywords[c]; len = strlen(category); if (strncmp(name, category, len) || name[len++] != ':' || name[len++] != ':') continue; } if (strcmp(name + len, ccs_mac_keywords[i])) continue; config = profile->config[i]; break; } if (i == CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX) return -EINVAL; } else { return -EINVAL; } if (strstr(value, "use_default")) { config = CCS_CONFIG_USE_DEFAULT; } else { u8 mode; for (mode = 0; mode < CCS_CONFIG_MAX_MODE; mode++) if (strstr(value, ccs_mode[mode])) /* * Update lower 3 bits in order to distinguish * 'config' from 'CCS_CONFIG_USE_DEFAULT'. */ config = (config & ~7) | mode; if (config != CCS_CONFIG_USE_DEFAULT) { switch (ccs_find_yesno(value, "grant_log")) { case 1: config |= CCS_CONFIG_WANT_GRANT_LOG; break; case 0: config &= ~CCS_CONFIG_WANT_GRANT_LOG; break; } switch (ccs_find_yesno(value, "reject_log")) { case 1: config |= CCS_CONFIG_WANT_REJECT_LOG; break; case 0: config &= ~CCS_CONFIG_WANT_REJECT_LOG; break; } } } if (i < CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX) profile->config[i] = config; else if (config != CCS_CONFIG_USE_DEFAULT) profile->default_config = config; return 0; } /** * ccs_write_profile - Write profile table. * * Returns 0 on success, negative value otherwise. */ static int ccs_write_profile(void) { char *data = head.data; unsigned int i; char *cp; struct ccs_profile *profile; if (sscanf(data, "PROFILE_VERSION=%u", &head.ns->profile_version) == 1) return 0; i = strtoul(data, &cp, 10); if (*cp != '-') return -EINVAL; data = cp + 1; profile = ccs_assign_profile(head.ns, i); if (!profile) return -EINVAL; cp = strchr(data, '='); if (!cp) return -EINVAL; *cp++ = '\0'; if (!strcmp(data, "COMMENT")) { const struct ccs_path_info *new_comment = ccs_get_name(cp); const struct ccs_path_info *old_comment = profile->comment; profile->comment = new_comment; ccs_put_name(old_comment); return 0; } if (!strcmp(data, "PREFERENCE")) { for (i = 0; i < CCS_MAX_PREF; i++) ccs_set_uint(&profile->pref[i], cp, ccs_pref_keywords[i]); return 0; } return ccs_set_mode(data, cp, profile); } /** * ccs_print_config - Print mode for specified functionality. * * @config: Mode for that functionality. * * Returns nothing. * * Caller prints functionality's name. */ static void ccs_print_config(const u8 config) { cprintf("={ mode=%s grant_log=%s reject_log=%s }\n", ccs_mode[config & 3], ccs_yesno(config & CCS_CONFIG_WANT_GRANT_LOG), ccs_yesno(config & CCS_CONFIG_WANT_REJECT_LOG)); } /** * ccs_read_profile - Read profile table. * * Returns nothing. */ static void ccs_read_profile(void) { struct ccs_policy_namespace *ns; if (head.eof) return; list_for_each_entry(ns, &ccs_namespace_list, namespace_list) { u16 index; ccs_print_namespace(ns); cprintf("PROFILE_VERSION=%u\n", ns->profile_version); for (index = 0; index < CCS_MAX_PROFILES; index++) { u8 i; const struct ccs_profile *profile = ns->profile_ptr[index]; if (!profile) continue; ccs_print_namespace(ns); cprintf("%u-COMMENT=%s\n", index, profile->comment ? profile->comment->name : ""); ccs_print_namespace(ns); cprintf("%u-PREFERENCE={ ", index); for (i = 0; i < CCS_MAX_PREF; i++) cprintf("%s=%u ", ccs_pref_keywords[i], profile->pref[i]); cprintf("}\n"); ccs_print_namespace(ns); cprintf("%u-CONFIG", index); ccs_print_config(profile->default_config); for (i = 0; i < CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX; i++) { const u8 config = profile->config[i]; if (config == CCS_CONFIG_USE_DEFAULT) continue; ccs_print_namespace(ns); if (i < CCS_MAX_MAC_INDEX) cprintf("%u-CONFIG::%s::%s", index, ccs_category_keywords [ccs_index2category[i]], ccs_mac_keywords[i]); else cprintf("%u-CONFIG::%s", index, ccs_mac_keywords[i]); ccs_print_config(config); } } } head.eof = true; } /** * ccs_same_manager - Check for duplicated "struct ccs_manager" entry. * * @a: Pointer to "struct ccs_acl_head". * @b: Pointer to "struct ccs_acl_head". * * Returns true if @a == @b, false otherwise. */ static bool ccs_same_manager(const struct ccs_acl_head *a, const struct ccs_acl_head *b) { return container_of(a, struct ccs_manager, head)->manager == container_of(b, struct ccs_manager, head)->manager; } /** * ccs_update_manager_entry - Add a manager entry. * * @manager: The path to manager or the domainnamme. * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. */ static inline int ccs_update_manager_entry(const char *manager, const bool is_delete) { struct ccs_manager e = { }; struct ccs_acl_param param = { .is_delete = is_delete, .list = &ccs_manager_list, }; int error = is_delete ? -ENOENT : -ENOMEM; if (ccs_domain_def(manager)) { if (!ccs_correct_domain(manager)) return -EINVAL; e.is_domain = true; } else { if (!ccs_correct_path(manager)) return -EINVAL; } e.manager = ccs_get_name(manager); error = ccs_update_policy(&e.head, sizeof(e), ¶m, ccs_same_manager); ccs_put_name(e.manager); return error; } /** * ccs_write_manager - Write manager policy. * * Returns 0 on success, negative value otherwise. */ static int ccs_write_manager(void) { return ccs_update_manager_entry(head.data, head.is_delete); } /** * ccs_read_manager - Read manager policy. * * Returns nothing. */ static void ccs_read_manager(void) { struct ccs_manager *ptr; if (head.eof) return; list_for_each_entry(ptr, &ccs_manager_list, head.list) if (!ptr->head.is_deleted) cprintf("%s\n", ptr->manager->name); head.eof = true; } /** * ccs_select_domain - Parse select command. * * @data: String to parse. * * Returns true on success, false otherwise. */ static bool ccs_select_domain(const char *data) { struct ccs_domain2_info *domain = NULL; if (strncmp(data, "select ", 7)) return false; data += 7; if (!strncmp(data, "domain=", 7)) { if (*(data + 7) == '<') domain = ccs_find_domain2(data + 7); } else return false; if (domain) { head.domain = domain; head.print_this_domain_only = domain; } else head.eof = true; cprintf("# select %s\n", data); return true; } /** * ccs_same_task_acl - Check for duplicated "struct ccs_task_acl" entry. * * @a: Pointer to "struct ccs_acl_info". * @b: Pointer to "struct ccs_acl_info". * * Returns true if @a == @b, false otherwise. */ static bool ccs_same_task_acl(const struct ccs_acl_info *a, const struct ccs_acl_info *b) { const struct ccs_task_acl *p1 = container_of(a, typeof(*p1), head); const struct ccs_task_acl *p2 = container_of(b, typeof(*p2), head); return p1->domainname == p2->domainname; } /** * ccs_write_task - Update task related list. * * @param: Pointer to "struct ccs_acl_param". * * Returns 0 on success, negative value otherwise. */ static int ccs_write_task(struct ccs_acl_param *param) { int error; if (!ccs_str_starts(param->data, "manual_domain_transition ")) { return -EINVAL; } else { struct ccs_task_acl e = { .head.type = CCS_TYPE_MANUAL_TASK_ACL, .domainname = ccs_get_domainname(param), }; if (!e.domainname) error = -EINVAL; else error = ccs_update_domain(&e.head, sizeof(e), param, ccs_same_task_acl, NULL); ccs_put_name(e.domainname); } return error; } /** * ccs_write_domain2 - Write domain policy. * * @ns: Pointer to "struct ccs_policy_namespace". * @list: Pointer to "struct list_head". * @data: Policy to be interpreted. * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. */ static int ccs_write_domain2(struct ccs_policy_namespace *ns, struct list_head *list, char *data, const bool is_delete) { struct ccs_acl_param param = { .ns = ns, .list = list, .data = data, .is_delete = is_delete, }; static const struct { const char *keyword; int (*write) (struct ccs_acl_param *); } ccs_callback[5] = { { "file ", ccs_write_file }, { "network inet ", ccs_write_inet_network }, { "network unix ", ccs_write_unix_network }, { "misc ", ccs_write_misc }, { "task ", ccs_write_task }, }; u8 i; for (i = 0; i < 5; i++) { if (!ccs_str_starts(param.data, ccs_callback[i].keyword)) continue; return ccs_callback[i].write(¶m); } return -EINVAL; } /** * ccs_delete_domain2 - Delete a domain from domain policy. * * @domainname: Name of domain. * * Returns nothing. */ static void ccs_delete_domain2(const char *domainname) { struct ccs_domain2_info *domain; list_for_each_entry(domain, &ccs_domain_list, list) { if (domain == &ccs_kernel_domain) continue; if (strcmp(domain->domainname->name, domainname)) continue; domain->is_deleted = true; } } /** * ccs_write_domain - Write domain policy. * * Returns 0 on success, negative value otherwise. */ static int ccs_write_domain(void) { char *data = head.data; struct ccs_policy_namespace *ns; struct ccs_domain2_info *domain = head.domain; const bool is_delete = head.is_delete; const bool is_select = !is_delete && ccs_str_starts(data, "select "); unsigned int idx; if (*data == '<') { domain = NULL; if (is_delete) ccs_delete_domain2(data); else if (is_select) domain = ccs_find_domain2(data); else domain = ccs_assign_domain2(data); head.domain = domain; return 0; } if (!domain) return -EINVAL; ns = ccs_assign_namespace(domain->domainname->name); if (!ns) return -EINVAL; if (sscanf(data, "use_profile %u\n", &idx) == 1 && idx < CCS_MAX_PROFILES) { if (ns->profile_ptr[(u8) idx]) if (!is_delete) domain->profile = (u8) idx; return 0; } if (sscanf(data, "use_group %u\n", &idx) == 1 && idx < CCS_MAX_ACL_GROUPS) { domain->group[idx] = !is_delete; return 0; } for (idx = 0; idx < CCS_MAX_DOMAIN_INFO_FLAGS; idx++) { const char *cp = ccs_dif[idx]; if (strncmp(data, cp, strlen(cp) - 1)) continue; domain->flags[idx] = !is_delete; return 0; } return ccs_write_domain2(ns, &domain->acl_info_list, data, is_delete); } /** * ccs_print_name_union - Print a ccs_name_union. * * @ptr: Pointer to "struct ccs_name_union". * * Returns nothing. */ static void ccs_print_name_union(const struct ccs_name_union *ptr) { if (ptr->group) cprintf(" @%s", ptr->group->group_name->name); else cprintf(" %s", ptr->filename->name); } /** * ccs_print_name_union_quoted - Print a ccs_name_union with a quote. * * @ptr: Pointer to "struct ccs_name_union". * * Returns nothing. */ static void ccs_print_name_union_quoted(const struct ccs_name_union *ptr) { if (ptr->group) cprintf("@%s", ptr->group->group_name->name); else cprintf("\"%s\"", ptr->filename->name); } /** * ccs_print_number_union_nospace - Print a ccs_number_union without a space. * * @ptr: Pointer to "struct ccs_number_union". * * Returns nothing. */ static void ccs_print_number_union_nospace(const struct ccs_number_union *ptr) { if (ptr->group) { cprintf("@%s", ptr->group->group_name->name); } else { int i; unsigned long min = ptr->values[0]; const unsigned long max = ptr->values[1]; u8 min_type = ptr->value_type[0]; const u8 max_type = ptr->value_type[1]; for (i = 0; i < 2; i++) { switch (min_type) { case CCS_VALUE_TYPE_HEXADECIMAL: cprintf("0x%lX", min); break; case CCS_VALUE_TYPE_OCTAL: cprintf("0%lo", min); break; default: cprintf("%lu", min); break; } if (min == max && min_type == max_type) break; cprintf("-"); min_type = max_type; min = max; } } } /** * ccs_print_number_union - Print a ccs_number_union. * * @ptr: Pointer to "struct ccs_number_union". * * Returns nothing. */ static void ccs_print_number_union(const struct ccs_number_union *ptr) { cprintf(" "); ccs_print_number_union_nospace(ptr); } /** * ccs_print_condition - Print condition part. * * @cond: Pointer to "struct ccs_condition". * * Returns nothing. */ static void ccs_print_condition(const struct ccs_condition *cond) { const u16 condc = cond->condc; const struct ccs_condition_element *condp = (typeof(condp)) (cond + 1); const struct ccs_number_union *numbers_p = (typeof(numbers_p)) (condp + condc); const struct ccs_name_union *names_p = (typeof(names_p)) (numbers_p + cond->numbers_count); const struct ccs_argv *argv = (typeof(argv)) (names_p + cond->names_count); const struct ccs_envp *envp = (typeof(envp)) (argv + cond->argc); u16 i; if (cond->transit && cond->exec_transit) cprintf(" %s", cond->transit->name); for (i = 0; i < condc; i++) { const u8 match = condp->equals; const u8 left = condp->left; const u8 right = condp->right; condp++; cprintf(" "); switch (left) { case CCS_ARGV_ENTRY: cprintf("exec.argv[%lu]%s=\"%s\"", argv->index, argv->is_not ? "!" : "", argv->value->name); argv++; continue; case CCS_ENVP_ENTRY: cprintf("exec.envp[\"%s\"]%s=", envp->name->name, envp->is_not ? "!" : ""); if (envp->value) cprintf("\"%s\"", envp->value->name); else cprintf("NULL"); envp++; continue; case CCS_NUMBER_UNION: ccs_print_number_union_nospace(numbers_p++); break; default: cprintf("%s", ccs_condition_keyword[left]); break; } cprintf("%s", match ? "=" : "!="); switch (right) { case CCS_NAME_UNION: ccs_print_name_union_quoted(names_p++); break; case CCS_NUMBER_UNION: ccs_print_number_union_nospace(numbers_p++); break; default: cprintf("%s", ccs_condition_keyword[right]); break; } } if (cond->grant_log != CCS_GRANTLOG_AUTO) cprintf(" grant_log=%s", ccs_yesno(cond->grant_log == CCS_GRANTLOG_YES)); if (cond->transit && !cond->exec_transit) cprintf(" auto_domain_transition=\"%s\"", cond->transit->name); } /** * ccs_set_group - Print "acl_group " header keyword and category name. * * @category: Category name. * * Returns nothing. */ static void ccs_set_group(const char *category) { if (head.type == CCS_EXCEPTIONPOLICY) { ccs_print_namespace(head.ns); cprintf("acl_group %u ", head.acl_group_index); } cprintf("%s", category); } /** * ccs_print_entry - Print an ACL entry. * * @acl: Pointer to an ACL entry. * * Returns nothing. */ static void ccs_print_entry(const struct ccs_acl_info *acl) { const u8 acl_type = acl->type; const bool may_trigger_transition = acl->cond && acl->cond->transit; bool first = true; u8 bit; if (acl->is_deleted) return; if (acl_type == CCS_TYPE_PATH_ACL) { struct ccs_path_acl *ptr = container_of(acl, typeof(*ptr), head); const u16 perm = ptr->perm; for (bit = 0; bit < CCS_MAX_PATH_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; if (head.print_transition_related_only && bit != CCS_TYPE_EXECUTE && !may_trigger_transition) continue; if (first) { ccs_set_group("file "); first = false; } else { cprintf("/"); } cprintf("%s", ccs_path_keyword[bit]); } if (first) return; ccs_print_name_union(&ptr->name); } else if (acl_type == CCS_TYPE_MANUAL_TASK_ACL) { struct ccs_task_acl *ptr = container_of(acl, typeof(*ptr), head); ccs_set_group("task "); cprintf("manual_domain_transition "); cprintf("%s", ptr->domainname->name); } else if (head.print_transition_related_only && !may_trigger_transition) { return; } else if (acl_type == CCS_TYPE_MKDEV_ACL) { struct ccs_mkdev_acl *ptr = container_of(acl, typeof(*ptr), head); const u8 perm = ptr->perm; for (bit = 0; bit < CCS_MAX_MKDEV_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; if (first) { ccs_set_group("file "); first = false; } else { cprintf("/"); } cprintf("%s", ccs_mac_keywords[ccs_pnnn2mac[bit]]); } if (first) return; ccs_print_name_union(&ptr->name); ccs_print_number_union(&ptr->mode); ccs_print_number_union(&ptr->major); ccs_print_number_union(&ptr->minor); } else if (acl_type == CCS_TYPE_PATH2_ACL) { struct ccs_path2_acl *ptr = container_of(acl, typeof(*ptr), head); const u8 perm = ptr->perm; for (bit = 0; bit < CCS_MAX_PATH2_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; if (first) { ccs_set_group("file "); first = false; } else { cprintf("/"); } cprintf("%s", ccs_mac_keywords[ccs_pp2mac[bit]]); } if (first) return; ccs_print_name_union(&ptr->name1); ccs_print_name_union(&ptr->name2); } else if (acl_type == CCS_TYPE_PATH_NUMBER_ACL) { struct ccs_path_number_acl *ptr = container_of(acl, typeof(*ptr), head); const u8 perm = ptr->perm; for (bit = 0; bit < CCS_MAX_PATH_NUMBER_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; if (first) { ccs_set_group("file "); first = false; } else { cprintf("/"); } cprintf("%s", ccs_mac_keywords[ccs_pn2mac[bit]]); } if (first) return; ccs_print_name_union(&ptr->name); ccs_print_number_union(&ptr->number); } else if (acl_type == CCS_TYPE_ENV_ACL) { struct ccs_env_acl *ptr = container_of(acl, typeof(*ptr), head); ccs_set_group("misc env "); cprintf("%s", ptr->env->name); } else if (acl_type == CCS_TYPE_INET_ACL) { struct ccs_inet_acl *ptr = container_of(acl, typeof(*ptr), head); const u8 perm = ptr->perm; for (bit = 0; bit < CCS_MAX_NETWORK_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; if (first) { ccs_set_group("network inet "); cprintf("%s ", ccs_proto_keyword[ptr->protocol]); first = false; } else { cprintf("/"); } cprintf("%s", ccs_socket_keyword[bit]); } if (first) return; cprintf(" "); if (ptr->address.group) cprintf("@%s", ptr->address.group->group_name->name); else ccs_print_ip(&ptr->address); ccs_print_number_union(&ptr->port); } else if (acl_type == CCS_TYPE_UNIX_ACL) { struct ccs_unix_acl *ptr = container_of(acl, typeof(*ptr), head); const u8 perm = ptr->perm; for (bit = 0; bit < CCS_MAX_NETWORK_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; if (first) { ccs_set_group("network unix "); cprintf("%s ", ccs_proto_keyword[ptr->protocol]); first = false; } else { cprintf("/"); } cprintf("%s", ccs_socket_keyword[bit]); } if (first) return; ccs_print_name_union(&ptr->name); } else if (acl_type == CCS_TYPE_MOUNT_ACL) { struct ccs_mount_acl *ptr = container_of(acl, typeof(*ptr), head); ccs_set_group("file mount"); ccs_print_name_union(&ptr->dev_name); ccs_print_name_union(&ptr->dir_name); ccs_print_name_union(&ptr->fs_type); ccs_print_number_union(&ptr->flags); } if (acl->cond) ccs_print_condition(acl->cond); cprintf("\n"); } /** * ccs_read_domain2 - Read domain policy. * * @list: Pointer to "struct list_head". * * Returns nothing. */ static void ccs_read_domain2(struct list_head *list) { struct ccs_acl_info *ptr; list_for_each_entry(ptr, list, list) ccs_print_entry(ptr); } /** * ccs_read_domain - Read domain policy. * * Returns nothing. */ static void ccs_read_domain(void) { struct ccs_domain2_info *domain; if (head.eof) return; list_for_each_entry(domain, &ccs_domain_list, list) { u16 i; if (domain->is_deleted) continue; if (head.print_this_domain_only && head.print_this_domain_only != domain) continue; /* Print domainname and flags. */ cprintf("%s\n", domain->domainname->name); cprintf("use_profile %u\n", domain->profile); for (i = 0; i < CCS_MAX_DOMAIN_INFO_FLAGS; i++) if (domain->flags[i]) cprintf("%s", ccs_dif[i]); for (i = 0; i < CCS_MAX_ACL_GROUPS; i++) if (domain->group[i]) cprintf("use_group %u\n", i); cprintf("\n"); ccs_read_domain2(&domain->acl_info_list); cprintf("\n"); } head.eof = true; } /** * ccs_same_path_group - Check for duplicated "struct ccs_path_group" entry. * * @a: Pointer to "struct ccs_acl_head". * @b: Pointer to "struct ccs_acl_head". * * Returns true if @a == @b, false otherwise. */ static bool ccs_same_path_group(const struct ccs_acl_head *a, const struct ccs_acl_head *b) { return container_of(a, struct ccs_path_group, head)->member_name == container_of(b, struct ccs_path_group, head)->member_name; } /** * ccs_same_number_group - Check for duplicated "struct ccs_number_group" entry. * * @a: Pointer to "struct ccs_acl_head". * @b: Pointer to "struct ccs_acl_head". * * Returns true if @a == @b, false otherwise. */ static bool ccs_same_number_group(const struct ccs_acl_head *a, const struct ccs_acl_head *b) { return !memcmp(&container_of(a, struct ccs_number_group, head)->number, &container_of(b, struct ccs_number_group, head)->number, sizeof(container_of(a, struct ccs_number_group, head) ->number)); } /** * ccs_same_address_group - Check for duplicated "struct ccs_address_group" entry. * * @a: Pointer to "struct ccs_acl_head". * @b: Pointer to "struct ccs_acl_head". * * Returns true if @a == @b, false otherwise. */ static bool ccs_same_address_group(const struct ccs_acl_head *a, const struct ccs_acl_head *b) { const struct ccs_address_group *p1 = container_of(a, typeof(*p1), head); const struct ccs_address_group *p2 = container_of(b, typeof(*p2), head); return ccs_same_ipaddr_union(&p1->address, &p2->address); } /** * ccs_write_group - Write "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group" list. * * @param: Pointer to "struct ccs_acl_param". * @type: Type of this group. * * Returns 0 on success, negative value otherwise. */ static int ccs_write_group(struct ccs_acl_param *param, const u8 type) { struct ccs_group *group = ccs_get_group(param, type == CCS_PATH_GROUP ? &ccs_path_group : type == CCS_NUMBER_GROUP ? &ccs_number_group : &ccs_address_group); int error = -EINVAL; if (!group) return -ENOMEM; param->list = &group->member_list; if (type == CCS_PATH_GROUP) { struct ccs_path_group e = { }; e.member_name = ccs_get_name(ccs_read_token(param)); error = ccs_update_policy(&e.head, sizeof(e), param, ccs_same_path_group); ccs_put_name(e.member_name); } else if (type == CCS_NUMBER_GROUP) { struct ccs_number_group e = { }; if (param->data[0] == '@' || !ccs_parse_number_union(param, &e.number)) goto out; error = ccs_update_policy(&e.head, sizeof(e), param, ccs_same_number_group); /* * ccs_put_number_union() is not needed because * param->data[0] != '@'. */ } else { struct ccs_address_group e = { }; if (param->data[0] == '@' || !ccs_parse_ipaddr_union(param, &e.address)) goto out; error = ccs_update_policy(&e.head, sizeof(e), param, ccs_same_address_group); } out: ccs_put_group(group); return error; } /** * ccs_write_exception - Write exception policy. * * Returns 0 on success, negative value otherwise. */ static int ccs_write_exception(void) { const bool is_delete = head.is_delete; struct ccs_acl_param param = { .ns = head.ns, .is_delete = is_delete, .data = head.data, }; u8 i; if (ccs_str_starts(param.data, "aggregator ")) return ccs_write_aggregator(¶m); for (i = 0; i < MAX_TRANSITION_TYPE; i++) if (ccs_str_starts(param.data, ccs_transition_type[i])) return ccs_write_transition_control(¶m, i); for (i = 0; i < CCS_MAX_GROUP; i++) if (ccs_str_starts(param.data, ccs_group_name[i])) return ccs_write_group(¶m, i); if (ccs_str_starts(param.data, "acl_group ")) { unsigned int group; char *data; group = strtoul(param.data, &data, 10); if (group < CCS_MAX_ACL_GROUPS && *data++ == ' ') return ccs_write_domain2(head.ns, &head.ns->acl_group[group], data, is_delete); } return -EINVAL; } /** * ccs_read_group - Read "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group" list. * * @ns: Pointer to "struct ccs_policy_namespace". * * Returns nothing. */ static void ccs_read_group(const struct ccs_policy_namespace *ns) { struct ccs_group *group; list_for_each_entry(group, &ccs_path_group, head.list) { struct ccs_acl_head *ptr; list_for_each_entry(ptr, &group->member_list, list) { if (group->ns != ns) continue; if (ptr->is_deleted) continue; ccs_print_namespace(group->ns); cprintf("%s%s", ccs_group_name[CCS_PATH_GROUP], group->group_name->name); cprintf(" %s", container_of(ptr, struct ccs_path_group, head)->member_name->name); cprintf("\n"); } } list_for_each_entry(group, &ccs_number_group, head.list) { struct ccs_acl_head *ptr; list_for_each_entry(ptr, &group->member_list, list) { if (group->ns != ns) continue; if (ptr->is_deleted) continue; ccs_print_namespace(group->ns); cprintf("%s%s", ccs_group_name[CCS_NUMBER_GROUP], group->group_name->name); ccs_print_number_union(&container_of (ptr, struct ccs_number_group, head)->number); cprintf("\n"); } } list_for_each_entry(group, &ccs_address_group, head.list) { struct ccs_acl_head *ptr; list_for_each_entry(ptr, &group->member_list, list) { if (group->ns != ns) continue; if (ptr->is_deleted) continue; ccs_print_namespace(group->ns); cprintf("%s%s", ccs_group_name[CCS_ADDRESS_GROUP], group->group_name->name); cprintf(" "); ccs_print_ip(&container_of (ptr, struct ccs_address_group, head)-> address); cprintf("\n"); } } } /** * ccs_read_policy - Read "struct ccs_..._entry" list. * * @ns: Pointer to "struct ccs_policy_namespace". * * Returns nothing. */ static void ccs_read_policy(const struct ccs_policy_namespace *ns) { struct ccs_acl_head *acl; if (head.print_transition_related_only) goto transition_only; list_for_each_entry(acl, &ccs_reserved_list, list) { struct ccs_reserved *ptr = container_of(acl, typeof(*ptr), head); if (acl->is_deleted || ptr->ns != ns) continue; ccs_print_namespace(ptr->ns); cprintf("deny_autobind "); ccs_print_number_union_nospace(&ptr->port); cprintf("\n"); } list_for_each_entry(acl, &ccs_aggregator_list, list) { struct ccs_aggregator *ptr = container_of(acl, typeof(*ptr), head); if (acl->is_deleted || ptr->ns != ns) continue; ccs_print_namespace(ptr->ns); cprintf("aggregator %s %s\n", ptr->original_name->name, ptr->aggregated_name->name); } transition_only: list_for_each_entry(acl, &ccs_transition_list, list) { struct ccs_transition_control *ptr = container_of(acl, typeof(*ptr), head); if (acl->is_deleted || ptr->ns != ns) continue; ccs_print_namespace(ptr->ns); cprintf("%s%s from %s\n", ccs_transition_type[ptr->type], ptr->program ? ptr->program->name : "any", ptr->domainname ? ptr->domainname->name : "any"); } } /** * ccs_read_exception - Read exception policy. * * Returns nothing. */ static void ccs_read_exception(void) { struct ccs_policy_namespace *ns; if (head.eof) return; list_for_each_entry(ns, &ccs_namespace_list, namespace_list) { unsigned int i; head.ns = ns; ccs_read_policy(ns); ccs_read_group(ns); for (i = 0; i < CCS_MAX_ACL_GROUPS; i++) { head.acl_group_index = i; ccs_read_domain2(&ns->acl_group[i]); } } head.eof = true; } /** * ccs_read_stat - Read statistic data. * * Returns nothing. */ static void ccs_read_stat(void) { u8 i; if (head.eof) return; for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) cprintf("Memory used by %-22s %10u\n", ccs_memory_headers[i], ccs_memory_quota[i]); head.eof = true; } /** * ccs_write_stat - Set memory quota. * * Returns 0. */ static int ccs_write_stat(void) { char *data = head.data; u8 i; if (ccs_str_starts(data, "Memory used by ")) for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) if (ccs_str_starts(data, ccs_memory_headers[i])) ccs_memory_quota[i] = strtoul(data, NULL, 10); return 0; } /** * ccs_parse_policy - Parse a policy line. * * @line: Line to parse. * * Returns 0 on success, negative value otherwise. */ static int ccs_parse_policy(char *line) { /* Delete request? */ head.is_delete = !strncmp(line, "delete ", 7); if (head.is_delete) memmove(line, line + 7, strlen(line + 7) + 1); /* Selecting namespace to update. */ if (head.type == CCS_EXCEPTIONPOLICY || head.type == CCS_PROFILE) { if (*line == '<') { char *cp = strchr(line, ' '); if (cp) { *cp++ = '\0'; head.ns = ccs_assign_namespace(line); memmove(line, cp, strlen(cp) + 1); } else head.ns = NULL; /* Don't allow updating if namespace is invalid. */ if (!head.ns) return -ENOENT; } else head.ns = &ccs_kernel_namespace; } /* Do the update. */ switch (head.type) { case CCS_DOMAINPOLICY: return ccs_write_domain(); case CCS_EXCEPTIONPOLICY: return ccs_write_exception(); case CCS_STAT: return ccs_write_stat(); case CCS_PROFILE: return ccs_write_profile(); case CCS_MANAGER: return ccs_write_manager(); default: return -ENOSYS; } } /** * ccs_write_control - write() for /sys/kernel/security/tomoyo/ interface. * * @buffer: Pointer to buffer to read from. * @buffer_len: Size of @buffer. * * Returns nothing. */ static void ccs_write_control(char *buffer, const size_t buffer_len) { static char *line = NULL; static int line_len = 0; size_t avail_len = buffer_len; while (avail_len > 0) { const char c = *buffer++; avail_len--; line = ccs_realloc(line, line_len + 1); line[line_len++] = c; if (c != '\n') continue; line[line_len - 1] = '\0'; line_len = 0; head.data = line; ccs_normalize_line(line); if (!strcmp(line, "reset")) { const u8 type = head.type; memset(&head, 0, sizeof(head)); head.reset = true; head.type = type; continue; } /* Don't allow updating policies by non manager programs. */ switch (head.type) { case CCS_DOMAINPOLICY: if (ccs_select_domain(line)) continue; /* fall through */ case CCS_EXCEPTIONPOLICY: if (!strcmp(line, "select transition_only")) { head.print_transition_related_only = true; continue; } } ccs_parse_policy(line); } } /** * editpolicy_offline_init - Initialize variables for offline daemon. * * Returns nothing. */ static void editpolicy_offline_init(void) { static _Bool first = true; int i; if (!first) return; first = false; memset(&head, 0, sizeof(head)); memset(&ccs_kernel_domain, 0, sizeof(ccs_kernel_domain)); memset(&ccs_kernel_namespace, 0, sizeof(ccs_kernel_namespace)); ccs_namespace_enabled = false; ccs_kernel_namespace.name = ""; for (i = 0; i < CCS_MAX_ACL_GROUPS; i++) INIT_LIST_HEAD(&ccs_kernel_namespace.acl_group[i]); list_add_tail(&ccs_kernel_namespace.namespace_list, &ccs_namespace_list); for (i = 0; i < CCS_MAX_HASH; i++) INIT_LIST_HEAD(&ccs_name_list[i]); INIT_LIST_HEAD(&ccs_kernel_domain.acl_info_list); ccs_kernel_domain.domainname = ccs_savename(""); list_add_tail(&ccs_kernel_domain.list, &ccs_domain_list); memset(ccs_memory_quota, 0, sizeof(ccs_memory_quota)); } /** * editpolicy_offline_main - Read request and handle policy I/O. * * @fd: Socket file descriptor. * * Returns nothing. */ static void editpolicy_offline_main(const int fd) { int i; static char buffer[4096]; editpolicy_offline_init(); /* Read filename. */ for (i = 0; i < sizeof(buffer); i++) { if (read(fd, buffer + i, 1) != 1) return; if (!buffer[i]) break; } if (!memchr(buffer, '\0', sizeof(buffer))) return; memset(&head, 0, sizeof(head)); head.reset = true; if (!strcmp(buffer, CCS_PROC_POLICY_DOMAIN_POLICY)) head.type = CCS_DOMAINPOLICY; else if (!strcmp(buffer, CCS_PROC_POLICY_EXCEPTION_POLICY)) head.type = CCS_EXCEPTIONPOLICY; else if (!strcmp(buffer, CCS_PROC_POLICY_PROFILE)) head.type = CCS_PROFILE; else if (!strcmp(buffer, CCS_PROC_POLICY_MANAGER)) head.type = CCS_MANAGER; else if (!strcmp(buffer, CCS_PROC_POLICY_STAT)) head.type = CCS_STAT; else return; /* Return \0 to indicate success. */ if (write(fd, "", 1) != 1) return; client_fd = fd; while (1) { struct pollfd pfd = { .fd = fd, .events = POLLIN }; int len; int nonzero_len; poll(&pfd, 1, -1); len = recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT); if (len <= 0) break; restart: for (nonzero_len = 0 ; nonzero_len < len; nonzero_len++) if (!buffer[nonzero_len]) break; if (nonzero_len) { ccs_write_control(buffer, nonzero_len); } else { switch (head.type) { case CCS_DOMAINPOLICY: ccs_read_domain(); break; case CCS_EXCEPTIONPOLICY: ccs_read_exception(); break; case CCS_STAT: ccs_read_stat(); break; case CCS_PROFILE: ccs_read_profile(); break; case CCS_MANAGER: ccs_read_manager(); break; } /* Flush data. */ cprintf("%s", ""); /* Return \0 to indicate EOF. */ if (write(fd, "", 1) != 1) return; nonzero_len = 1; } len -= nonzero_len; memmove(buffer, buffer + nonzero_len, len); if (len) goto restart; } } /** * editpolicy_offline_daemon - Emulate /sys/kernel/security/tomoyo/ interface. * * @listener: Listener fd. This is a listening PF_INET socket. * @notifier: Notifier fd. This is a pipe's reader side. * * This function does not return. */ void editpolicy_offline_daemon(const int listener, const int notifier) { while (1) { struct pollfd pfd[2] = { { .fd = listener, .events = POLLIN }, { .fd = notifier, .events = POLLIN } }; struct sockaddr_in addr; socklen_t size = sizeof(addr); int fd; if (poll(pfd, 2, -1) == EOF || (pfd[1].revents & (POLLIN | POLLHUP))) _exit(1); fd = accept(listener, (struct sockaddr *) &addr, &size); if (fd == EOF) continue; editpolicy_offline_main(fd); close(fd); } } tomoyo-tools/usr_sbin/tomoyo-domainmatch.c0000644000000000000000000000310714116520000020026 0ustar rootroot/* * tomoyo-domainmatch.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" int main(int argc, char *argv[]) { char buffer[16384]; _Bool flag = 0; static char *domain = NULL; FILE *fp; if (argc != 2) { printf("%s string_to_find\n\n", argv[0]); return 0; } ccs_mount_securityfs(); fp = fopen(CCS_PROC_POLICY_DOMAIN_POLICY, "r"); if (!fp) { fprintf(stderr, "You can't run this program for this kernel.\n"); return 0; } memset(buffer, 0, sizeof(buffer)); while (fgets(buffer, sizeof(buffer) - 1, fp)) { char *cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; if (ccs_domain_def(buffer)) { free(domain); domain = ccs_strdup(buffer); flag = 0; continue; } if (strstr(buffer, argv[1])) { if (!flag) printf("\n%s\n", domain); flag = 1; printf("%s\n", buffer); } } fclose(fp); free(domain); return 0; } tomoyo-tools/usr_sbin/tomoyo-patternize.c0000644000000000000000000003062614116520000017735 0ustar rootroot/* * tomoyo-patternize.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" /* * Check whether the given filename is patterened. * Returns nonzero if patterned, zero otherwise. */ static _Bool ccs_path_contains_pattern(const char *filename) { if (filename) { char c; char d; char e; while (true) { c = *filename++; if (!c) break; if (c != '\\') continue; c = *filename++; switch (c) { case '\\': /* "\\" */ continue; case '0': /* "\ooo" */ case '1': case '2': case '3': d = *filename++; if (d < '0' || d > '7') break; e = *filename++; if (e < '0' || e > '7') break; continue; } return true; } } return false; } #define CCS_PATTERNIZE_CONF "/etc/tomoyo/tools/patternize.conf" enum ccs_target_types { CCS_TARGET_DOMAIN, CCS_TARGET_ACL, CCS_REWRITE_PATH, CCS_REWRITE_HEAD, CCS_REWRITE_TAIL, CCS_REWRITE_NUMBER, CCS_REWRITE_ADDRESS, }; enum ccs_operator_types { CCS_TARGET_CONTAINS, CCS_TARGET_EQUALS, CCS_TARGET_STARTS, }; struct ccs_replace_rules { enum ccs_target_types type; union { /* Used by CCS_TARGET_{DOMAIN,ACL} */ struct { enum ccs_operator_types operation; unsigned int index; } cond; /* Used by CCS_REWRITE_NUMBER */ struct ccs_number_entry number; /* Used by CCS_REWRITE_ADDRESS */ struct ccs_ip_address_entry ip; /* Used by CCS_REWRITE_{PATH,HEAD,TAIL} */ struct ccs_path_info path; } u; const char *string; unsigned int string_len; /* strlen(string). */ }; static struct ccs_replace_rules *rules = NULL; static unsigned int rules_len = 0; static char *ccs_current_domainname = NULL; static char *ccs_current_acl = NULL; static _Bool ccs_head_pattern(char *string, const struct ccs_replace_rules *ptr) { char *pos; struct ccs_path_info subword; subword.name = string; for (pos = strrchr(string, '/'); pos >= string; pos--) { _Bool matched; char c; if (*pos != '/') continue; c = *(pos + 1); *(pos + 1) = '\0'; ccs_fill_path_info(&subword); matched = ccs_path_matches_pattern(&subword, &ptr->u.path); *(pos + 1) = c; if (!matched) continue; printf("%s%s", ptr->string, pos + 1); return true; } return false; } static _Bool ccs_tail_pattern(const char *string, const struct ccs_replace_rules *ptr) { const char *pos; struct ccs_path_info subword; int ret_ignored; for (pos = string; *pos; pos++) { if (*pos != '/') continue; subword.name = pos; ccs_fill_path_info(&subword); if (!ccs_path_matches_pattern(&subword, &ptr->u.path)) continue; ret_ignored = fwrite(string, 1, pos - string, stdout); printf("%s", ptr->string); return true; } return false; } static _Bool ccs_path_pattern(const char *string, const struct ccs_replace_rules *ptr) { struct ccs_path_info word; word.name = string; ccs_fill_path_info(&word); if (ccs_path_matches_pattern(&word, &ptr->u.path)) { printf("%s", ptr->string); return true; } return false; } static _Bool ccs_number_pattern(const char *string, const struct ccs_replace_rules *ptr) { struct ccs_number_entry entry; if (!ccs_parse_number(string, &entry) && ptr->u.number.min <= entry.min && ptr->u.number.max >= entry.max) { printf("%s", ptr->string); return true; } return false; } static _Bool ccs_address_pattern(const char *string, const struct ccs_replace_rules *ptr) { struct ccs_ip_address_entry entry; if (!ccs_parse_ip(string, &entry) && ptr->u.ip.is_ipv6 == entry.is_ipv6 && memcmp(entry.min, ptr->u.ip.min, 16) >= 0 && memcmp(ptr->u.ip.max, entry.max, 16) >= 0) { printf("%s", ptr->string); return true; } return false; } static _Bool ccs_check_rule(char *string, const enum ccs_target_types type) { unsigned int i; _Bool matched = true; if (*string == '@') return false; for (i = 0; i < rules_len; i++) { const struct ccs_replace_rules *ptr = &rules[i]; char *line = NULL; unsigned int index = ptr->u.cond.index; const char *find = ptr->string; unsigned int find_len = ptr->string_len; switch (ptr->type) { case CCS_TARGET_DOMAIN: line = ccs_current_domainname; break; case CCS_TARGET_ACL: line = ccs_current_acl; break; case CCS_REWRITE_PATH: if (type == CCS_REWRITE_PATH && matched && ccs_path_pattern(string, ptr)) return true; matched = true; continue; case CCS_REWRITE_HEAD: if (type == CCS_REWRITE_PATH && matched && ccs_head_pattern(string, ptr)) return true; matched = true; continue; case CCS_REWRITE_TAIL: if (type == CCS_REWRITE_PATH && matched && ccs_tail_pattern(string, ptr)) return true; matched = true; continue; case CCS_REWRITE_NUMBER: if (type == CCS_REWRITE_NUMBER && matched && ccs_number_pattern(string, ptr)) return true; matched = true; continue; case CCS_REWRITE_ADDRESS: if (type == CCS_REWRITE_ADDRESS && matched && ccs_address_pattern(string, ptr)) return true; matched = true; continue; } if (!matched || !line) continue; if (!index) { switch (ptr->u.cond.operation) { case CCS_TARGET_CONTAINS: while (1) { char *cp = strstr(line, find); if (!cp) { matched = false; break; } if ((cp == line || *(cp - 1) == ' ') && (!cp[find_len] || cp[find_len] == ' ')) break; line = cp + 1; } break; case CCS_TARGET_EQUALS: matched = !strcmp(line, find); break; case CCS_TARGET_STARTS: matched = !strncmp(line, find, find_len) && (!line[find_len] || line[find_len] == ' '); } } else { char *word = line; char *word_end; while (--index) { char *cp = strchr(word, ' '); if (!cp) { matched = false; break; } word = cp + 1; } if (!matched) continue; word_end = strchr(word, ' '); if (word_end) *word_end = '\0'; switch (ptr->u.cond.operation) { case CCS_TARGET_CONTAINS: matched = strstr(word, find) != NULL; break; case CCS_TARGET_EQUALS: matched = !strcmp(word, find); break; case CCS_TARGET_STARTS: matched = !strncmp(word, find, find_len); break; } if (word_end) *word_end = ' '; } } return false; } static void ccs_patternize_init_rules(const char *filename) { FILE *fp = fopen(filename, "r"); unsigned int line_no = 0; if (!fp) { fprintf(stderr, "Can't open %s for reading.\n", filename); exit(1); } ccs_get(); while (true) { struct ccs_replace_rules *ptr; char *line = ccs_freadline(fp); if (!line) break; line_no++; ccs_normalize_line(line); if (*line == '#' || !*line) continue; rules = ccs_realloc(rules, (rules_len + 1) * sizeof(*ptr)); ptr = &rules[rules_len++]; memset(ptr, 0, sizeof(*ptr)); if (ccs_str_starts(line, "rewrite ")) { char *cp = strchr(line, ' '); if (!cp) goto invalid_rule; cp = strchr(cp + 1, ' '); if (cp) *cp++ = '\0'; if (ccs_str_starts(line, "path_pattern ")) { ptr->type = CCS_REWRITE_PATH; } else if (ccs_str_starts(line, "head_pattern ")) { ptr->type = CCS_REWRITE_HEAD; if (!*line || line[strlen(line) - 1] != '/') goto invalid_rule; } else if (ccs_str_starts(line, "tail_pattern ")) { ptr->type = CCS_REWRITE_TAIL; if (*line != '/') goto invalid_rule; } else if (ccs_str_starts(line, "number_pattern ")) { if (ccs_parse_number(line, &ptr->u.number)) goto invalid_rule; ptr->type = CCS_REWRITE_NUMBER; } else if (ccs_str_starts(line, "address_pattern ")) { if (ccs_parse_ip(line, &ptr->u.ip)) goto invalid_rule; ptr->type = CCS_REWRITE_ADDRESS; } else { goto invalid_rule; } if (ptr->type != CCS_REWRITE_NUMBER && ptr->type != CCS_REWRITE_ADDRESS) { if (!*line) goto invalid_rule; if (!ccs_correct_word(line)) goto invalid_rule; line = ccs_strdup(line); ptr->u.path.name = line; ccs_fill_path_info(&ptr->u.path); } if (cp) line = cp; if (!ccs_correct_word(line)) goto invalid_rule; } else { unsigned char c; if (ccs_str_starts(line, "domain")) ptr->type = CCS_TARGET_DOMAIN; else if (ccs_str_starts(line, "acl")) ptr->type = CCS_TARGET_ACL; else goto invalid_rule; switch (sscanf(line, "[%u%c", &ptr->u.cond.index, &c)) { case 0: break; case 2: if (c == ']') { char *cp = strchr(line, ']') + 1; memmove(line, cp, strlen(cp) + 1); break; } default: goto invalid_rule; } if (ccs_str_starts(line, ".contains ")) ptr->u.cond.operation = CCS_TARGET_CONTAINS; else if (ccs_str_starts(line, ".equals ")) ptr->u.cond.operation = CCS_TARGET_EQUALS; else if (ccs_str_starts(line, ".starts ")) ptr->u.cond.operation = CCS_TARGET_STARTS; else goto invalid_rule; } if (!*line) goto invalid_rule; line = ccs_strdup(line); ptr->string = line; ptr->string_len = strlen(line); } ccs_put(); fclose(fp); if (!rules_len) { fprintf(stderr, "No rules defined in %s .\n", filename); exit(1); } return; invalid_rule: fprintf(stderr, "Invalid rule at line %u in %s .\n", line_no, filename); exit(1); } static void ccs_process_line(char *sp) { char *cp; _Bool first = true; u8 path_count = 0; u8 number_count = 0; u8 address_count = 0; u8 skip_count = 0; while (true) { cp = strsep(&sp, " "); if (!cp) break; if (first) { if (!strcmp(cp, "network")) { printf("network "); cp = strsep(&sp, " "); if (!cp) break; if (strstr(cp, "unix")) { path_count = 1; } else if (strstr(cp, "inet")) { address_count = 1; number_count = 1; } else { break; } skip_count = 2; } else if (!strcmp(cp, "file")) { printf("file "); cp = strsep(&sp, " "); if (!cp) break; if (strstr(cp, "execute") || strstr(cp, "read") || strstr(cp, "getattr") || strstr(cp, "write") || strstr(cp, "append") || strstr(cp, "unlink") || strstr(cp, "rmdir") || strstr(cp, "truncate") || strstr(cp, "symlink") || strstr(cp, "chroot") || strstr(cp, "unmount")) { path_count = 1; } else if (strstr(cp, "link") || strstr(cp, "rename") || strstr(cp, "pivot_root")) { path_count = 2; } else if (strstr(cp, "create") || strstr(cp, "mkdir") || strstr(cp, "mkfifo") || strstr(cp, "mksock") || strstr(cp, "ioctl") || strstr(cp, "chmod") || strstr(cp, "chown") || strstr(cp, "chgrp")) { path_count = 1; number_count = 1; } else if (strstr(cp, "mkblock") || strstr(cp, "mkchar")) { path_count = 1; number_count = 3; } else if (!strcmp(cp, "mount")) { path_count = 3; number_count = 1; } } printf("%s", cp); first = false; continue; } putchar(' '); if (skip_count) { skip_count--; } else if (path_count) { path_count--; if (!ccs_path_contains_pattern(cp) && ccs_check_rule(cp, CCS_REWRITE_PATH)) continue; } else if (address_count) { address_count--; if (ccs_check_rule(cp, CCS_REWRITE_ADDRESS)) continue; } else if (number_count) { number_count--; if (ccs_check_rule(cp, CCS_REWRITE_NUMBER)) continue; } printf("%s", cp); } putchar('\n'); } int main(int argc, char *argv[]) { ccs_patternize_init_rules(argc == 2 ? argv[1] : CCS_PATTERNIZE_CONF); ccs_get(); while (true) { char *sp = ccs_freadline_unpack(stdin); if (!sp) break; if (ccs_domain_def(sp)) { free(ccs_current_domainname); ccs_current_domainname = strdup(sp); printf("%s\n", sp); continue; } free(ccs_current_acl); ccs_current_acl = strdup(sp); if (!ccs_current_domainname || !ccs_current_acl) { /* Continue without conversion. */ printf("%s\n", sp); continue; } ccs_process_line(sp); } ccs_put(); return 0; } tomoyo-tools/usr_sbin/editpolicy_keyword.c0000644000000000000000000005164114116520000020135 0ustar rootroot/* * editpolicy_keyword.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" #include "editpolicy.h" /* keyword array for rewriting keywords upon display. */ struct editpolicy_directive directive_map[MAX_DIRECTIVE_TYPE] = { [DIRECTIVE_ACL_GROUP_000] = { "acl_group 0", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_001] = { "acl_group 1", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_002] = { "acl_group 2", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_003] = { "acl_group 3", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_004] = { "acl_group 4", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_005] = { "acl_group 5", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_006] = { "acl_group 6", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_007] = { "acl_group 7", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_008] = { "acl_group 8", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_009] = { "acl_group 9", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_010] = { "acl_group 10", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_011] = { "acl_group 11", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_012] = { "acl_group 12", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_013] = { "acl_group 13", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_014] = { "acl_group 14", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_015] = { "acl_group 15", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_016] = { "acl_group 16", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_017] = { "acl_group 17", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_018] = { "acl_group 18", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_019] = { "acl_group 19", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_020] = { "acl_group 20", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_021] = { "acl_group 21", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_022] = { "acl_group 22", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_023] = { "acl_group 23", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_024] = { "acl_group 24", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_025] = { "acl_group 25", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_026] = { "acl_group 26", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_027] = { "acl_group 27", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_028] = { "acl_group 28", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_029] = { "acl_group 29", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_030] = { "acl_group 30", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_031] = { "acl_group 31", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_032] = { "acl_group 32", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_033] = { "acl_group 33", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_034] = { "acl_group 34", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_035] = { "acl_group 35", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_036] = { "acl_group 36", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_037] = { "acl_group 37", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_038] = { "acl_group 38", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_039] = { "acl_group 39", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_040] = { "acl_group 40", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_041] = { "acl_group 41", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_042] = { "acl_group 42", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_043] = { "acl_group 43", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_044] = { "acl_group 44", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_045] = { "acl_group 45", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_046] = { "acl_group 46", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_047] = { "acl_group 47", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_048] = { "acl_group 48", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_049] = { "acl_group 49", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_050] = { "acl_group 50", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_051] = { "acl_group 51", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_052] = { "acl_group 52", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_053] = { "acl_group 53", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_054] = { "acl_group 54", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_055] = { "acl_group 55", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_056] = { "acl_group 56", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_057] = { "acl_group 57", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_058] = { "acl_group 58", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_059] = { "acl_group 59", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_060] = { "acl_group 60", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_061] = { "acl_group 61", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_062] = { "acl_group 62", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_063] = { "acl_group 63", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_064] = { "acl_group 64", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_065] = { "acl_group 65", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_066] = { "acl_group 66", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_067] = { "acl_group 67", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_068] = { "acl_group 68", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_069] = { "acl_group 69", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_070] = { "acl_group 70", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_071] = { "acl_group 71", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_072] = { "acl_group 72", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_073] = { "acl_group 73", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_074] = { "acl_group 74", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_075] = { "acl_group 75", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_076] = { "acl_group 76", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_077] = { "acl_group 77", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_078] = { "acl_group 78", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_079] = { "acl_group 79", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_080] = { "acl_group 80", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_081] = { "acl_group 81", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_082] = { "acl_group 82", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_083] = { "acl_group 83", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_084] = { "acl_group 84", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_085] = { "acl_group 85", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_086] = { "acl_group 86", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_087] = { "acl_group 87", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_088] = { "acl_group 88", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_089] = { "acl_group 89", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_090] = { "acl_group 90", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_091] = { "acl_group 91", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_092] = { "acl_group 92", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_093] = { "acl_group 93", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_094] = { "acl_group 94", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_095] = { "acl_group 95", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_096] = { "acl_group 96", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_097] = { "acl_group 97", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_098] = { "acl_group 98", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_099] = { "acl_group 99", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_100] = { "acl_group 100", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_101] = { "acl_group 101", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_102] = { "acl_group 102", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_103] = { "acl_group 103", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_104] = { "acl_group 104", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_105] = { "acl_group 105", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_106] = { "acl_group 106", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_107] = { "acl_group 107", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_108] = { "acl_group 108", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_109] = { "acl_group 109", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_110] = { "acl_group 110", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_111] = { "acl_group 111", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_112] = { "acl_group 112", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_113] = { "acl_group 113", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_114] = { "acl_group 114", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_115] = { "acl_group 115", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_116] = { "acl_group 116", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_117] = { "acl_group 117", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_118] = { "acl_group 118", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_119] = { "acl_group 119", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_120] = { "acl_group 120", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_121] = { "acl_group 121", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_122] = { "acl_group 122", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_123] = { "acl_group 123", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_124] = { "acl_group 124", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_125] = { "acl_group 125", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_126] = { "acl_group 126", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_127] = { "acl_group 127", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_128] = { "acl_group 128", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_129] = { "acl_group 129", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_130] = { "acl_group 130", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_131] = { "acl_group 131", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_132] = { "acl_group 132", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_133] = { "acl_group 133", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_134] = { "acl_group 134", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_135] = { "acl_group 135", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_136] = { "acl_group 136", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_137] = { "acl_group 137", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_138] = { "acl_group 138", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_139] = { "acl_group 139", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_140] = { "acl_group 140", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_141] = { "acl_group 141", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_142] = { "acl_group 142", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_143] = { "acl_group 143", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_144] = { "acl_group 144", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_145] = { "acl_group 145", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_146] = { "acl_group 146", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_147] = { "acl_group 147", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_148] = { "acl_group 148", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_149] = { "acl_group 149", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_150] = { "acl_group 150", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_151] = { "acl_group 151", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_152] = { "acl_group 152", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_153] = { "acl_group 153", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_154] = { "acl_group 154", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_155] = { "acl_group 155", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_156] = { "acl_group 156", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_157] = { "acl_group 157", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_158] = { "acl_group 158", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_159] = { "acl_group 159", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_160] = { "acl_group 160", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_161] = { "acl_group 161", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_162] = { "acl_group 162", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_163] = { "acl_group 163", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_164] = { "acl_group 164", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_165] = { "acl_group 165", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_166] = { "acl_group 166", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_167] = { "acl_group 167", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_168] = { "acl_group 168", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_169] = { "acl_group 169", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_170] = { "acl_group 170", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_171] = { "acl_group 171", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_172] = { "acl_group 172", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_173] = { "acl_group 173", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_174] = { "acl_group 174", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_175] = { "acl_group 175", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_176] = { "acl_group 176", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_177] = { "acl_group 177", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_178] = { "acl_group 178", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_179] = { "acl_group 179", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_180] = { "acl_group 180", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_181] = { "acl_group 181", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_182] = { "acl_group 182", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_183] = { "acl_group 183", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_184] = { "acl_group 184", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_185] = { "acl_group 185", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_186] = { "acl_group 186", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_187] = { "acl_group 187", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_188] = { "acl_group 188", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_189] = { "acl_group 189", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_190] = { "acl_group 190", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_191] = { "acl_group 191", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_192] = { "acl_group 192", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_193] = { "acl_group 193", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_194] = { "acl_group 194", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_195] = { "acl_group 195", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_196] = { "acl_group 196", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_197] = { "acl_group 197", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_198] = { "acl_group 198", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_199] = { "acl_group 199", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_200] = { "acl_group 200", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_201] = { "acl_group 201", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_202] = { "acl_group 202", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_203] = { "acl_group 203", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_204] = { "acl_group 204", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_205] = { "acl_group 205", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_206] = { "acl_group 206", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_207] = { "acl_group 207", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_208] = { "acl_group 208", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_209] = { "acl_group 209", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_210] = { "acl_group 210", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_211] = { "acl_group 211", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_212] = { "acl_group 212", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_213] = { "acl_group 213", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_214] = { "acl_group 214", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_215] = { "acl_group 215", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_216] = { "acl_group 216", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_217] = { "acl_group 217", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_218] = { "acl_group 218", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_219] = { "acl_group 219", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_220] = { "acl_group 220", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_221] = { "acl_group 221", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_222] = { "acl_group 222", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_223] = { "acl_group 223", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_224] = { "acl_group 224", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_225] = { "acl_group 225", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_226] = { "acl_group 226", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_227] = { "acl_group 227", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_228] = { "acl_group 228", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_229] = { "acl_group 229", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_230] = { "acl_group 230", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_231] = { "acl_group 231", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_232] = { "acl_group 232", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_233] = { "acl_group 233", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_234] = { "acl_group 234", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_235] = { "acl_group 235", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_236] = { "acl_group 236", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_237] = { "acl_group 237", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_238] = { "acl_group 238", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_239] = { "acl_group 239", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_240] = { "acl_group 240", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_241] = { "acl_group 241", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_242] = { "acl_group 242", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_243] = { "acl_group 243", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_244] = { "acl_group 244", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_245] = { "acl_group 245", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_246] = { "acl_group 246", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_247] = { "acl_group 247", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_248] = { "acl_group 248", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_249] = { "acl_group 249", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_250] = { "acl_group 250", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_251] = { "acl_group 251", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_252] = { "acl_group 252", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_253] = { "acl_group 253", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_254] = { "acl_group 254", NULL, 0, 0 }, [DIRECTIVE_ACL_GROUP_255] = { "acl_group 255", NULL, 0, 0 }, [DIRECTIVE_ADDRESS_GROUP] = { "address_group", NULL, 0, 0 }, [DIRECTIVE_AGGREGATOR] = { "aggregator", NULL, 0, 0 }, [DIRECTIVE_FILE_APPEND] = { "file append", NULL, 0, 0 }, [DIRECTIVE_FILE_CHGRP] = { "file chgrp", NULL, 0, 0 }, [DIRECTIVE_FILE_CHMOD] = { "file chmod", NULL, 0, 0 }, [DIRECTIVE_FILE_CHOWN] = { "file chown", NULL, 0, 0 }, [DIRECTIVE_FILE_CHROOT] = { "file chroot", NULL, 0, 0 }, [DIRECTIVE_FILE_CREATE] = { "file create", NULL, 0, 0 }, [DIRECTIVE_FILE_EXECUTE] = { "file execute", NULL, 0, 0 }, [DIRECTIVE_FILE_GETATTR] = { "file getattr", NULL, 0, 0 }, [DIRECTIVE_FILE_IOCTL] = { "file ioctl", NULL, 0, 0 }, [DIRECTIVE_FILE_LINK] = { "file link", NULL, 0, 0 }, [DIRECTIVE_FILE_MKBLOCK] = { "file mkblock", NULL, 0, 0 }, [DIRECTIVE_FILE_MKCHAR] = { "file mkchar", NULL, 0, 0 }, [DIRECTIVE_FILE_MKDIR] = { "file mkdir", NULL, 0, 0 }, [DIRECTIVE_FILE_MKFIFO] = { "file mkfifo", NULL, 0, 0 }, [DIRECTIVE_FILE_MKSOCK] = { "file mksock", NULL, 0, 0 }, [DIRECTIVE_FILE_MOUNT] = { "file mount", NULL, 0, 0 }, [DIRECTIVE_FILE_PIVOT_ROOT] = { "file pivot_root", NULL, 0, 0 }, [DIRECTIVE_FILE_READ] = { "file read", NULL, 0, 0 }, [DIRECTIVE_FILE_RENAME] = { "file rename", NULL, 0, 0 }, [DIRECTIVE_FILE_RMDIR] = { "file rmdir", NULL, 0, 0 }, [DIRECTIVE_FILE_SYMLINK] = { "file symlink", NULL, 0, 0 }, [DIRECTIVE_FILE_TRUNCATE] = { "file truncate", NULL, 0, 0 }, [DIRECTIVE_FILE_UNLINK] = { "file unlink", NULL, 0, 0 }, [DIRECTIVE_FILE_UNMOUNT] = { "file unmount", NULL, 0, 0 }, [DIRECTIVE_FILE_WRITE] = { "file write", NULL, 0, 0 }, [DIRECTIVE_INITIALIZE_DOMAIN] = { "initialize_domain", NULL, 0, 0 }, [DIRECTIVE_KEEP_DOMAIN] = { "keep_domain", NULL, 0, 0 }, [DIRECTIVE_MISC_ENV] = { "misc env", NULL, 0, 0 }, [DIRECTIVE_NETWORK_INET] = { "network inet", NULL, 0, 0 }, [DIRECTIVE_NETWORK_UNIX] = { "network unix", NULL, 0, 0 }, [DIRECTIVE_NONE] = { "", NULL, 0, 0 }, [DIRECTIVE_NO_INITIALIZE_DOMAIN] = { "no_initialize_domain", NULL, 0, 0 }, [DIRECTIVE_NO_KEEP_DOMAIN] = { "no_keep_domain", NULL, 0, 0 }, [DIRECTIVE_NO_RESET_DOMAIN] = { "no_reset_domain", NULL, 0, 0 }, [DIRECTIVE_NUMBER_GROUP] = { "number_group", NULL, 0, 0 }, [DIRECTIVE_PATH_GROUP] = { "path_group", NULL, 0, 0 }, [DIRECTIVE_QUOTA_EXCEEDED] = { "quota_exceeded", NULL, 0, 0 }, [DIRECTIVE_RESET_DOMAIN] = { "reset_domain", NULL, 0, 0 }, [DIRECTIVE_TASK_MANUAL_DOMAIN_TRANSITION] = { "task manual_domain_transition", NULL, 0, 0 }, [DIRECTIVE_TRANSITION_FAILED] = { "transition_failed", NULL, 0, 0 }, [DIRECTIVE_USE_GROUP] = { "use_group", NULL, 0, 0 }, [DIRECTIVE_USE_PROFILE] = { "use_profile", NULL, 0, 0 }, }; /** * find_directive - Find keyword index. * * @forward: True if original -> alias conversion, false otherwise. * @line: A line containing keyword and operand. * * Returns one of values in "enum directive_type". */ enum directive_type find_directive(const _Bool forward, char *line) { enum directive_type i; for (i = 1; i < MAX_DIRECTIVE_TYPE; i++) { int len; if (forward) { len = directive_map[i].original_len; if (strncmp(line, directive_map[i].original, len) || (line[len] != ' ' && line[len])) continue; } else { len = directive_map[i].alias_len; if (strncmp(line, directive_map[i].alias, len) || (line[len] != ' ' && line[len])) continue; } if (line[len]) memmove(line, line + len + 1, strlen(line + len + 1) + 1); else line[0] = '\0'; return i; } return DIRECTIVE_NONE; } /** * editpolicy_init_keyword_map - Initialize keyword mapping table. * * Returns nothing. */ void editpolicy_init_keyword_map(void) { FILE *fp = fopen(CCS_EDITPOLICY_CONF, "r"); int i; if (!fp) goto use_default; ccs_get(); while (true) { char *line = ccs_freadline(fp); char *cp; if (!line) break; if (!ccs_str_starts(line, "keyword_alias ")) continue; cp = strchr(line, '='); if (!cp) continue; *cp++ = '\0'; ccs_normalize_line(line); ccs_normalize_line(cp); if (!*line || !*cp) continue; for (i = 1; i < MAX_DIRECTIVE_TYPE; i++) { if (strcmp(line, directive_map[i].original)) continue; free((void *) directive_map[i].alias); cp = ccs_strdup(cp); directive_map[i].alias = cp; directive_map[i].alias_len = strlen(cp); break; } } ccs_put(); fclose(fp); use_default: for (i = 1; i < MAX_DIRECTIVE_TYPE; i++) { if (!directive_map[i].alias) directive_map[i].alias = directive_map[i].original; directive_map[i].original_len = strlen(directive_map[i].original); directive_map[i].alias_len = strlen(directive_map[i].alias); } } tomoyo-tools/usr_sbin/editpolicy.h0000644000000000000000000003126714116520000016400 0ustar rootroot/* * editpolicy.h * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include enum screen_type { SCREEN_EXCEPTION_LIST, SCREEN_DOMAIN_LIST, SCREEN_ACL_LIST, SCREEN_PROFILE_LIST, SCREEN_MANAGER_LIST, /* SCREEN_QUERY_LIST, */ SCREEN_NS_LIST, SCREEN_STAT_LIST, MAX_SCREEN_TYPE }; enum transition_type { /* Do not change this order, */ TRANSITION_NO_RESET, TRANSITION_RESET, TRANSITION_NO_INITIALIZE, TRANSITION_INITIALIZE, TRANSITION_NO_KEEP, TRANSITION_KEEP, MAX_TRANSITION_TYPE }; enum directive_type { DIRECTIVE_NONE, DIRECTIVE_ACL_GROUP_000, DIRECTIVE_ACL_GROUP_001, DIRECTIVE_ACL_GROUP_002, DIRECTIVE_ACL_GROUP_003, DIRECTIVE_ACL_GROUP_004, DIRECTIVE_ACL_GROUP_005, DIRECTIVE_ACL_GROUP_006, DIRECTIVE_ACL_GROUP_007, DIRECTIVE_ACL_GROUP_008, DIRECTIVE_ACL_GROUP_009, DIRECTIVE_ACL_GROUP_010, DIRECTIVE_ACL_GROUP_011, DIRECTIVE_ACL_GROUP_012, DIRECTIVE_ACL_GROUP_013, DIRECTIVE_ACL_GROUP_014, DIRECTIVE_ACL_GROUP_015, DIRECTIVE_ACL_GROUP_016, DIRECTIVE_ACL_GROUP_017, DIRECTIVE_ACL_GROUP_018, DIRECTIVE_ACL_GROUP_019, DIRECTIVE_ACL_GROUP_020, DIRECTIVE_ACL_GROUP_021, DIRECTIVE_ACL_GROUP_022, DIRECTIVE_ACL_GROUP_023, DIRECTIVE_ACL_GROUP_024, DIRECTIVE_ACL_GROUP_025, DIRECTIVE_ACL_GROUP_026, DIRECTIVE_ACL_GROUP_027, DIRECTIVE_ACL_GROUP_028, DIRECTIVE_ACL_GROUP_029, DIRECTIVE_ACL_GROUP_030, DIRECTIVE_ACL_GROUP_031, DIRECTIVE_ACL_GROUP_032, DIRECTIVE_ACL_GROUP_033, DIRECTIVE_ACL_GROUP_034, DIRECTIVE_ACL_GROUP_035, DIRECTIVE_ACL_GROUP_036, DIRECTIVE_ACL_GROUP_037, DIRECTIVE_ACL_GROUP_038, DIRECTIVE_ACL_GROUP_039, DIRECTIVE_ACL_GROUP_040, DIRECTIVE_ACL_GROUP_041, DIRECTIVE_ACL_GROUP_042, DIRECTIVE_ACL_GROUP_043, DIRECTIVE_ACL_GROUP_044, DIRECTIVE_ACL_GROUP_045, DIRECTIVE_ACL_GROUP_046, DIRECTIVE_ACL_GROUP_047, DIRECTIVE_ACL_GROUP_048, DIRECTIVE_ACL_GROUP_049, DIRECTIVE_ACL_GROUP_050, DIRECTIVE_ACL_GROUP_051, DIRECTIVE_ACL_GROUP_052, DIRECTIVE_ACL_GROUP_053, DIRECTIVE_ACL_GROUP_054, DIRECTIVE_ACL_GROUP_055, DIRECTIVE_ACL_GROUP_056, DIRECTIVE_ACL_GROUP_057, DIRECTIVE_ACL_GROUP_058, DIRECTIVE_ACL_GROUP_059, DIRECTIVE_ACL_GROUP_060, DIRECTIVE_ACL_GROUP_061, DIRECTIVE_ACL_GROUP_062, DIRECTIVE_ACL_GROUP_063, DIRECTIVE_ACL_GROUP_064, DIRECTIVE_ACL_GROUP_065, DIRECTIVE_ACL_GROUP_066, DIRECTIVE_ACL_GROUP_067, DIRECTIVE_ACL_GROUP_068, DIRECTIVE_ACL_GROUP_069, DIRECTIVE_ACL_GROUP_070, DIRECTIVE_ACL_GROUP_071, DIRECTIVE_ACL_GROUP_072, DIRECTIVE_ACL_GROUP_073, DIRECTIVE_ACL_GROUP_074, DIRECTIVE_ACL_GROUP_075, DIRECTIVE_ACL_GROUP_076, DIRECTIVE_ACL_GROUP_077, DIRECTIVE_ACL_GROUP_078, DIRECTIVE_ACL_GROUP_079, DIRECTIVE_ACL_GROUP_080, DIRECTIVE_ACL_GROUP_081, DIRECTIVE_ACL_GROUP_082, DIRECTIVE_ACL_GROUP_083, DIRECTIVE_ACL_GROUP_084, DIRECTIVE_ACL_GROUP_085, DIRECTIVE_ACL_GROUP_086, DIRECTIVE_ACL_GROUP_087, DIRECTIVE_ACL_GROUP_088, DIRECTIVE_ACL_GROUP_089, DIRECTIVE_ACL_GROUP_090, DIRECTIVE_ACL_GROUP_091, DIRECTIVE_ACL_GROUP_092, DIRECTIVE_ACL_GROUP_093, DIRECTIVE_ACL_GROUP_094, DIRECTIVE_ACL_GROUP_095, DIRECTIVE_ACL_GROUP_096, DIRECTIVE_ACL_GROUP_097, DIRECTIVE_ACL_GROUP_098, DIRECTIVE_ACL_GROUP_099, DIRECTIVE_ACL_GROUP_100, DIRECTIVE_ACL_GROUP_101, DIRECTIVE_ACL_GROUP_102, DIRECTIVE_ACL_GROUP_103, DIRECTIVE_ACL_GROUP_104, DIRECTIVE_ACL_GROUP_105, DIRECTIVE_ACL_GROUP_106, DIRECTIVE_ACL_GROUP_107, DIRECTIVE_ACL_GROUP_108, DIRECTIVE_ACL_GROUP_109, DIRECTIVE_ACL_GROUP_110, DIRECTIVE_ACL_GROUP_111, DIRECTIVE_ACL_GROUP_112, DIRECTIVE_ACL_GROUP_113, DIRECTIVE_ACL_GROUP_114, DIRECTIVE_ACL_GROUP_115, DIRECTIVE_ACL_GROUP_116, DIRECTIVE_ACL_GROUP_117, DIRECTIVE_ACL_GROUP_118, DIRECTIVE_ACL_GROUP_119, DIRECTIVE_ACL_GROUP_120, DIRECTIVE_ACL_GROUP_121, DIRECTIVE_ACL_GROUP_122, DIRECTIVE_ACL_GROUP_123, DIRECTIVE_ACL_GROUP_124, DIRECTIVE_ACL_GROUP_125, DIRECTIVE_ACL_GROUP_126, DIRECTIVE_ACL_GROUP_127, DIRECTIVE_ACL_GROUP_128, DIRECTIVE_ACL_GROUP_129, DIRECTIVE_ACL_GROUP_130, DIRECTIVE_ACL_GROUP_131, DIRECTIVE_ACL_GROUP_132, DIRECTIVE_ACL_GROUP_133, DIRECTIVE_ACL_GROUP_134, DIRECTIVE_ACL_GROUP_135, DIRECTIVE_ACL_GROUP_136, DIRECTIVE_ACL_GROUP_137, DIRECTIVE_ACL_GROUP_138, DIRECTIVE_ACL_GROUP_139, DIRECTIVE_ACL_GROUP_140, DIRECTIVE_ACL_GROUP_141, DIRECTIVE_ACL_GROUP_142, DIRECTIVE_ACL_GROUP_143, DIRECTIVE_ACL_GROUP_144, DIRECTIVE_ACL_GROUP_145, DIRECTIVE_ACL_GROUP_146, DIRECTIVE_ACL_GROUP_147, DIRECTIVE_ACL_GROUP_148, DIRECTIVE_ACL_GROUP_149, DIRECTIVE_ACL_GROUP_150, DIRECTIVE_ACL_GROUP_151, DIRECTIVE_ACL_GROUP_152, DIRECTIVE_ACL_GROUP_153, DIRECTIVE_ACL_GROUP_154, DIRECTIVE_ACL_GROUP_155, DIRECTIVE_ACL_GROUP_156, DIRECTIVE_ACL_GROUP_157, DIRECTIVE_ACL_GROUP_158, DIRECTIVE_ACL_GROUP_159, DIRECTIVE_ACL_GROUP_160, DIRECTIVE_ACL_GROUP_161, DIRECTIVE_ACL_GROUP_162, DIRECTIVE_ACL_GROUP_163, DIRECTIVE_ACL_GROUP_164, DIRECTIVE_ACL_GROUP_165, DIRECTIVE_ACL_GROUP_166, DIRECTIVE_ACL_GROUP_167, DIRECTIVE_ACL_GROUP_168, DIRECTIVE_ACL_GROUP_169, DIRECTIVE_ACL_GROUP_170, DIRECTIVE_ACL_GROUP_171, DIRECTIVE_ACL_GROUP_172, DIRECTIVE_ACL_GROUP_173, DIRECTIVE_ACL_GROUP_174, DIRECTIVE_ACL_GROUP_175, DIRECTIVE_ACL_GROUP_176, DIRECTIVE_ACL_GROUP_177, DIRECTIVE_ACL_GROUP_178, DIRECTIVE_ACL_GROUP_179, DIRECTIVE_ACL_GROUP_180, DIRECTIVE_ACL_GROUP_181, DIRECTIVE_ACL_GROUP_182, DIRECTIVE_ACL_GROUP_183, DIRECTIVE_ACL_GROUP_184, DIRECTIVE_ACL_GROUP_185, DIRECTIVE_ACL_GROUP_186, DIRECTIVE_ACL_GROUP_187, DIRECTIVE_ACL_GROUP_188, DIRECTIVE_ACL_GROUP_189, DIRECTIVE_ACL_GROUP_190, DIRECTIVE_ACL_GROUP_191, DIRECTIVE_ACL_GROUP_192, DIRECTIVE_ACL_GROUP_193, DIRECTIVE_ACL_GROUP_194, DIRECTIVE_ACL_GROUP_195, DIRECTIVE_ACL_GROUP_196, DIRECTIVE_ACL_GROUP_197, DIRECTIVE_ACL_GROUP_198, DIRECTIVE_ACL_GROUP_199, DIRECTIVE_ACL_GROUP_200, DIRECTIVE_ACL_GROUP_201, DIRECTIVE_ACL_GROUP_202, DIRECTIVE_ACL_GROUP_203, DIRECTIVE_ACL_GROUP_204, DIRECTIVE_ACL_GROUP_205, DIRECTIVE_ACL_GROUP_206, DIRECTIVE_ACL_GROUP_207, DIRECTIVE_ACL_GROUP_208, DIRECTIVE_ACL_GROUP_209, DIRECTIVE_ACL_GROUP_210, DIRECTIVE_ACL_GROUP_211, DIRECTIVE_ACL_GROUP_212, DIRECTIVE_ACL_GROUP_213, DIRECTIVE_ACL_GROUP_214, DIRECTIVE_ACL_GROUP_215, DIRECTIVE_ACL_GROUP_216, DIRECTIVE_ACL_GROUP_217, DIRECTIVE_ACL_GROUP_218, DIRECTIVE_ACL_GROUP_219, DIRECTIVE_ACL_GROUP_220, DIRECTIVE_ACL_GROUP_221, DIRECTIVE_ACL_GROUP_222, DIRECTIVE_ACL_GROUP_223, DIRECTIVE_ACL_GROUP_224, DIRECTIVE_ACL_GROUP_225, DIRECTIVE_ACL_GROUP_226, DIRECTIVE_ACL_GROUP_227, DIRECTIVE_ACL_GROUP_228, DIRECTIVE_ACL_GROUP_229, DIRECTIVE_ACL_GROUP_230, DIRECTIVE_ACL_GROUP_231, DIRECTIVE_ACL_GROUP_232, DIRECTIVE_ACL_GROUP_233, DIRECTIVE_ACL_GROUP_234, DIRECTIVE_ACL_GROUP_235, DIRECTIVE_ACL_GROUP_236, DIRECTIVE_ACL_GROUP_237, DIRECTIVE_ACL_GROUP_238, DIRECTIVE_ACL_GROUP_239, DIRECTIVE_ACL_GROUP_240, DIRECTIVE_ACL_GROUP_241, DIRECTIVE_ACL_GROUP_242, DIRECTIVE_ACL_GROUP_243, DIRECTIVE_ACL_GROUP_244, DIRECTIVE_ACL_GROUP_245, DIRECTIVE_ACL_GROUP_246, DIRECTIVE_ACL_GROUP_247, DIRECTIVE_ACL_GROUP_248, DIRECTIVE_ACL_GROUP_249, DIRECTIVE_ACL_GROUP_250, DIRECTIVE_ACL_GROUP_251, DIRECTIVE_ACL_GROUP_252, DIRECTIVE_ACL_GROUP_253, DIRECTIVE_ACL_GROUP_254, DIRECTIVE_ACL_GROUP_255, DIRECTIVE_ADDRESS_GROUP, DIRECTIVE_AGGREGATOR, DIRECTIVE_FILE_APPEND, DIRECTIVE_FILE_CHGRP, DIRECTIVE_FILE_CHMOD, DIRECTIVE_FILE_CHOWN, DIRECTIVE_FILE_CHROOT, DIRECTIVE_FILE_CREATE, DIRECTIVE_FILE_EXECUTE, DIRECTIVE_FILE_GETATTR, DIRECTIVE_FILE_IOCTL, DIRECTIVE_FILE_LINK, DIRECTIVE_FILE_MKBLOCK, DIRECTIVE_FILE_MKCHAR, DIRECTIVE_FILE_MKDIR, DIRECTIVE_FILE_MKFIFO, DIRECTIVE_FILE_MKSOCK, DIRECTIVE_FILE_MOUNT, DIRECTIVE_FILE_PIVOT_ROOT, DIRECTIVE_FILE_READ, DIRECTIVE_FILE_RENAME, DIRECTIVE_FILE_RMDIR, DIRECTIVE_FILE_SYMLINK, DIRECTIVE_FILE_TRUNCATE, DIRECTIVE_FILE_UNLINK, DIRECTIVE_FILE_UNMOUNT, DIRECTIVE_FILE_WRITE, DIRECTIVE_INITIALIZE_DOMAIN, DIRECTIVE_KEEP_DOMAIN, DIRECTIVE_MISC_ENV, DIRECTIVE_NETWORK_INET, DIRECTIVE_NETWORK_UNIX, DIRECTIVE_NO_INITIALIZE_DOMAIN, DIRECTIVE_NO_KEEP_DOMAIN, DIRECTIVE_NO_RESET_DOMAIN, DIRECTIVE_NUMBER_GROUP, DIRECTIVE_PATH_GROUP, DIRECTIVE_QUOTA_EXCEEDED, DIRECTIVE_RESET_DOMAIN, DIRECTIVE_TASK_MANUAL_DOMAIN_TRANSITION, DIRECTIVE_TRANSITION_FAILED, DIRECTIVE_USE_GROUP, DIRECTIVE_USE_PROFILE, MAX_DIRECTIVE_TYPE }; enum color_type { COLOR_NORMAL, COLOR_DOMAIN_HEAD, COLOR_DOMAIN_CURSOR, COLOR_EXCEPTION_HEAD, COLOR_EXCEPTION_CURSOR, COLOR_ACL_HEAD, COLOR_ACL_CURSOR, COLOR_PROFILE_HEAD, COLOR_PROFILE_CURSOR, COLOR_MANAGER_HEAD, COLOR_MANAGER_CURSOR, COLOR_STAT_HEAD, COLOR_STAT_CURSOR, COLOR_DEFAULT_COLOR, COLOR_DISP_ERR }; struct transition_entry { const struct ccs_path_info *ns; const struct ccs_path_info *domainname; /* This may be NULL */ const struct ccs_path_info *program; /* This may be NULL */ enum transition_type type; }; struct transition_preference { int index; char *program; char *domainname; }; struct generic_entry { enum directive_type directive; _Bool selected; const char *operand; }; struct editpolicy_directive { const char *original; const char *alias; int original_len; int alias_len; }; struct path_group { const struct ccs_path_info *ns; const struct ccs_path_info *group_name; const struct ccs_path_info **member_name; int member_name_len; }; struct address_group { const struct ccs_path_info *group_name; struct ccs_ip_address_entry *member_name; int member_name_len; }; struct number_group { const struct ccs_path_info *group_name; struct ccs_number_entry *member_name; int member_name_len; }; struct ccs_readline_data { const char **history; int count; int max; char *search_buffer[MAX_SCREEN_TYPE]; }; struct ccs_screen { /* Index of currently selected line on each screen. */ int current; /* Current cursor position on CUI screen. */ int y; /* Columns to shift when displaying. */ int x; /* For editpolicy_line_draw(). */ int saved_color_current; /* Initialized to -1 */ int saved_color_y; }; #define CCS_HEADER_LINES 3 #define CCS_EDITPOLICY_CONF "/etc/tomoyo/tools/editpolicy.conf" enum color_type editpolicy_color_head(void); enum directive_type find_directive(const _Bool forward, char *line); int editpolicy_get_current(void); void editpolicy_attr_change(const attr_t attr, const _Bool flg); void editpolicy_color_change(const attr_t attr, const _Bool flg); void editpolicy_color_init(void); void editpolicy_init_keyword_map(void); void editpolicy_line_draw(void); void editpolicy_offline_daemon(const int listener, const int notifier); void editpolicy_optimize(void); void editpolicy_sttr_restore(void); void editpolicy_sttr_save(void); struct path_group *find_path_group_ns(const struct ccs_path_info *ns, const char *group_name); struct ccs_domain { const struct ccs_path_info *domainname; const struct ccs_path_info *target; /* This may be NULL */ const struct transition_entry *d_t; /* This may be NULL */ const struct ccs_path_info **string_ptr; int string_count; int number; /* domain number (-1 if target or is_dd) */ _Bool group[256]; u8 profile; _Bool is_djt; /* domain jump target */ _Bool is_dk; /* domain keeper */ _Bool is_du; /* unreachable domain */ _Bool is_dd; /* deleted domain */ }; struct domain_policy { struct ccs_domain *list; int list_len; unsigned char *list_selected; }; struct policy { /* Array of "path_group" entries. */ struct path_group *path_group; /* Length of path_group_list array. */ int path_group_len; /* Array of "number_group" entry. */ struct number_group *number_group; /* Length of number_group_list array. */ int number_group_len; /* Array of "address_group" entry. */ struct address_group *address_group; /* Length of address_group_list array. */ int address_group_len; /* Array of "acl_group" entry. */ char **acl_group[256]; /* Length of acl_group_list array.*/ int acl_group_len[256]; /* Array of string ACL entries. */ struct generic_entry *generic; /* Length of generic_list array. */ int generic_len; }; extern enum screen_type active; extern struct policy p; extern struct editpolicy_directive directive_map[MAX_DIRECTIVE_TYPE]; extern struct ccs_screen screen[MAX_SCREEN_TYPE]; extern const struct ccs_path_info *current_ns; tomoyo-tools/usr_sbin/tomoyo-diffpolicy.c0000644000000000000000000000777114116520000017705 0ustar rootroot/* * tomoyo-diffpolicy.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" int main(int argc, char *argv[]) { struct ccs_domain_policy old_policy = { NULL, 0, NULL }; struct ccs_domain_policy new_policy = { NULL, 0, NULL }; const struct ccs_path_info **old_string_ptr; const struct ccs_path_info **new_string_ptr; int old_string_count; int new_string_count; int old_index; int new_index; const struct ccs_path_info *domainname; int i; int j; const char *old = NULL; const char *new = NULL; if (argc != 3) goto usage; old = argv[1]; new = argv[2]; if (!strcmp(new, "-")) new = NULL; if (!strcmp(old, "-")) old = NULL; if (!new && !old) { usage: printf("%s old_domain_policy new_domain_policy\n" "- : Read policy from stdin.\n", argv[0]); return 0; } ccs_read_domain_policy(&old_policy, old); ccs_read_domain_policy(&new_policy, new); for (old_index = 0; old_index < old_policy.list_len; old_index++) { domainname = old_policy.list[old_index].domainname; new_index = ccs_find_domain_by_ptr(&new_policy, domainname); if (new_index >= 0) continue; /* This domain was deleted. */ printf("delete %s\n\n", domainname->name); } for (new_index = 0; new_index < new_policy.list_len; new_index++) { domainname = new_policy.list[new_index].domainname; old_index = ccs_find_domain_by_ptr(&old_policy, domainname); if (old_index >= 0) continue; /* This domain was added. */ printf("%s\n\n", domainname->name); if (new_policy.list[new_index].profile_assigned) printf("use_profile %u\n", new_policy.list[new_index].profile); new_string_ptr = new_policy.list[new_index].string_ptr; new_string_count = new_policy.list[new_index].string_count; for (i = 0; i < new_string_count; i++) printf("%s\n", new_string_ptr[i]->name); printf("\n"); } for (old_index = 0; old_index < old_policy.list_len; old_index++) { _Bool first = true; domainname = old_policy.list[old_index].domainname; new_index = ccs_find_domain_by_ptr(&new_policy, domainname); if (new_index == EOF) continue; /* This domain exists in both old policy and new policy. */ old_string_ptr = old_policy.list[old_index].string_ptr; old_string_count = old_policy.list[old_index].string_count; new_string_ptr = new_policy.list[new_index].string_ptr; new_string_count = new_policy.list[new_index].string_count; for (i = 0; i < old_string_count; i++) { for (j = 0; j < new_string_count; j++) { if (old_string_ptr[i] != new_string_ptr[j]) continue; old_string_ptr[i] = NULL; new_string_ptr[j] = NULL; } } for (i = 0; i < new_string_count; i++) { if (!new_string_ptr[i]) continue; if (first) printf("%s\n\n", domainname->name); first = false; printf("%s\n", new_string_ptr[i]->name); } for (i = 0; i < old_string_count; i++) { if (!old_string_ptr[i]) continue; if (first) printf("%s\n\n", domainname->name); first = false; printf("delete %s\n", old_string_ptr[i]->name); } if (old_policy.list[old_index].profile != new_policy.list[new_index].profile) { if (first) printf("%s\n\n", domainname->name); first = false; if (new_policy.list[new_index].profile_assigned) printf("use_profile %u\n", new_policy.list[new_index].profile); } if (!first) printf("\n"); } return 0; } tomoyo-tools/usr_sbin/tomoyotools.h0000644000000000000000000001414714116520000016640 0ustar rootroot/* * tomoyotools.h * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define _FILE_OFFSET_BITS 64 #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define s8 __s8 #define u8 __u8 #define u16 __u16 #define u32 __u32 #define true 1 #define false 0 #ifndef MS_REC #define MS_REC 16384 #endif #ifndef MS_PRIVATE #define MS_PRIVATE (1<<18) #endif /***** CONSTANTS DEFINITION START *****/ #define CCS_PROC_POLICY_DIR "/sys/kernel/security/tomoyo/" #define CCS_PROC_POLICY_DOMAIN_POLICY "/sys/kernel/security/tomoyo/domain_policy" #define CCS_PROC_POLICY_EXCEPTION_POLICY "/sys/kernel/security/tomoyo/exception_policy" #define CCS_PROC_POLICY_AUDIT "/sys/kernel/security/tomoyo/audit" #define CCS_PROC_POLICY_MANAGER "/sys/kernel/security/tomoyo/manager" #define CCS_PROC_POLICY_STAT "/sys/kernel/security/tomoyo/stat" #define CCS_PROC_POLICY_PROCESS_STATUS "/sys/kernel/security/tomoyo/.process_status" #define CCS_PROC_POLICY_PROFILE "/sys/kernel/security/tomoyo/profile" #define CCS_PROC_POLICY_QUERY "/sys/kernel/security/tomoyo/query" /***** CONSTANTS DEFINITION END *****/ /***** STRUCTURES DEFINITION START *****/ struct ccs_path_info { const char *name; u32 hash; /* = ccs_full_name_hash(name, total_len) */ u16 total_len; /* = strlen(name) */ u16 const_len; /* = ccs_const_part_length(name) */ _Bool is_dir; /* = ccs_strendswith(name, "/") */ _Bool is_patterned; /* = const_len < total_len */ }; struct ccs_ip_address_entry { u8 min[16]; u8 max[16]; _Bool is_ipv6; }; struct ccs_number_entry { unsigned long min; unsigned long max; }; struct ccs_domain_info { const struct ccs_path_info *domainname; const struct ccs_path_info **string_ptr; int string_count; u8 profile; _Bool profile_assigned; u8 group; }; struct ccs_domain_policy { struct ccs_domain_info *list; int list_len; unsigned char *list_selected; }; struct ccs_task_entry { pid_t pid; pid_t ppid; char *name; char *domain; u8 profile; _Bool selected; int index; int depth; }; /***** STRUCTURES DEFINITION END *****/ /***** PROTOTYPES DEFINITION START *****/ FILE *ccs_open_read(const char *filename); FILE *ccs_open_write(const char *filename); _Bool ccs_check_remote_host(void); _Bool ccs_close_write(FILE *fp); _Bool ccs_correct_domain(const char *domainname); _Bool ccs_correct_path(const char *filename); _Bool ccs_correct_word(const char *string); _Bool ccs_decode(const char *ascii, char *bin); _Bool ccs_domain_def(const char *domainname); _Bool ccs_move_proc_to_file(const char *src, const char *dest); _Bool ccs_path_matches_pattern(const struct ccs_path_info *pathname0, const struct ccs_path_info *pattern0); _Bool ccs_pathcmp(const struct ccs_path_info *a, const struct ccs_path_info *b); _Bool ccs_str_starts(char *str, const char *begin); char *ccs_freadline(FILE *fp); char *ccs_freadline_unpack(FILE *fp); char *ccs_shprintf(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); char *ccs_strdup(const char *string); const char *ccs_domain_name(const struct ccs_domain_policy *dp, const int index); const struct ccs_path_info *ccs_savename(const char *name); int ccs_add_string_entry(struct ccs_domain_policy *dp, const char *entry, const int index); int ccs_assign_domain(struct ccs_domain_policy *dp, const char *domainname); int ccs_del_string_entry(struct ccs_domain_policy *dp, const char *entry, const int index); int ccs_find_domain(const struct ccs_domain_policy *dp, const char *domainname0); int ccs_find_domain_by_ptr(struct ccs_domain_policy *dp, const struct ccs_path_info *domainname); int ccs_open_stream(const char *filename); int ccs_parse_ip(const char *address, struct ccs_ip_address_entry *entry); int ccs_parse_number(const char *number, struct ccs_number_entry *entry); int ccs_string_compare(const void *a, const void *b); int ccs_write_domain_policy(struct ccs_domain_policy *dp, const int fd); struct ccs_path_group_entry *ccs_find_path_group(const char *group_name); void *ccs_malloc(const size_t size); void *ccs_realloc(void *ptr, const size_t size); void *ccs_realloc2(void *ptr, const size_t size); void ccs_clear_domain_policy(struct ccs_domain_policy *dp); void ccs_delete_domain(struct ccs_domain_policy *dp, const int index); void ccs_fill_path_info(struct ccs_path_info *ptr); void ccs_fprintf_encoded(FILE *fp, const char *ccs_pathname); void ccs_get(void); void ccs_handle_domain_policy(struct ccs_domain_policy *dp, FILE *fp, _Bool is_write); void ccs_mount_securityfs(void); void ccs_normalize_line(char *buffer); void ccs_put(void); void ccs_read_domain_policy(struct ccs_domain_policy *dp, const char *filename); void ccs_read_process_list(_Bool show_all); extern _Bool ccs_freadline_raw; extern _Bool ccs_network_mode; extern int ccs_task_list_len; extern struct ccs_task_entry *ccs_task_list; extern u16 ccs_network_port; extern u32 ccs_network_ip; /***** PROTOTYPES DEFINITION END *****/ tomoyo-tools/usr_sbin/tomoyo-checkpolicy.c0000644000000000000000000004641614116520000020051 0ustar rootroot/* * tomoyo-checkpolicy.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2012 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" #define CCS_MAX_DOMAINNAME_LEN (4096 - 10) static unsigned int ccs_line = 0; static unsigned int ccs_errors = 0; static _Bool ccs_parse_ulong(unsigned long *result, char **str) { const char *cp = *str; char *ep; int base = 10; if (*cp == '0') { char c = *(cp + 1); if (c == 'x' || c == 'X') { base = 16; cp += 2; } else if (c >= '0' && c <= '7') { base = 8; cp++; } } *result = strtoul(cp, &ep, base); if (cp == ep) return false; *str = ep; return true; } static _Bool ccs_check_number_range(char *pos) { unsigned long min_value; unsigned long max_value; if (!ccs_parse_ulong(&min_value, &pos)) return false; if (*pos == '-') { pos++; if (!ccs_parse_ulong(&max_value, &pos) || *pos || min_value > max_value) return false; } else if (*pos) return false; return true; } static void ccs_check_condition(char *condition) { enum ccs_conditions_index { CCS_TASK_UID, /* current_uid() */ CCS_TASK_EUID, /* current_euid() */ CCS_TASK_SUID, /* current_suid() */ CCS_TASK_FSUID, /* current_fsuid() */ CCS_TASK_GID, /* current_gid() */ CCS_TASK_EGID, /* current_egid() */ CCS_TASK_SGID, /* current_sgid() */ CCS_TASK_FSGID, /* current_fsgid() */ CCS_TASK_PID, /* sys_getpid() */ CCS_TASK_PPID, /* sys_getppid() */ CCS_EXEC_ARGC, /* "struct linux_binprm *"->argc */ CCS_EXEC_ENVC, /* "struct linux_binprm *"->envc */ CCS_TYPE_IS_SOCKET, /* S_IFSOCK */ CCS_TYPE_IS_SYMLINK, /* S_IFLNK */ CCS_TYPE_IS_FILE, /* S_IFREG */ CCS_TYPE_IS_BLOCK_DEV, /* S_IFBLK */ CCS_TYPE_IS_DIRECTORY, /* S_IFDIR */ CCS_TYPE_IS_CHAR_DEV, /* S_IFCHR */ CCS_TYPE_IS_FIFO, /* S_IFIFO */ CCS_MODE_SETUID, /* S_ISUID */ CCS_MODE_SETGID, /* S_ISGID */ CCS_MODE_STICKY, /* S_ISVTX */ CCS_MODE_OWNER_READ, /* S_IRUSR */ CCS_MODE_OWNER_WRITE, /* S_IWUSR */ CCS_MODE_OWNER_EXECUTE, /* S_IXUSR */ CCS_MODE_GROUP_READ, /* S_IRGRP */ CCS_MODE_GROUP_WRITE, /* S_IWGRP */ CCS_MODE_GROUP_EXECUTE, /* S_IXGRP */ CCS_MODE_OTHERS_READ, /* S_IROTH */ CCS_MODE_OTHERS_WRITE, /* S_IWOTH */ CCS_MODE_OTHERS_EXECUTE, /* S_IXOTH */ CCS_EXEC_REALPATH, CCS_SYMLINK_TARGET, CCS_PATH1_UID, CCS_PATH1_GID, CCS_PATH1_INO, CCS_PATH1_MAJOR, CCS_PATH1_MINOR, CCS_PATH1_PERM, CCS_PATH1_TYPE, CCS_PATH1_DEV_MAJOR, CCS_PATH1_DEV_MINOR, CCS_PATH2_UID, CCS_PATH2_GID, CCS_PATH2_INO, CCS_PATH2_MAJOR, CCS_PATH2_MINOR, CCS_PATH2_PERM, CCS_PATH2_TYPE, CCS_PATH2_DEV_MAJOR, CCS_PATH2_DEV_MINOR, CCS_PATH1_PARENT_UID, CCS_PATH1_PARENT_GID, CCS_PATH1_PARENT_INO, CCS_PATH1_PARENT_PERM, CCS_PATH2_PARENT_UID, CCS_PATH2_PARENT_GID, CCS_PATH2_PARENT_INO, CCS_PATH2_PARENT_PERM, CCS_MAX_CONDITION_KEYWORD, CCS_NUMBER_UNION, CCS_NAME_UNION, CCS_ARGV_ENTRY, CCS_ENVP_ENTRY }; static const char *ccs_condition_keyword[CCS_MAX_CONDITION_KEYWORD] = { [CCS_TASK_UID] = "task.uid", [CCS_TASK_EUID] = "task.euid", [CCS_TASK_SUID] = "task.suid", [CCS_TASK_FSUID] = "task.fsuid", [CCS_TASK_GID] = "task.gid", [CCS_TASK_EGID] = "task.egid", [CCS_TASK_SGID] = "task.sgid", [CCS_TASK_FSGID] = "task.fsgid", [CCS_TASK_PID] = "task.pid", [CCS_TASK_PPID] = "task.ppid", [CCS_EXEC_ARGC] = "exec.argc", [CCS_EXEC_ENVC] = "exec.envc", [CCS_TYPE_IS_SOCKET] = "socket", [CCS_TYPE_IS_SYMLINK] = "symlink", [CCS_TYPE_IS_FILE] = "file", [CCS_TYPE_IS_BLOCK_DEV] = "block", [CCS_TYPE_IS_DIRECTORY] = "directory", [CCS_TYPE_IS_CHAR_DEV] = "char", [CCS_TYPE_IS_FIFO] = "fifo", [CCS_MODE_SETUID] = "setuid", [CCS_MODE_SETGID] = "setgid", [CCS_MODE_STICKY] = "sticky", [CCS_MODE_OWNER_READ] = "owner_read", [CCS_MODE_OWNER_WRITE] = "owner_write", [CCS_MODE_OWNER_EXECUTE] = "owner_execute", [CCS_MODE_GROUP_READ] = "group_read", [CCS_MODE_GROUP_WRITE] = "group_write", [CCS_MODE_GROUP_EXECUTE] = "group_execute", [CCS_MODE_OTHERS_READ] = "others_read", [CCS_MODE_OTHERS_WRITE] = "others_write", [CCS_MODE_OTHERS_EXECUTE] = "others_execute", [CCS_EXEC_REALPATH] = "exec.realpath", [CCS_SYMLINK_TARGET] = "symlink.target", [CCS_PATH1_UID] = "path1.uid", [CCS_PATH1_GID] = "path1.gid", [CCS_PATH1_INO] = "path1.ino", [CCS_PATH1_MAJOR] = "path1.major", [CCS_PATH1_MINOR] = "path1.minor", [CCS_PATH1_PERM] = "path1.perm", [CCS_PATH1_TYPE] = "path1.type", [CCS_PATH1_DEV_MAJOR] = "path1.dev_major", [CCS_PATH1_DEV_MINOR] = "path1.dev_minor", [CCS_PATH2_UID] = "path2.uid", [CCS_PATH2_GID] = "path2.gid", [CCS_PATH2_INO] = "path2.ino", [CCS_PATH2_MAJOR] = "path2.major", [CCS_PATH2_MINOR] = "path2.minor", [CCS_PATH2_PERM] = "path2.perm", [CCS_PATH2_TYPE] = "path2.type", [CCS_PATH2_DEV_MAJOR] = "path2.dev_major", [CCS_PATH2_DEV_MINOR] = "path2.dev_minor", [CCS_PATH1_PARENT_UID] = "path1.parent.uid", [CCS_PATH1_PARENT_GID] = "path1.parent.gid", [CCS_PATH1_PARENT_INO] = "path1.parent.ino", [CCS_PATH1_PARENT_PERM] = "path1.parent.perm", [CCS_PATH2_PARENT_UID] = "path2.parent.uid", [CCS_PATH2_PARENT_GID] = "path2.parent.gid", [CCS_PATH2_PARENT_INO] = "path2.parent.ino", [CCS_PATH2_PARENT_PERM] = "path2.parent.perm", }; char *pos = condition; u8 left; u8 right; if (*pos && pos[strlen(pos) - 1] == ' ') condition[strlen(pos) - 1] = '\0'; if (!*pos) return; while (pos) { char *eq; char *next = strchr(pos, ' '); int r_len; if (next) *next++ = '\0'; if (!ccs_correct_word(pos)) goto out; eq = strchr(pos, '='); if (!eq) goto out; *eq = '\0'; if (eq > pos && *(eq - 1) == '!') *(eq - 1) = '\0'; r_len = strlen(eq + 1); if (!strncmp(pos, "exec.argv[", 10)) { unsigned long value; pos += 10; if (!ccs_parse_ulong(&value, &pos) || strcmp(pos, "]")) goto out; pos = eq + 1; if (r_len < 2) goto out; if (pos[0] == '"' && pos[r_len - 1] == '"') goto next; goto out; } else if (!strncmp(pos, "exec.envp[\"", 11)) { if (strcmp(pos + strlen(pos) - 2, "\"]")) goto out; pos = eq + 1; if (!strcmp(pos, "NULL")) goto next; if (r_len < 2 || pos[0] != '"' || pos[r_len - 1] != '"') goto out; goto next; } else if (!strcmp(pos, "auto_domain_transition")) { pos = eq + 1; if (r_len < 2 || pos[0] != '"' || pos[r_len - 1] != '"') goto out; pos[r_len - 1] = '\0'; if (pos[1] != '/' && !ccs_domain_def(pos + 1)) goto out; goto next; } else if (!strcmp(pos, "grant_log")) { pos = eq + 1; if (!strcmp(pos, "yes") || !strcmp(pos, "no")) goto next; goto out; } for (left = 0; left < CCS_MAX_CONDITION_KEYWORD; left++) { const char *keyword = ccs_condition_keyword[left]; if (strcmp(pos, keyword)) continue; break; } if (left == CCS_MAX_CONDITION_KEYWORD) { if (!ccs_check_number_range(pos)) goto out; } pos = eq + 1; if (left == CCS_EXEC_REALPATH || left == CCS_SYMLINK_TARGET) { if (r_len < 2) goto out; if (pos[0] == '@') goto next; if (pos[0] == '"' && pos[r_len - 1] == '"') goto next; goto out; } for (right = 0; right < CCS_MAX_CONDITION_KEYWORD; right++) { const char *keyword = ccs_condition_keyword[right]; if (strcmp(pos, keyword)) continue; goto next; } if (pos[0] == '@' && pos[1]) goto next; if (!ccs_check_number_range(pos)) goto out; next: pos = next; } return; out: printf("%u: ERROR: '%s' is an illegal condition.\n", ccs_line, pos); ccs_errors++; } static _Bool ccs_prune_word(char *arg, const char *cp) { if (cp) memmove(arg, cp, strlen(cp) + 1); else *arg = '\0'; return true; } static _Bool ccs_check_path(char *arg) { char *cp = strchr(arg, ' '); if (cp) *cp++ = '\0'; if (!ccs_correct_word(arg)) return false; return ccs_prune_word(arg, cp); } static _Bool ccs_check_number(char *arg) { char *cp = strchr(arg, ' '); char *start = arg; unsigned long min_value; unsigned long max_value; if (cp) *cp++ = '\0'; if (*arg == '@') goto ok; if (!ccs_parse_ulong(&min_value, &arg)) return false; if (!*arg) goto ok; if (*arg++ != '-' || !ccs_parse_ulong(&max_value, &arg) || *arg || min_value > max_value) return false; ok: return ccs_prune_word(start, cp); } static _Bool ccs_check_domain(char *arg) { char *cp = arg; while (*cp) { if (*cp++ != ' ' || *cp++ == '/') continue; cp -= 2; *cp++ = '\0'; break; } if (!ccs_correct_domain(arg)) return false; return ccs_prune_word(arg, cp); } static _Bool ccs_check_transition(char *arg) { char *cp; _Bool conflict = strstr(arg, " auto_domain_transition=") != NULL; if (*arg == '<') return !conflict && ccs_check_domain(arg); cp = strchr(arg, ' '); if (cp) *cp++ = '\0'; if (ccs_correct_path(arg) || !strcmp(arg, "keep") || !strcmp(arg, "initialize") || !strcmp(arg, "reset") || !strcmp(arg, "child") || !strcmp(arg, "parent")) return !conflict && ccs_prune_word(arg, cp); if (cp) *--cp = ' '; return true; } static _Bool ccs_check_path_transition(char *arg) { if (!ccs_check_path(arg)) return false; return ccs_check_transition(arg); } static _Bool ccs_check_u8(char *arg) { char *cp = strchr(arg, ' '); unsigned int value; if (cp) *cp++ = '\0'; if (sscanf(arg, "%u", &value) != 1 || value >= 256) return false; return ccs_prune_word(arg, cp); } static _Bool ccs_check_ip_address(char *arg) { char *cp = strchr(arg, ' '); struct ccs_ip_address_entry entry = { }; if (cp) *cp++ = '\0'; if (*arg == '@') /* Don't reject address_group. */ goto found; if (ccs_parse_ip(arg, &entry) || memcmp(entry.min, entry.max, 16) > 0) return false; found: return ccs_prune_word(arg, cp); } static _Bool ccs_check_network(char *arg) { static const struct { const char *directive; u8 flags; } list[] = { { "bind", 1 }, { "listen", 2 }, { "connect", 2 }, { "accept", 2 }, { "send", 4 }, }; _Bool inet; u8 mask; u8 flags = 0; char *start = arg; if (ccs_str_starts(arg, "inet ")) inet = true; else if (ccs_str_starts(arg, "unix ")) inet = false; else return false; if ((inet && ccs_str_starts(arg, "stream ")) || (!inet && (ccs_str_starts(arg, "stream ") || ccs_str_starts(arg, "seqpacket ")))) mask = 2; else if ((inet && (ccs_str_starts(arg, "dgram ") || ccs_str_starts(arg, "raw "))) || (!inet && ccs_str_starts(arg, "dgram "))) mask = 4; else return false; while (1) { u8 type; while (ccs_str_starts(arg, "/")); if (ccs_str_starts(arg, " ")) break; for (type = 0; list[type].directive; type++) { if (((list[type].flags | mask) & 6) == 6) continue; if (!ccs_str_starts(arg, list[type].directive)) continue; flags |= list[type].flags; break; } if (!list[type].directive) { while (*arg && *arg != ' ' && *arg != '/') arg++; *arg = '\0'; goto out; } } if (!flags) goto out; ccs_prune_word(start, arg); return inet ? ccs_check_ip_address(start) && ccs_check_number(start) : ccs_check_path(start); out: return false; } static _Bool ccs_check_path_domain(char *arg) { if (!strncmp(arg, "any ", 4)) ccs_prune_word(arg, arg + 4); else if (*arg != '/' || !ccs_check_path(arg)) return false; if (!strncmp(arg, "from ", 5)) ccs_prune_word(arg, arg + 5); else if (!*arg) return true; else return false; if (!strncmp(arg, "any", 3)) { ccs_prune_word(arg, arg + 3); } else if (*arg == '/') { if (!ccs_check_path(arg)) return false; } else { if (!ccs_check_domain(arg)) return false; } return !*arg; } static _Bool ccs_check_path2(char *arg) { return ccs_check_path(arg) && ccs_check_path(arg); } static _Bool ccs_check_path_number(char *arg) { return ccs_check_path(arg) && ccs_check_number(arg); } static _Bool ccs_check_path_number3(char *arg) { return ccs_check_path(arg) && ccs_check_number(arg) && ccs_check_number(arg) && ccs_check_number(arg); } static _Bool ccs_check_path3_number(char *arg) { return ccs_check_path(arg) && ccs_check_path(arg) && ccs_check_path(arg) && ccs_check_number(arg); } static _Bool ccs_check_file(char *arg) { static const struct { const char *directive; _Bool (*func) (char *arg); } list[] = { { "append", ccs_check_path }, { "chgrp", ccs_check_path_number }, { "chmod", ccs_check_path_number }, { "chown", ccs_check_path_number }, { "chroot", ccs_check_path }, { "create", ccs_check_path_number }, { "execute", ccs_check_path_transition }, { "getattr", ccs_check_path }, { "ioctl", ccs_check_path_number }, { "link", ccs_check_path2 }, { "mkblock", ccs_check_path_number3 }, { "mkchar", ccs_check_path_number3 }, { "mkdir", ccs_check_path_number }, { "mkfifo", ccs_check_path_number }, { "mksock", ccs_check_path_number }, { "mount", ccs_check_path3_number }, { "pivot_root", ccs_check_path2 }, { "read", ccs_check_path }, { "rename", ccs_check_path2 }, { "rmdir", ccs_check_path }, { "symlink", ccs_check_path }, { "truncate", ccs_check_path }, { "unlink", ccs_check_path }, { "unmount", ccs_check_path }, { "write", ccs_check_path }, { } }; _Bool (*func) (char *) = NULL; char *start = arg; while (1) { u8 type; while (ccs_str_starts(arg, "/")); if (ccs_str_starts(arg, " ")) break; for (type = 0; list[type].directive; type++) { if (func && func != list[type].func) continue; if (!ccs_str_starts(arg, list[type].directive)) continue; func = list[type].func; break; } if (!list[type].directive) { while (*arg && *arg != ' ' && *arg != '/') arg++; *arg = '\0'; goto out; } } if (!func || !func(arg)) goto out; return ccs_prune_word(start, arg); out: return false; } static _Bool ccs_check_domain_policy2(char *policy) { u8 type; static const struct { const char *directive; _Bool (*arg1) (char *arg); _Bool (*arg2) (char *arg); } list[] = { { "file ", ccs_check_file }, { "misc env ", ccs_check_path }, { "network ", ccs_check_network }, { "task manual_domain_transition ", ccs_check_domain }, { } }; for (type = 0; list[type].directive; type++) { if (!ccs_str_starts(policy, list[type].directive)) continue; if (!list[type].arg1(policy)) break; if (list[type].arg2 && !list[type].arg2(policy)) break; ccs_check_condition(policy); return true; } return false; } static void ccs_check_domain_policy(char *policy) { if (ccs_domain_def(policy)) { if (!ccs_correct_domain(policy) || strlen(policy) >= CCS_MAX_DOMAINNAME_LEN) { printf("%u: ERROR: '%s' is a bad domainname.\n", ccs_line, policy); ccs_errors++; } return; } else if (!strcmp(policy, "quota_exceeded") || !strcmp(policy, "transition_failed")) { return; } else if (ccs_str_starts(policy, "use_group ") || ccs_str_starts(policy, "use_profile ")) { if (ccs_check_u8(policy)) return; } else if (ccs_check_domain_policy2(policy)) return; { char *cp = policy; while (*cp && *cp != ' ') cp++; *cp = '\0'; } printf("%u: ERROR: '%s' is a bad argument.\n", ccs_line, policy); ccs_errors++; } static void ccs_check_exception_policy(char *policy) { static const struct { const char *directive; _Bool (*arg1) (char *arg); _Bool (*arg2) (char *arg); } list[] = { { "acl_group ", ccs_check_u8, ccs_check_domain_policy2 }, { "address_group ", ccs_check_path, ccs_check_ip_address }, { "aggregator ", ccs_check_path, ccs_check_path }, { "initialize_domain ", ccs_check_path_domain }, { "keep_domain ", ccs_check_path_domain }, { "no_initialize_domain ", ccs_check_path_domain }, { "no_keep_domain ", ccs_check_path_domain }, { "no_reset_domain ", ccs_check_path_domain }, { "number_group ", ccs_check_path, ccs_check_number }, { "path_group ", ccs_check_path, ccs_check_path }, { "reset_domain ", ccs_check_path_domain }, { } }; u8 type; if (*policy == '<') { char *ns = strchr(policy, ' '); if (ns) { *ns++= '\0'; if (!ccs_domain_def(policy)) { printf("%u: ERROR: '%s' is a bad namespace\n", ccs_line, policy); ccs_errors++; } policy = ns; } } for (type = 0; list[type].directive; type++) { const int len = strlen(list[type].directive); if (strncmp(policy, list[type].directive, len)) continue; policy += len; if (!list[type].arg1(policy)) break; if (list[type].arg2 && !list[type].arg2(policy)) break; return; } { char *cp = policy; while (*cp && *cp != ' ') cp++; *cp = '\0'; } printf("%u: ERROR: '%s' is a bad argument.\n", ccs_line, policy); ccs_errors++; } int main(int argc, char *argv[]) { unsigned int ccs_warnings = 0; char *policy = NULL; enum ccs_policy_type { CCS_POLICY_TYPE_UNKNOWN, CCS_POLICY_TYPE_DOMAIN_POLICY, CCS_POLICY_TYPE_EXCEPTION_POLICY, }; enum ccs_policy_type policy_type = CCS_POLICY_TYPE_UNKNOWN; if (argc > 1) { switch (argv[1][0]) { case 'e': policy_type = CCS_POLICY_TYPE_EXCEPTION_POLICY; break; case 'd': policy_type = CCS_POLICY_TYPE_DOMAIN_POLICY; break; } } if (policy_type == CCS_POLICY_TYPE_UNKNOWN) { fprintf(stderr, "%s e|d < policy_to_check\n", argv[0]); return 0; } while (true) { _Bool badchar_warned = false; int pos = 0; ccs_line++; while (true) { static int max_policy_len = 0; int c = getchar(); if (c == EOF) goto out; if (pos == max_policy_len) { max_policy_len += 4096; policy = ccs_realloc(policy, max_policy_len); } policy[pos++] = (char) c; if (c == '\n') { policy[--pos] = '\0'; break; } if (badchar_warned || c == '\t' || (c >= ' ' && c < 127)) continue; printf("%u: WARNING: Line contains illegal " "character (\\%03o).\n", ccs_line, c); ccs_warnings++; badchar_warned = true; } ccs_normalize_line(policy); if (!policy[0] || policy[0] == '#') continue; switch (policy_type) { case CCS_POLICY_TYPE_DOMAIN_POLICY: ccs_check_domain_policy(policy); break; case CCS_POLICY_TYPE_EXCEPTION_POLICY: ccs_check_exception_policy(policy); break; default: break; } } out: free(policy); policy = NULL; ccs_line--; printf("Total: %u Line%s %u Error%s %u Warning%s\n", ccs_line, ccs_line > 1 ? "s" : "", ccs_errors, ccs_errors > 1 ? "s" : "", ccs_warnings, ccs_warnings > 1 ? "s" : ""); return ccs_errors ? 2 : (ccs_warnings ? 1 : 0); } tomoyo-tools/usr_sbin/tomoyo-loadpolicy.c0000644000000000000000000001677614116520000017721 0ustar rootroot/* * tomoyo-loadpolicy.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" static _Bool ccs_move_file_to_proc(const char *dest) { FILE *proc_fp = ccs_open_write(dest); _Bool result = true; if (!proc_fp) { fprintf(stderr, "Can't open %s for writing.\n", dest); return false; } ccs_get(); while (true) { char *line = ccs_freadline(stdin); if (!line) break; if (line[0]) if (fprintf(proc_fp, "%s\n", line) < 0) result = false; } ccs_put(); if (!ccs_close_write(proc_fp)) result = false; return result; } static _Bool ccs_delete_proc_policy(const char *name) { FILE *fp_in; FILE *fp_out; _Bool result = false; if (ccs_network_mode) { fp_in = ccs_open_read(name); fp_out = ccs_open_write(name); } else { fp_in = fopen(name, "r"); fp_out = fopen(name, "w"); } if (!fp_in || !fp_out) { fprintf(stderr, "Can't open %s for reading and writing.\n", name); if (fp_in) fclose(fp_in); if (fp_out) fclose(fp_out); return false; } ccs_get(); while (true) { char *line = ccs_freadline(fp_in); if (!line) break; if (fprintf(fp_out, "delete %s\n", line) < 0) result = false; } ccs_put(); if (fclose(fp_in)) result = false; if (!ccs_close_write(fp_out)) result = false; return result; } static _Bool ccs_update_domain_policy(struct ccs_domain_policy *proc_policy, struct ccs_domain_policy *file_policy, const char *src, const char *dest) { int file_index; int proc_index; FILE *proc_fp; _Bool result = true; _Bool nm = ccs_network_mode; /* Load disk policy to file_policy->list. */ ccs_network_mode = false; ccs_read_domain_policy(file_policy, src); ccs_network_mode = nm; /* Load proc policy to proc_policy->list. */ ccs_read_domain_policy(proc_policy, dest); proc_fp = ccs_open_write(dest); if (!proc_fp) { fprintf(stderr, "Can't open %s for writing.\n", dest); return false; } for (file_index = 0; file_index < file_policy->list_len; file_index++) { int i; int j; const struct ccs_path_info *domainname = file_policy->list[file_index].domainname; const struct ccs_path_info **file_string_ptr = file_policy->list[file_index].string_ptr; const int file_string_count = file_policy->list[file_index].string_count; const struct ccs_path_info **proc_string_ptr; int proc_string_count; proc_index = ccs_find_domain_by_ptr(proc_policy, domainname); if (fprintf(proc_fp, "%s\n", domainname->name) < 0) result = false; if (proc_index == EOF) goto not_found; /* Proc policy for this domain found. */ proc_string_ptr = proc_policy->list[proc_index].string_ptr; proc_string_count = proc_policy->list[proc_index].string_count; for (j = 0; j < proc_string_count; j++) { for (i = 0; i < file_string_count; i++) { if (file_string_ptr[i] == proc_string_ptr[j]) break; } /* Delete this entry from proc policy if not found in disk policy. */ if (i == file_string_count) if (fprintf(proc_fp, "delete %s\n", proc_string_ptr[j]->name) < 0) result = false; } ccs_delete_domain(proc_policy, proc_index); not_found: /* Append entries defined in disk policy. */ for (i = 0; i < file_string_count; i++) if (fprintf(proc_fp, "%s\n", file_string_ptr[i]->name) < 0) result = false; if (file_policy->list[file_index].profile_assigned) if (fprintf(proc_fp, "use_profile %u\n", file_policy->list[file_index].profile) < 0) result = false; } /* Delete all domains that are not defined in disk policy. */ for (proc_index = 0; proc_index < proc_policy->list_len; proc_index++) if (fprintf(proc_fp, "delete %s\n", proc_policy->list[proc_index].domainname->name) < 0) result = false; if (!ccs_close_write(proc_fp)) result = false; return result; } int main(int argc, char *argv[]) { struct ccs_domain_policy proc_policy = { NULL, 0, NULL }; struct ccs_domain_policy file_policy = { NULL, 0, NULL }; _Bool refresh_policy = false; _Bool result = true; char target = 0; int i; for (i = 1; i < argc; i++) { char *ptr = argv[i]; char *cp = strchr(ptr, ':'); if (cp) { *cp++ = '\0'; ccs_network_ip = inet_addr(ptr); ccs_network_port = htons(atoi(cp)); if (ccs_network_mode) { fprintf(stderr, "You cannot specify multiple " "%s at the same time.\n\n", "remote agents"); goto usage; } ccs_network_mode = true; } else { if (target) { fprintf(stderr, "You cannot specify multiple " "%s at the same time.\n\n", "policies"); goto usage; } if (*ptr++ != '-') goto usage; target = *ptr++; if (target != 'e' && target != 'd' && target != 'p' && target != 'm' && target != 's') goto usage; if (*ptr) { if ((target != 'e' && target != 'd') || strcmp(ptr, "f")) goto usage; refresh_policy = true; } } } if (!target) { fprintf(stderr, "You need to specify %s.\n\n", "policy to load"); goto usage; } if (ccs_network_mode) { if (!ccs_check_remote_host()) return 1; } else if (ccs_mount_securityfs(), access(CCS_PROC_POLICY_DIR, F_OK)) { fprintf(stderr, "You can't run this program for this kernel.\n"); return 1; } switch (target) { case 'p': result = ccs_move_file_to_proc(CCS_PROC_POLICY_PROFILE); break; case 'm': result = ccs_move_file_to_proc(CCS_PROC_POLICY_MANAGER); break; case 's': result = ccs_move_file_to_proc(CCS_PROC_POLICY_STAT); break; case 'e': if (refresh_policy) result = ccs_delete_proc_policy (CCS_PROC_POLICY_EXCEPTION_POLICY); result = ccs_move_file_to_proc (CCS_PROC_POLICY_EXCEPTION_POLICY); break; case 'd': if (!refresh_policy) { result = ccs_move_file_to_proc (CCS_PROC_POLICY_DOMAIN_POLICY); break; } result = ccs_update_domain_policy (&proc_policy, &file_policy, NULL, CCS_PROC_POLICY_DOMAIN_POLICY); ccs_clear_domain_policy(&proc_policy); ccs_clear_domain_policy(&file_policy); break; } return !result; usage: printf("%s {-e|-ef|-d|-df|-m|-p|-s} [remote_ip:remote_port]\n\n" "-e : Read from stdin and append to " "/sys/kernel/security/tomoyo/exception_policy .\n" "-ef : Read from stdin and overwrite " "/sys/kernel/security/tomoyo/exception_policy .\n" "-d : Read from stdin and append to /sys/kernel/security/tomoyo/domain_policy " ".\n" "-df : Read from stdin and overwrite /sys/kernel/security/tomoyo/domain_policy " ".\n" "-m : Read from stdin and append to /sys/kernel/security/tomoyo/manager .\n" "-p : Read from stdin and append to /sys/kernel/security/tomoyo/profile .\n" "-s : Read from stdin and append to /sys/kernel/security/tomoyo/stat .\n" "remote_ip:remote_port : Write to tomoyo-editpolicy-agent " "listening at remote_ip:remote_port rather than /sys/kernel/security/tomoyo/ " "directory.\n", argv[0]); return 1; } tomoyo-tools/usr_sbin/tomoyo-selectpolicy.c0000644000000000000000000000322514116520000020242 0ustar rootroot/* * tomoyo-selectpolicy.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" int main(int argc, char *argv[]) { _Bool recursive = false; _Bool matched = false; int start = 1; int i; if (argc > 1 && !strcmp(argv[1], "-r")) { recursive = true; start++; } if (argc <= start) { fprintf(stderr, "%s [-r] domainname [domainname ...]" " < domain_policy\n", argv[0]); return 0; } for (i = start; i < argc; i++) ccs_normalize_line(argv[i]); ccs_get(); while (true) { char *line = ccs_freadline(stdin); if (!line) break; if (ccs_domain_def(line)) { matched = false; for (i = start; i < argc; i++) { const int len = strlen(argv[i]); if (strncmp(line, argv[i], len)) continue; if (!recursive) { if (line[len]) continue; } else { if (line[len] && line[len] != ' ') continue; } matched = true; } } if (matched) puts(line); } ccs_put(); return 0; } tomoyo-tools/usr_sbin/tomoyo-pstree.c0000644000000000000000000000533114116520000017045 0ustar rootroot/* * tomoyo-pstree.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" static void ccs_dump(const pid_t pid, const int depth) { int i; for (i = 0; i < ccs_task_list_len; i++) { int j; if (pid != ccs_task_list[i].pid) continue; printf("%3d", ccs_task_list[i].profile); for (j = 0; j < depth - 1; j++) printf(" "); for (; j < depth; j++) printf(" +-"); printf(" %s (%u) %s\n", ccs_task_list[i].name, ccs_task_list[i].pid, ccs_task_list[i].domain); ccs_task_list[i].selected = true; } for (i = 0; i < ccs_task_list_len; i++) { if (pid != ccs_task_list[i].ppid) continue; ccs_dump(ccs_task_list[i].pid, depth + 1); } } int main(int argc, char *argv[]) { static _Bool show_all = false; int i; for (i = 1; i < argc; i++) { char *ptr = argv[i]; char *cp = strchr(ptr, ':'); if (cp) { *cp++ = '\0'; if (ccs_network_mode) goto usage; ccs_network_ip = inet_addr(ptr); ccs_network_port = htons(atoi(cp)); ccs_network_mode = true; if (!ccs_check_remote_host()) return 1; } else if (!strcmp(ptr, "-a")) { show_all = true; } else { usage: fprintf(stderr, "Usage: %s " "[-a] [remote_ip:remote_port]\n", argv[0]); return 0; } } if (!ccs_network_mode) ccs_mount_securityfs(); ccs_read_process_list(show_all); if (!ccs_task_list_len) { if (ccs_network_mode) { fprintf(stderr, "Can't connect.\n"); return 1; } else { fprintf(stderr, "You can't use this command " "for this kernel.\n"); return 1; } } ccs_dump(1, 0); for (i = 0; i < ccs_task_list_len; i++) { if (ccs_task_list[i].selected) continue; printf("%3d %s (%u) %s\n", ccs_task_list[i].profile, ccs_task_list[i].name, ccs_task_list[i].pid, ccs_task_list[i].domain); ccs_task_list[i].selected = true; } while (ccs_task_list_len) { ccs_task_list_len--; free((void *) ccs_task_list[ccs_task_list_len].name); free((void *) ccs_task_list[ccs_task_list_len].domain); } free(ccs_task_list); ccs_task_list = NULL; return 0; } tomoyo-tools/usr_sbin/readline.h0000644000000000000000000001560314116520000016012 0ustar rootroot/* * readline.h * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include static int ccs_getch0(void) { static int enter_key = EOF; int c; again: c = getch(); if (c == 127 || c == 8) c = KEY_BACKSPACE; /* syslog(LOG_INFO, "ccs_getch0='%c' (%d)\n", c, c); */ if (c == '\r' || c == '\n') { if (enter_key == EOF) enter_key = c; else if (c != enter_key) goto again; } return c; } static int ccs_getch2(void) { static int c0 = 0; static int c1 = 0; static int c2 = 0; static int c3 = 0; static int len = 0; if (len > 0) { c0 = c1; c1 = c2; c2 = c3; len--; return c0; } c0 = ccs_getch0(); if (c0 != 0x1B) return c0; c1 = ccs_getch0(); if (c1 != '[') { len = 1; return c0; } c2 = ccs_getch0(); if (c2 < '1' || c2 > '6') { len = 2; return c0; } c3 = ccs_getch0(); if (c3 != '~') { len = 3; return c0; } /* syslog(LOG_INFO, "ccs_getch2='%c'\n", c2); */ switch (c2) { case '1': return KEY_HOME; case '2': return KEY_IC; case '3': return KEY_DC; case '4': return KEY_END; case '5': return KEY_PPAGE; case '6': return KEY_NPAGE; } return 0; } static int ccs_add_history(const char *buffer, const char **history, const int history_count, const int max_history) { char *cp = buffer ? strdup(buffer) : NULL; if (!cp) return history_count; if (history_count && !strcmp(history[history_count - 1], cp)) { free(cp); return history_count; } if (history_count < max_history) { history[history_count] = cp; return history_count + 1; } else if (max_history) { int i; free((char *) history[0]); for (i = 0; i < history_count - 1; i++) history[i] = history[i + 1]; history[history_count - 1] = cp; return history_count; } return 0; } static int ccs_query_fd = EOF; static char *ccs_initial_readline_data = NULL; static char *ccs_readline(const int start_y, const int start_x, const char *prompt, const char *history[], const int history_count, const int max_length, const int scroll_width) { const int prompt_len = prompt ? strlen(prompt) : 0; int buffer_len = 0; int line_pos = 0; int cur_pos = 0; int history_pos = 0; _Bool tmp_saved = false; static char *buffer = NULL; static char *tmp_buffer = NULL; { int i; for (i = 0; i < history_count; i++) if (!history[i]) return NULL; } { char *tmp; tmp = realloc(buffer, max_length + 1); if (!tmp) return NULL; buffer = tmp; tmp = realloc(tmp_buffer, max_length + 1); if (!tmp) return NULL; tmp_buffer = tmp; memset(buffer, 0, max_length + 1); memset(tmp_buffer, 0, max_length + 1); } move(start_y, start_x); history_pos = history_count; if (ccs_initial_readline_data) { strncpy(buffer, ccs_initial_readline_data, max_length); buffer_len = strlen(buffer); ungetch(KEY_END); } while (true) { int window_width; int window_height; int c; int x; int y; int i; int ret_ignored; getmaxyx(stdscr, window_height, window_width); window_width -= prompt_len; getyx(stdscr, y, x); move(y, 0); while (cur_pos > window_width - 1) { cur_pos--; line_pos++; } if (prompt_len) printw("%s", prompt); for (i = line_pos; i < line_pos + window_width; i++) { if (i < buffer_len) addch(buffer[i]); else break; } clrtoeol(); move(y, cur_pos + prompt_len); refresh(); c = ccs_getch2(); if (ccs_query_fd != EOF) ret_ignored = write(ccs_query_fd, "\n", 1); if (c == 4) { /* Ctrl-D */ if (!buffer_len) buffer_len = -1; break; } else if (c == KEY_IC) { scrollok(stdscr, TRUE); printw("\n"); for (i = 0; i < history_count; i++) printw("%d: '%s'\n", i, history[i]); scrollok(stdscr, FALSE); } else if (c >= 0x20 && c <= 0x7E && buffer_len < max_length - 1) { for (i = buffer_len - 1; i >= line_pos + cur_pos; i--) buffer[i + 1] = buffer[i]; buffer[line_pos + cur_pos] = c; buffer[++buffer_len] = '\0'; if (cur_pos < window_width - 1) cur_pos++; else line_pos++; } else if (c == '\r' || c == '\n') { break; } else if (c == KEY_BACKSPACE) { if (line_pos + cur_pos) { buffer_len--; for (i = line_pos + cur_pos - 1; i < buffer_len; i++) buffer[i] = buffer[i + 1]; buffer[buffer_len] = '\0'; if (line_pos >= scroll_width && cur_pos == 0) { line_pos -= scroll_width; cur_pos += scroll_width - 1; } else if (cur_pos) { cur_pos--; } else if (line_pos) { line_pos--; } } } else if (c == KEY_DC) { if (line_pos + cur_pos < buffer_len) { buffer_len--; for (i = line_pos + cur_pos; i < buffer_len; i++) buffer[i] = buffer[i + 1]; buffer[buffer_len] = '\0'; } } else if (c == KEY_UP) { if (history_pos) { if (!tmp_saved) { tmp_saved = true; strncpy(tmp_buffer, buffer, max_length); } history_pos--; strncpy(buffer, history[history_pos], max_length); buffer_len = strlen(buffer); goto end_key; } } else if (c == KEY_DOWN) { if (history_pos < history_count - 1) { history_pos++; strncpy(buffer, history[history_pos], max_length); buffer_len = strlen(buffer); goto end_key; } else if (tmp_saved) { tmp_saved = false; history_pos = history_count; strncpy(buffer, tmp_buffer, max_length); buffer_len = strlen(buffer); goto end_key; } } else if (c == KEY_HOME) { cur_pos = 0; line_pos = 0; } else if (c == KEY_END) { goto end_key; } else if (c == KEY_LEFT) { if (line_pos >= scroll_width && cur_pos == 0) { line_pos -= scroll_width; cur_pos += scroll_width - 1; } else if (cur_pos) { cur_pos--; } else if (line_pos) { line_pos--; } } else if (c == KEY_RIGHT) { if (line_pos + cur_pos < buffer_len) { if (cur_pos < window_width - 1) cur_pos++; else if (line_pos + cur_pos < buffer_len - scroll_width && cur_pos >= scroll_width - 1) { cur_pos -= scroll_width - 1; line_pos += scroll_width; } else { line_pos++; } } } continue; end_key: cur_pos = buffer_len; line_pos = 0; if (cur_pos > window_width - 1) { line_pos = buffer_len - (window_width - 1); cur_pos = window_width - 1; } } if (buffer_len == -1) return NULL; ccs_normalize_line(buffer); return strdup(buffer); } tomoyo-tools/usr_sbin/tomoyo-findtemp.c0000644000000000000000000000532514116520000017354 0ustar rootroot/* * tomoyo-findtemp.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" int main(int argc, char *argv[]) { const char **pattern_list = NULL; int pattern_list_count = 0; int i; char buffer[16384]; char buffer2[sizeof(buffer)]; if (argc > 1) { if (!strcmp(argv[1], "--with-domainname")) { _Bool flag = 0; static char *domain = NULL; memset(buffer, 0, sizeof(buffer)); while (fgets(buffer, sizeof(buffer) - 1, stdin)) { char *cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; if (ccs_domain_def(buffer)) { free(domain); domain = ccs_strdup(buffer); flag = 0; continue; } cp = buffer; while (1) { struct stat64 buf; char *cp2 = strchr(cp, ' '); if (cp2) *cp2 = '\0'; if (*cp == '/' && ccs_decode(cp, buffer2) && lstat64(buffer2, &buf)) { if (!flag) printf("\n%s\n", domain); flag = 1; printf("%s\n", buffer); break; } if (!cp2) break; *cp2++ = ' '; cp = cp2; } } free(domain); return 0; } if (strcmp(argv[1], "--all")) { printf("%s < domain_policy\n\n", argv[0]); return 0; } } while (memset(buffer, 0, sizeof(buffer)) && fscanf(stdin, "%16380s", buffer) == 1) { if (buffer[0] != '/') continue; { struct stat64 buf; if (!ccs_decode(buffer, buffer2)) continue; if (!lstat64(buffer2, &buf)) continue; } for (i = 0; i < pattern_list_count; i++) { if (!strcmp(pattern_list[i], buffer)) break; } if (i < pattern_list_count) continue; pattern_list = ccs_realloc(pattern_list, sizeof(const char *) * (pattern_list_count + 1)); pattern_list[pattern_list_count++] = ccs_strdup(buffer); } qsort(pattern_list, pattern_list_count, sizeof(const char *), ccs_string_compare); for (i = 0; i < pattern_list_count; i++) printf("%s\n", pattern_list[i]); for (i = 0; i < pattern_list_count; i++) free((void *) pattern_list[i]); free(pattern_list); return 0; } tomoyo-tools/usr_sbin/tomoyo-sortpolicy.c0000644000000000000000000000177614116520000017763 0ustar rootroot/* * tomoyo-sortpolicy.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" int main(int argc, char *argv[]) { struct ccs_domain_policy dp = { NULL, 0, NULL }; ccs_read_domain_policy(&dp, NULL); ccs_write_domain_policy(&dp, 1); ccs_clear_domain_policy(&dp); return 0; } tomoyo-tools/usr_sbin/Makefile0000644000000000000000000000422414116520000015513 0ustar rootrootinclude ../Include.make BUILD_FILES := tomoyo-auditd tomoyo-checkpolicy tomoyo-diffpolicy tomoyo-domainmatch \ tomoyo-editpolicy tomoyo-findtemp tomoyo-loadpolicy tomoyo-notifyd tomoyo-patternize \ tomoyo-pstree tomoyo-queryd tomoyo-savepolicy tomoyo-selectpolicy tomoyo-setlevel \ tomoyo-setprofile tomoyo-sortpolicy all: libtomoyotools.so $(BUILD_FILES) $(BUILD_FILES): libtomoyotools.so /usr/include/ncurses.h: @echo "/usr/include/ncurses.h is missing." @echo "Run 'yum install ncurses-devel' or 'apt-get install libncurses-dev'" sleep 10 # -fPIE conflicts with -fPIC, disable it for libraries. CFLAGS_PIC := $(filter-out -fPIE,$(CFLAGS)) LDFLAGS_PIC := $(filter-out -pie,$(filter-out -fPIE,$(LDFLAGS))) libtomoyotools.so: tomoyotools.c tomoyotools.h $(CC) $(CPPFLAGS) $(CFLAGS_PIC) $(LDFLAGS_PIC) -fPIC tomoyotools.c -shared -Wl,-soname,libtomoyotools.so.3 -o libtomoyotools.so.3.0.4 ln -sf libtomoyotools.so.3.0.4 libtomoyotools.so .c: $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< -ltomoyotools -L. tomoyo-editpolicy: tomoyotools.h editpolicy*.c readline.h /usr/include/ncurses.h libtomoyotools.so $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o tomoyo-editpolicy editpolicy*.c -lncurses -ltinfo -ltomoyotools -L. -DCOLOR_ON -DNCURSES_WIDECHAR=0 || \ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o tomoyo-editpolicy editpolicy*.c -lncurses -ltomoyotools -L. -DCOLOR_ON -DNCURSES_WIDECHAR=0 tomoyo-queryd: tomoyotools.h tomoyo-queryd.c readline.h /usr/include/ncurses.h libtomoyotools.so $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o tomoyo-queryd tomoyo-queryd.c -lncurses -ltinfo -ltomoyotools -L. -DNCURSES_WIDECHAR=0 || \ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o tomoyo-queryd tomoyo-queryd.c -lncurses -ltomoyotools -L. -DNCURSES_WIDECHAR=0 install: all mkdir -p -m 0755 $(INSTALLDIR)$(USRLIBDIR) $(INSTALL) -m 0755 libtomoyotools.so.3.0.4 $(INSTALLDIR)$(USRLIBDIR) ln -sf libtomoyotools.so.3.0.4 $(INSTALLDIR)$(USRLIBDIR)/libtomoyotools.so.3 ifeq ($(INSTALLDIR),) ldconfig || true endif mkdir -p -m 0755 $(INSTALLDIR)$(USRSBINDIR) $(INSTALL) -m 0755 $(BUILD_FILES) $(INSTALLDIR)$(USRSBINDIR) clean: rm -f -- $(BUILD_FILES) libtomoyotools.so* .PHONY: clean install tomoyo-tools/usr_sbin/tomoyo-queryd.c0000644000000000000000000002114614116520000017056 0ustar rootroot/* * tomoyo-queryd.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" #include "readline.h" /* Prototypes */ static void ccs_printw(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); static _Bool ccs_handle_query(unsigned int serial); /* Utility functions */ static void ccs_printw(const char *fmt, ...) { va_list args; int i; int len; char *buffer; va_start(args, fmt); len = vsnprintf((char *) &i, sizeof(i) - 1, fmt, args) + 16; va_end(args); buffer = ccs_malloc(len); va_start(args, fmt); len = vsnprintf(buffer, len, fmt, args); va_end(args); for (i = 0; i < len; i++) { addch(buffer[i]); refresh(); } free(buffer); } static void ccs_send_keepalive(void) { static time_t previous = 0; time_t now = time(NULL); if (previous != now || !previous) { int ret_ignored; previous = now; ret_ignored = write(ccs_query_fd, "\n", 1); } } /* Variables */ static unsigned short int ccs_retries = 0; static FILE *ccs_domain_fp = NULL; static int ccs_domain_policy_fd = EOF; #define CCS_MAX_READLINE_HISTORY 20 static const char **ccs_readline_history = NULL; static int ccs_readline_history_count = 0; static char ccs_buffer[32768]; /* Main functions */ static _Bool ccs_handle_query(unsigned int serial) { int c = 0; int y; int x; int ret_ignored; char *line = NULL; static unsigned int prev_pid = 0; unsigned int pid; char pidbuf[128]; char *cp = strstr(ccs_buffer, " (global-pid="); if (!cp || sscanf(cp + 13, "%u", &pid) != 1) { ccs_printw("ERROR: Unsupported query.\n"); return false; } cp = ccs_buffer + strlen(ccs_buffer); if (*(cp - 1) != '\n') { ccs_printw("ERROR: Unsupported query.\n"); return false; } *(cp - 1) = '\0'; if (pid != prev_pid) { if (prev_pid) ccs_printw("----------------------------------------" "\n"); prev_pid = pid; } ccs_printw("%s\n", ccs_buffer); /* Is this domain query? */ if (strstr(ccs_buffer, "\n#")) goto not_domain_query; memset(pidbuf, 0, sizeof(pidbuf)); snprintf(pidbuf, sizeof(pidbuf) - 1, "select Q=%u\n", serial); ccs_printw("Allow? ('Y'es/'N'o/'R'etry/'S'how policy/'A'dd to policy " "and retry):"); while (true) { c = ccs_getch2(); if (c == 'Y' || c == 'y' || c == 'N' || c == 'n' || c == 'R' || c == 'r' || c == 'A' || c == 'a' || c == 'S' || c == 's') break; ccs_send_keepalive(); } ccs_printw("%c\n", c); if (c == 'S' || c == 's') { if (ccs_network_mode) { fprintf(ccs_domain_fp, "%s", pidbuf); fputc(0, ccs_domain_fp); fflush(ccs_domain_fp); rewind(ccs_domain_fp); while (1) { char c; if (fread(&c, 1, 1, ccs_domain_fp) != 1 || !c) break; addch(c); refresh(); ccs_send_keepalive(); } } else { ret_ignored = write(ccs_domain_policy_fd, pidbuf, strlen(pidbuf)); while (1) { int i; int len = read(ccs_domain_policy_fd, ccs_buffer, sizeof(ccs_buffer) - 1); if (len <= 0) break; for (i = 0; i < len; i++) { addch(ccs_buffer[i]); refresh(); } ccs_send_keepalive(); } } c = 'r'; } /* Append to domain policy. */ if (c != 'A' && c != 'a') goto not_append; c = 'r'; getyx(stdscr, y, x); cp = strrchr(ccs_buffer, '\n'); if (!cp) return false; *cp++ = '\0'; ccs_initial_readline_data = cp; ccs_readline_history_count = ccs_add_history(cp, ccs_readline_history, ccs_readline_history_count, CCS_MAX_READLINE_HISTORY); line = ccs_readline(y, 0, "Enter new entry> ", ccs_readline_history, ccs_readline_history_count, 128000, 8); scrollok(stdscr, TRUE); ccs_printw("\n"); if (!line || !*line) { ccs_printw("None added.\n"); goto not_append; } ccs_readline_history_count = ccs_add_history(line, ccs_readline_history, ccs_readline_history_count, CCS_MAX_READLINE_HISTORY); if (ccs_network_mode) { fprintf(ccs_domain_fp, "%s%s\n", pidbuf, line); fflush(ccs_domain_fp); } else { ret_ignored = write(ccs_domain_policy_fd, pidbuf, strlen(pidbuf)); ret_ignored = write(ccs_domain_policy_fd, line, strlen(line)); ret_ignored = write(ccs_domain_policy_fd, "\n", 1); } ccs_printw("Added '%s'.\n", line); not_append: free(line); write_answer: /* Write answer. */ if (c == 'Y' || c == 'y' || c == 'A' || c == 'a') c = 1; else if (c == 'R' || c == 'r') c = 3; else c = 2; snprintf(ccs_buffer, sizeof(ccs_buffer) - 1, "A%u=%u\n", serial, c); ret_ignored = write(ccs_query_fd, ccs_buffer, strlen(ccs_buffer)); ccs_printw("\n"); return true; not_domain_query: ccs_printw("Allow? ('Y'es/'N'o/'R'etry):"); while (true) { c = ccs_getch2(); if (c == 'Y' || c == 'y' || c == 'N' || c == 'n' || c == 'R' || c == 'r') break; ccs_send_keepalive(); } ccs_printw("%c\n", c); goto write_answer; } int main(int argc, char *argv[]) { if (argc == 1) goto ok; { char *cp = strchr(argv[1], ':'); if (cp) { *cp++ = '\0'; ccs_network_ip = inet_addr(argv[1]); ccs_network_port = htons(atoi(cp)); ccs_network_mode = true; if (!ccs_check_remote_host()) return 1; goto ok; } } printf("Usage: %s [remote_ip:remote_port]\n\n", argv[0]); printf("This program is used for granting access requests manually." "\n"); printf("This program shows access requests that are about to be " "rejected by the kernel's decision.\n"); printf("If you answer before the kernel's decision takes effect, your " "decision will take effect.\n"); printf("You can use this program to respond to accidental access " "requests triggered by non-routine tasks (such as restarting " "daemons after updating).\n"); printf("To terminate this program, use 'Ctrl-C'.\n"); return 0; ok: if (ccs_network_mode) { ccs_query_fd = ccs_open_stream("proc:query"); ccs_domain_fp = ccs_open_write(CCS_PROC_POLICY_DOMAIN_POLICY); } else { ccs_mount_securityfs(); ccs_query_fd = open(CCS_PROC_POLICY_QUERY, O_RDWR); ccs_domain_policy_fd = open(CCS_PROC_POLICY_DOMAIN_POLICY, O_RDWR); } if (ccs_query_fd == EOF) { fprintf(stderr, "You can't run this utility for this kernel.\n"); return 1; } else if (!ccs_network_mode && write(ccs_query_fd, "", 0) != 0) { fprintf(stderr, "You need to register this program to %s to " "run this program.\n", CCS_PROC_POLICY_MANAGER); return 1; } ccs_readline_history = ccs_malloc(CCS_MAX_READLINE_HISTORY * sizeof(const char *)); ccs_send_keepalive(); initscr(); cbreak(); noecho(); nonl(); intrflush(stdscr, FALSE); keypad(stdscr, TRUE); clear(); refresh(); scrollok(stdscr, TRUE); if (ccs_network_mode) { const u32 ip = ntohl(ccs_network_ip); ccs_printw("Monitoring /sys/kernel/security/tomoyo/query via %u.%u.%u.%u:%u.", (u8) (ip >> 24), (u8) (ip >> 16), (u8) (ip >> 8), (u8) ip, ntohs(ccs_network_port)); } else ccs_printw("Monitoring /sys/kernel/security/tomoyo/query ."); ccs_printw(" Press Ctrl-C to terminate.\n\n"); while (true) { unsigned int serial; char *cp; /* Wait for query and read query. */ memset(ccs_buffer, 0, sizeof(ccs_buffer)); if (ccs_network_mode) { int i; int ret_ignored; ret_ignored = write(ccs_query_fd, "", 1); for (i = 0; i < sizeof(ccs_buffer) - 1; i++) { if (read(ccs_query_fd, ccs_buffer + i, 1) != 1) break; if (!ccs_buffer[i]) goto read_ok; } break; } else { struct pollfd pfd; pfd.fd = ccs_query_fd; pfd.events = POLLIN; pfd.revents = 0; poll(&pfd, 1, -1); if (!(pfd.revents & POLLIN)) continue; if (read(ccs_query_fd, ccs_buffer, sizeof(ccs_buffer) - 1) <= 0) continue; } read_ok: cp = strchr(ccs_buffer, '\n'); if (!cp) continue; *cp = '\0'; /* Get query number. */ if (sscanf(ccs_buffer, "Q%u-%hu", &serial, &ccs_retries) != 2) continue; memmove(ccs_buffer, cp + 1, strlen(cp + 1) + 1); /* Clear pending input. */; timeout(0); while (true) { int c = ccs_getch2(); if (c == EOF || c == ERR) break; } timeout(1000); if (ccs_handle_query(serial)) continue; break; } endwin(); return 0; } tomoyo-tools/usr_sbin/tomoyo-auditd.c0000644000000000000000000002573614116520000017030 0ustar rootroot/* * tomoyo-auditd.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" #include #include #include #define CCS_AUDITD_CONF "/etc/tomoyo/tools/auditd.conf" struct ccs_destination { const char *pathname; int fd; }; static struct ccs_destination *destination_list = NULL; static unsigned int destination_list_len = 0; enum ccs_rule_types { CCS_SORT_RULE_HEADER, CCS_SORT_RULE_DOMAIN, CCS_SORT_RULE_ACL, CCS_SORT_RULE_DESTINATION, }; enum ccs_operator_types { CCS_SORT_OPERATOR_CONTAINS, CCS_SORT_OPERATOR_EQUALS, CCS_SORT_OPERATOR_STARTS, }; struct ccs_sort_rules { enum ccs_rule_types type; enum ccs_operator_types operation; unsigned int index; const char *string; unsigned int string_len; /* strlen(string). */ }; static struct ccs_sort_rules *rules = NULL; static unsigned int rules_len = 0; static void ccs_auditd_init_rules(const char *filename) { static _Bool first = 1; FILE *fp = fopen(filename, "r"); unsigned int line_no = 0; unsigned int i; if (!first) { for (i = 0; i < rules_len; i++) free((void *) rules[i].string); rules_len = 0; for (i = 0; i < destination_list_len; i++) { free((void *) destination_list[i].pathname); close(destination_list[i].fd); } destination_list_len = 0; } if (!fp) { if (first) fprintf(stderr, "Can't open %s for reading.\n", filename); else syslog(LOG_WARNING, "Can't open %s for reading.\n", filename); exit(1); } ccs_get(); while (true) { char *line = ccs_freadline(fp); struct ccs_sort_rules *ptr; unsigned char c; if (!line) break; line_no++; ccs_normalize_line(line); if (*line == '#' || !*line) continue; rules = ccs_realloc(rules, sizeof(struct ccs_sort_rules) * (rules_len + 1)); ptr = &rules[rules_len++]; memset(ptr, 0, sizeof(*ptr)); if (ccs_str_starts(line, "destination ")) { if (*line != '/') goto invalid_rule; for (i = 0; i < destination_list_len; i++) if (!strcmp(destination_list[i].pathname, line)) break; if (i < destination_list_len) goto store_destination; destination_list = ccs_realloc(destination_list, ++destination_list_len * sizeof(struct ccs_destination)); if (!ccs_decode(line, line)) goto invalid_rule; destination_list[i].pathname = ccs_strdup(line); destination_list[i].fd = EOF; store_destination: ptr->type = CCS_SORT_RULE_DESTINATION; ptr->index = i; continue; } if (ccs_str_starts(line, "header")) ptr->type = CCS_SORT_RULE_HEADER; else if (ccs_str_starts(line, "domain")) ptr->type = CCS_SORT_RULE_DOMAIN; else if (ccs_str_starts(line, "acl")) ptr->type = CCS_SORT_RULE_ACL; else goto invalid_rule; switch (sscanf(line, "[%u%c", &ptr->index, &c)) { case 0: break; case 2: if (c == ']') { char *cp = strchr(line, ']') + 1; memmove(line, cp, strlen(cp) + 1); break; } default: goto invalid_rule; } if (ccs_str_starts(line, ".contains ")) ptr->operation = CCS_SORT_OPERATOR_CONTAINS; else if (ccs_str_starts(line, ".equals ")) ptr->operation = CCS_SORT_OPERATOR_EQUALS; else if (ccs_str_starts(line, ".starts ")) ptr->operation = CCS_SORT_OPERATOR_STARTS; else goto invalid_rule; if (!*line) goto invalid_rule; line = ccs_strdup(line); ptr->string = line; ptr->string_len = strlen(line); } ccs_put(); fclose(fp); if (!rules_len) { if (first) fprintf(stderr, "No rules defined in %s .\n", filename); else syslog(LOG_WARNING, "No rules defined in %s .\n", filename); exit(1); } for (i = 0; i < destination_list_len; i++) { struct ccs_destination *ptr = &destination_list[i]; const char *path = ptr->pathname; /* This is OK because path is a strdup()ed string. */ char *pos = (char *) path; while (*pos) { int ret_ignored; if (*pos++ != '/') continue; *(pos - 1) = '\0'; ret_ignored = mkdir(path, 0700); *(pos - 1) = '/'; } do { ptr->fd = open(path, O_WRONLY | O_APPEND | O_CREAT, 0600); } while (ptr->fd == EOF && errno == EINTR); if (ptr->fd == EOF) { if (first) fprintf(stderr, "Can't open %s for writing.\n", path); else syslog(LOG_WARNING, "Can't open %s for writing.\n", path); exit(1); } } first = 0; return; invalid_rule: if (first) fprintf(stderr, "Invalid rule at line %u in %s .\n", line_no, filename); else syslog(LOG_WARNING, "Invalid rule at line %u in %s .\n", line_no, filename); exit(1); } static int ccs_check_rules(char *header, char *domain, char *acl) { unsigned int i; _Bool matched = true; for (i = 0; i < rules_len; i++) { const struct ccs_sort_rules *ptr = &rules[i]; char *line; unsigned int index = ptr->index; const char *find = ptr->string; unsigned int find_len = ptr->string_len; switch (ptr->type) { case CCS_SORT_RULE_HEADER: line = header; break; case CCS_SORT_RULE_DOMAIN: line = domain; break; case CCS_SORT_RULE_ACL: line = acl; break; default: /* CCS_SORT_RULE_DESTINATION */ if (matched) return ptr->index; matched = true; continue; } if (!matched) continue; if (!index) { switch (ptr->operation) { case CCS_SORT_OPERATOR_CONTAINS: while (1) { char *cp = strstr(line, find); if (!cp) { matched = false; break; } if ((cp == line || *(cp - 1) == ' ') && (!cp[find_len] || cp[find_len] == ' ')) break; line = cp + 1; } break; case CCS_SORT_OPERATOR_EQUALS: matched = !strcmp(line, find); break; default: /* CCS_SORT_OPERATOR_STARTS */ matched = !strncmp(line, find, find_len) && (!line[find_len] || line[find_len] == ' '); } } else { char *word = line; char *word_end; while (--index) { char *cp = strchr(word, ' '); if (!cp) { matched = false; break; } word = cp + 1; } if (!matched) continue; word_end = strchr(word, ' '); if (word_end) *word_end = '\0'; switch (ptr->operation) { case CCS_SORT_OPERATOR_CONTAINS: matched = strstr(word, find) != NULL; break; case CCS_SORT_OPERATOR_EQUALS: matched = !strcmp(word, find); break; default: /* CCS_SORT_OPERATOR_STARTS */ matched = !strncmp(word, find, find_len); break; } if (word_end) *word_end = ' '; } } return EOF; } static _Bool ccs_write_log(const int i, char *buffer) { int len = strlen(buffer); int ret; struct ccs_destination *ptr = &destination_list[i]; /* Create destination file if needed. */ if (access(ptr->pathname, F_OK)) { close(ptr->fd); do { ptr->fd = open(ptr->pathname, O_WRONLY | O_APPEND | O_CREAT, 0600); } while (ptr->fd == EOF && errno == EINTR); if (ptr->fd == EOF) { syslog(LOG_WARNING, "Can't open %s for writing.\n", ptr->pathname); return 0; } } /* * This is OK because we read only up to sizeof(buffer) - 1 bytes. */ buffer[len++] = '\n'; do { ret = write(ptr->fd, buffer, len); if (ret == len) return 1; } while (ret == EOF && errno == EINTR); syslog(LOG_WARNING, "Can't write to %s .\n", ptr->pathname); return 0; } static void block_sighup(const _Bool block) { sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGHUP); sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sigset, NULL); } static void ccs_reload_config(int sig) { block_sighup(1); syslog(LOG_WARNING, "Reloading configuration file.\n"); ccs_auditd_init_rules(CCS_AUDITD_CONF); block_sighup(0); } int main(int argc, char *argv[]) { unsigned int i; int fd_in; for (i = 1; i < argc; i++) { char *ptr = argv[i]; char *cp = strchr(ptr, ':'); if (!cp) goto usage; *cp++ = '\0'; if (ccs_network_mode) goto usage; ccs_network_ip = inet_addr(ptr); ccs_network_port = htons(atoi(cp)); ccs_network_mode = true; if (!ccs_check_remote_host()) return 1; } ccs_auditd_init_rules(CCS_AUDITD_CONF); if (ccs_network_mode) goto start; ccs_mount_securityfs(); if (access(CCS_PROC_POLICY_AUDIT, R_OK)) { fprintf(stderr, "You can't run this daemon for this kernel." "\n"); return 1; } { /* Get exclusive lock. */ int fd = open("/proc/self/exe", O_RDONLY); if (flock(fd, LOCK_EX | LOCK_NB) == EOF) return 0; } start: if (ccs_network_mode) fd_in = ccs_open_stream("proc:audit"); else fd_in = open(CCS_PROC_POLICY_AUDIT, O_RDONLY); if (fd_in == EOF) { fprintf(stderr, "Can't open %s for reading.\n", CCS_PROC_POLICY_AUDIT); return 1; } switch (fork()) { case 0: break; case -1: fprintf(stderr, "Can't fork()\n"); return 1; default: return 0; } if (setsid() == EOF) { fprintf(stderr, "Can't setsid()\n"); return 1; } switch (fork()) { case 0: break; case -1: fprintf(stderr, "Can't fork()\n"); return 1; default: return 0; } if (chdir("/")) { fprintf(stderr, "Can't chdir()\n"); return 1; } close(0); close(1); close(2); openlog("tomoyo-auditd", 0, LOG_USER); syslog(LOG_WARNING, "Started.\n"); signal(SIGHUP, ccs_reload_config); while (true) { static char buffer[32768]; char *domain; char *acl; char *tail; int ret; memset(buffer, 0, sizeof(buffer)); if (ccs_network_mode) { int j; for (j = 0; j < sizeof(buffer) - 1; j++) { do { ret = read(fd_in, buffer + j, 1); } while (ret == EOF && errno == EINTR); if (ret != 1) goto out; if (!buffer[j]) break; } if (j == sizeof(buffer) - 1) goto out; } else { while (read(fd_in, buffer, sizeof(buffer) - 1) <= 0) { /* Wait for data. */ struct pollfd pfd = { .fd = fd_in, .events = POLLIN, }; if (poll(&pfd, 1, -1) == EOF && errno != EINTR) goto out; } } /* Split into three lines. */ domain = strchr(buffer, '\n'); if (!domain) continue; *domain++ = '\0'; acl = strchr(domain, '\n'); if (!acl) continue; *acl++ = '\0'; tail = strchr(acl, '\n'); if (!tail) continue; *tail = '\0'; block_sighup(1); /* Check for filtering rules. */ i = ccs_check_rules(buffer, domain, acl); if (i != EOF) { *tail = '\n'; *--acl = '\n'; *--domain = '\n'; /* Write the audit log. */ if (!ccs_write_log(i, buffer)) break; } block_sighup(0); } out: syslog(LOG_WARNING, "Terminated.\n"); closelog(); return 1; usage: fprintf(stderr, "%s [remote_ip:remote_port]\n" " See %s for configuration.\n", argv[0], CCS_AUDITD_CONF); return 1; } tomoyo-tools/usr_sbin/tomoyo-setprofile.c0000644000000000000000000000525714116520000017726 0ustar rootroot/* * tomoyo-setprofile.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" int main(int argc, char *argv[]) { unsigned int profile = 0; _Bool recursive = false; int try; int i; int start = 2; if (argc > 1 && !strcmp(argv[1], "-r")) { recursive = true; start = 3; } if (argc <= start || sscanf(argv[start - 1], "%u", &profile) != 1) { fprintf(stderr, "%s [-r] profile domainname [domainname ...]\n", argv[0]); return 0; } for (i = start; i < argc; i++) ccs_normalize_line(argv[i]); ccs_mount_securityfs(); { const int fd = open(CCS_PROC_POLICY_DOMAIN_POLICY, O_RDWR); if (fd == EOF) { fprintf(stderr, "You can't run this command for this " "kernel.\n"); return 1; } else if (write(fd, "", 0) != 0) { fprintf(stderr, "You need to register this program to " "%s to run this program.\n", CCS_PROC_POLICY_MANAGER); return 1; } close(fd); } for (try = 0; try < 2; try++) { FILE *fp_in = fopen(CCS_PROC_POLICY_DOMAIN_POLICY, "r"); FILE *fp_out = fopen(CCS_PROC_POLICY_DOMAIN_POLICY, "w"); char *domainname = NULL; if (!fp_in || !fp_out) { fprintf(stderr, "Can't open policy file.\n"); exit(1); } ccs_get(); while (true) { char *line = ccs_freadline(fp_in); if (!line) break; if (domainname) { if (sscanf(line, "use_profile %u", &profile) != 1) continue; printf("%u %s\n", profile, domainname); free(domainname); domainname = NULL; continue; } if (*line != '<') continue; for (i = start; i < argc; i++) { const int len = strlen(argv[i]); if (strncmp(line, argv[i], len)) continue; if (!recursive) { if (line[len]) continue; } else { if (line[len] && line[len] != ' ') continue; } if (try) { domainname = ccs_strdup(line); break; } fprintf(fp_out, "select %s\nuse_profile %u\n", line, profile); break; } } ccs_put(); fclose(fp_in); fclose(fp_out); } return 0; } tomoyo-tools/usr_sbin/tomoyo-notifyd.c0000644000000000000000000001400614116520000017216 0ustar rootroot/* * tomoyo-notifyd.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" #include #include #include #include #define CCS_NOTIFYD_CONF "/etc/tomoyo/tools/notifyd.conf" static const char *proc_policy_query = "/sys/kernel/security/tomoyo/query"; static int query_fd = EOF; static int time_to_wait = 0; static char **action_to_take = NULL; static int minimal_interval = 0; static void ccs_notifyd_init_rules(const char *filename) { static _Bool first = 1; FILE *fp = fopen(filename, "r"); unsigned int line_no = 0; char *action = NULL; if (!first) { free(action_to_take); action_to_take = NULL; time_to_wait = 0; minimal_interval = 0; } if (!fp) { if (first) fprintf(stderr, "Can't open %s for reading.\n", filename); else syslog(LOG_WARNING, "Can't open %s for reading.\n", filename); exit(1); } ccs_get(); while (true) { char *line = ccs_freadline(fp); if (!line) break; line_no++; ccs_normalize_line(line); if (*line == '#' || !*line) continue; if (sscanf(line, "time_to_wait %u", &time_to_wait) == 1 || sscanf(line, "minimal_interval %u", &minimal_interval) == 1) continue; if (!ccs_str_starts(line, "action_to_take ")) continue; if (!*line) goto invalid_rule; if (action) goto invalid_rule; action = ccs_strdup(line); } ccs_put(); fclose(fp); if (!action) { if (first) fprintf(stderr, "No actions defined in %s .\n", filename); else syslog(LOG_WARNING, "No actions defined in %s .\n", filename); exit(1); } { int count = 0; char *sp = action; while (true) { char *cp = strsep(&sp, " "); action_to_take = ccs_realloc(action_to_take, sizeof(char *) * ++count); action_to_take[count - 1] = cp; if (!cp) break; if (!ccs_decode(cp, cp)) goto invalid_rule; } } first = 0; return; invalid_rule: if (first) fprintf(stderr, "Invalid rule at line %u in %s .\n", line_no, filename); else syslog(LOG_WARNING, "Invalid rule at line %u in %s .\n", line_no, filename); exit(1); } static void block_sighup(const _Bool block) { sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGHUP); sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sigset, NULL); } static void main_loop(void) { static char buffer[32768]; while (query_fd != EOF) { int pipe_fd[2]; pid_t pid; memset(buffer, 0, sizeof(buffer)); while (read(query_fd, buffer, sizeof(buffer) - 1) <= 0) { /* Wait for data. */ struct pollfd pfd = { .fd = query_fd, .events = POLLIN, }; if (poll(&pfd, 1, -1) == EOF && errno != EINTR) return; } if (pipe(pipe_fd) == EOF) { syslog(LOG_WARNING, "Can't create pipe.\n"); return; } block_sighup(1); pid = fork(); if (pid == -1) { syslog(LOG_WARNING, "Can't fork().\n"); return; } if (!pid) { int ret_ignored; ret_ignored = close(query_fd); ret_ignored = close(pipe_fd[1]); ret_ignored = close(0); ret_ignored = dup2(pipe_fd[0], 0); ret_ignored = close(pipe_fd[0]); execvp(action_to_take[0], action_to_take); syslog(LOG_WARNING, "Can't execute %s\n", action_to_take[0]); closelog(); _exit(1); } else { int ret_ignored; int len = strlen(buffer); close(pipe_fd[0]); /* This is OK because read() < sizeof(buffer). */ buffer[len++] = '\n'; ret_ignored = write(pipe_fd[1], buffer, len); close(pipe_fd[1]); } block_sighup(0); while (time_to_wait-- > 0) { int ret_ignored; sleep(1); ret_ignored = write(query_fd, "\n", 1); } close(query_fd); while (waitpid(pid, NULL, __WALL) == EOF && errno == EINTR); sleep(minimal_interval); do { query_fd = open(proc_policy_query, O_RDWR); } while (query_fd == EOF && errno == EINTR); } } static void ccs_reload_config(int sig) { block_sighup(1); syslog(LOG_WARNING, "Reloading configuration file.\n"); ccs_notifyd_init_rules(CCS_NOTIFYD_CONF); block_sighup(0); } int main(int argc, char *argv[]) { unsetenv("SHELLOPTS"); /* Make sure popen() executes commands. */ if (argc != 1) goto usage; ccs_notifyd_init_rules(CCS_NOTIFYD_CONF); ccs_mount_securityfs(); query_fd = open(proc_policy_query, O_RDWR); if (query_fd == EOF) { fprintf(stderr, "You can't run this daemon for this kernel." "\n"); return 1; } else if (time_to_wait && write(query_fd, "", 0) != 0) { fprintf(stderr, "You need to register this program to %s to " "run this program.\n", "/sys/kernel/security/tomoyo/manager"); return 1; } umask(0); switch (fork()) { case 0: break; case -1: fprintf(stderr, "Can't fork()\n"); return 1; default: return 0; } if (setsid() == EOF) { fprintf(stderr, "Can't setsid()\n"); return 1; } switch (fork()) { case 0: break; case -1: fprintf(stderr, "Can't fork()\n"); return 1; default: return 0; } if (chdir("/")) { fprintf(stderr, "Can't chdir()\n"); return 1; } { /* Get exclusive lock. */ int fd = open("/proc/self/exe", O_RDONLY); if (flock(fd, LOCK_EX | LOCK_NB) == EOF) return 0; } close(0); close(1); close(2); openlog("tomoyo-notifyd", 0, LOG_USER); syslog(LOG_WARNING, "Started.\n"); signal(SIGHUP, ccs_reload_config); main_loop(); syslog(LOG_WARNING, "Terminated.\n"); closelog(); return 1; usage: fprintf(stderr, "%s\n See %s for configuration.\n", argv[0], CCS_NOTIFYD_CONF); return 1; } tomoyo-tools/usr_sbin/tomoyo-savepolicy.c0000644000000000000000000001247314116520000017726 0ustar rootroot/* * tomoyo-savepolicy.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" static const char *ccs_policy_dir = NULL; static _Bool ccs_cat_file(const char *path) { FILE *fp = ccs_open_read(path); _Bool result = true; if (!fp) { fprintf(stderr, "Can't open %s\n", path); return false; } while (true) { int c = fgetc(fp); if (ccs_network_mode && !c) break; if (c == EOF) break; if (putchar(c) == EOF) result = false; } fclose(fp); return result; } static _Bool ccs_save_policy(void) { time_t now = time(NULL); char stamp[32] = { }; while (1) { struct tm *tm = localtime(&now); snprintf(stamp, sizeof(stamp) - 1, "%02d-%02d-%02d.%02d:%02d:%02d/", tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); if (!mkdir(stamp, 0700)) break; else if (errno == EEXIST) now++; else { fprintf(stderr, "Can't create %s/%s .\n", ccs_policy_dir, stamp); return false; } } if ((symlink("policy/current/profile.conf", "../profile.conf") && errno != EEXIST) || (symlink("policy/current/manager.conf", "../manager.conf") && errno != EEXIST) || (symlink("policy/current/exception_policy.conf", "../exception_policy.conf") && errno != EEXIST) || (symlink("policy/current/domain_policy.conf", "../domain_policy.conf") && errno != EEXIST) || chdir(stamp) || !ccs_move_proc_to_file(CCS_PROC_POLICY_PROFILE, "profile.conf") || !ccs_move_proc_to_file(CCS_PROC_POLICY_MANAGER, "manager.conf") || !ccs_move_proc_to_file(CCS_PROC_POLICY_EXCEPTION_POLICY, "exception_policy.conf") || !ccs_move_proc_to_file(CCS_PROC_POLICY_DOMAIN_POLICY, "domain_policy.conf") || chdir("..") || (rename("current", "previous") && errno != ENOENT) || symlink(stamp, "current")) { fprintf(stderr, "Failed to save policy.\n"); return false; } return true; } int main(int argc, char *argv[]) { char target = 0; int i; for (i = 1; i < argc; i++) { char *ptr = argv[i]; char *cp = strchr(ptr, ':'); if (*ptr == '/') { if (ccs_policy_dir || target) { fprintf(stderr, "You cannot specify multiple " "%s at the same time.\n\n", "policy directories"); goto usage; } ccs_policy_dir = ptr; } else if (cp) { *cp++ = '\0'; ccs_network_ip = inet_addr(ptr); ccs_network_port = htons(atoi(cp)); if (ccs_network_mode) { fprintf(stderr, "You cannot specify multiple " "%s at the same time.\n\n", "remote agents"); goto usage; } ccs_network_mode = true; } else if (*ptr++ == '-' && !target) { target = *ptr++; if (target != 'e' && target != 'd' && target != 'p' && target != 'm' && target != 's') goto usage; if (*ptr || ccs_policy_dir) { fprintf(stderr, "You cannot specify multiple " "%s at the same time.\n\n", "policies"); goto usage; } } else goto usage; } if (ccs_network_mode) { if (!ccs_check_remote_host()) return 1; } else if (ccs_mount_securityfs(), access(CCS_PROC_POLICY_DIR, F_OK)) { fprintf(stderr, "You can't run this program for this kernel.\n"); return 1; } if (target) { const char *file; switch (target) { case 'p': file = CCS_PROC_POLICY_PROFILE; break; case 'm': file = CCS_PROC_POLICY_MANAGER; break; case 'e': file = CCS_PROC_POLICY_EXCEPTION_POLICY; break; case 'd': file = CCS_PROC_POLICY_DOMAIN_POLICY; break; default: file = CCS_PROC_POLICY_STAT; break; } return !ccs_cat_file(file); } if (!ccs_policy_dir) { if (ccs_network_mode && !target) { fprintf(stderr, "You need to specify %s.\n\n", "policy directory"); goto usage; } ccs_policy_dir = "/etc/tomoyo/"; } if (chdir(ccs_policy_dir) || chdir("policy/")) { fprintf(stderr, "Directory %s/policy/ doesn't exist.\n", ccs_policy_dir); return 1; } return !ccs_save_policy(); usage: printf("%s [policy_dir [remote_ip:remote_port]]\n" "%s [{-e|-d|-p|-m|-s} [remote_ip:remote_port]]\n\n" "policy_dir : Use policy_dir rather than /etc/tomoyo/ directory.\n" "remote_ip:remote_port : Read from tomoyo-editpolicy-agent " "listening at remote_ip:remote_port rather than /sys/kernel/security/tomoyo/ " "directory.\n" "-e : Print /sys/kernel/security/tomoyo/exception_policy to stdout.\n" "-d : Print /sys/kernel/security/tomoyo/domain_policy to stdout.\n" "-p : Print /sys/kernel/security/tomoyo/profile to stdout.\n" "-m : Print /sys/kernel/security/tomoyo/manager to stdout.\n" "-s : Print /sys/kernel/security/tomoyo/stat to stdout.\n", argv[0], argv[0]); return 1; } tomoyo-tools/usr_sbin/editpolicy_color.c0000644000000000000000000001637114116520000017570 0ustar rootroot/* * editpolicy_color.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" #include "editpolicy.h" #ifdef COLOR_ON /** * editpolicy_color_init - Initialize line coloring table. * * Returns nothing. */ void editpolicy_color_init(void) { static struct ccs_color_env_t { enum color_type tag; short int fore; short int back; const char *name; } color_env[] = { { COLOR_DOMAIN_HEAD, COLOR_BLACK, COLOR_GREEN, "DOMAIN_HEAD" }, { COLOR_DOMAIN_CURSOR, COLOR_BLACK, COLOR_GREEN, "DOMAIN_CURSOR" }, { COLOR_EXCEPTION_HEAD, COLOR_BLACK, COLOR_CYAN, "EXCEPTION_HEAD" }, { COLOR_EXCEPTION_CURSOR, COLOR_BLACK, COLOR_CYAN, "EXCEPTION_CURSOR" }, { COLOR_ACL_HEAD, COLOR_BLACK, COLOR_YELLOW, "ACL_HEAD" }, { COLOR_ACL_CURSOR, COLOR_BLACK, COLOR_YELLOW, "ACL_CURSOR" }, { COLOR_PROFILE_HEAD, COLOR_WHITE, COLOR_RED, "PROFILE_HEAD" }, { COLOR_PROFILE_CURSOR, COLOR_WHITE, COLOR_RED, "PROFILE_CURSOR" }, { COLOR_MANAGER_HEAD, COLOR_WHITE, COLOR_GREEN, "MANAGER_HEAD" }, { COLOR_MANAGER_CURSOR, COLOR_WHITE, COLOR_GREEN, "MANAGER_CURSOR" }, { COLOR_STAT_HEAD, COLOR_BLACK, COLOR_YELLOW, "STAT_HEAD" }, { COLOR_STAT_CURSOR, COLOR_BLACK, COLOR_YELLOW, "STAT_CURSOR" }, { COLOR_DEFAULT_COLOR, COLOR_WHITE, COLOR_BLACK, "DEFAULT_COLOR" }, { COLOR_NORMAL, COLOR_WHITE, COLOR_BLACK, NULL } }; FILE *fp = fopen(CCS_EDITPOLICY_CONF, "r"); int i; if (!fp) goto use_default; ccs_get(); while (true) { char *line = ccs_freadline(fp); char *cp; if (!line) break; if (!ccs_str_starts(line, "line_color ")) continue; cp = strchr(line, '='); if (!cp) continue; *cp++ = '\0'; ccs_normalize_line(line); ccs_normalize_line(cp); if (!*line || !*cp) continue; for (i = 0; color_env[i].name; i++) { short int fore; short int back; if (strcmp(line, color_env[i].name)) continue; if (strlen(cp) != 2) break; fore = (*cp++) - '0'; /* foreground color */ back = (*cp) - '0'; /* background color */ if (fore < 0 || fore > 7 || back < 0 || back > 7) break; color_env[i].fore = fore; color_env[i].back = back; break; } } ccs_put(); fclose(fp); use_default: start_color(); for (i = 0; color_env[i].name; i++) { struct ccs_color_env_t *colorp = &color_env[i]; init_pair(colorp->tag, colorp->fore, colorp->back); } init_pair(COLOR_DISP_ERR, COLOR_RED, COLOR_BLACK); /* error message */ bkgdset(A_NORMAL | COLOR_PAIR(COLOR_DEFAULT_COLOR) | ' '); for (i = 0; i < MAX_SCREEN_TYPE; i++) screen[i].saved_color_current = -1; } /** * editpolicy_color_save - Save or load current color. * * @flg: True if save request, false otherwise. * * Returns nothing. */ static void editpolicy_color_save(const _Bool flg) { static attr_t save_color = COLOR_DEFAULT_COLOR; if (flg) save_color = getattrs(stdscr); else attrset(save_color); } /** * editpolicy_color_change - Change current color. * * @attr: Coloe to use. * @flg: True if turn on, false otherwise. * * Returns nothing. */ void editpolicy_color_change(const attr_t attr, const _Bool flg) { if (flg) attron(COLOR_PAIR(attr)); else attroff(COLOR_PAIR(attr)); } /** * editpolicy_attr_change - Change current attribute. * * @attr: Coloe to use. * @flg: True if turn on, false otherwise. * * Returns nothing. */ void editpolicy_attr_change(const attr_t attr, const _Bool flg) { if (flg) attron(attr); else attroff(attr); } /** * editpolicy_sttr_save - Save current color. * * Returns nothing. */ void editpolicy_sttr_save(void) { editpolicy_color_save(true); } /** * editpolicy_sttr_restore - Load current color. * * Returns nothing. */ void editpolicy_sttr_restore(void) { editpolicy_color_save(false); } /** * editpolicy_color_head - Get color to use for header line. * * Returns one of values in "enum color_type". */ enum color_type editpolicy_color_head(void) { switch (active) { case SCREEN_DOMAIN_LIST: return COLOR_DOMAIN_HEAD; case SCREEN_EXCEPTION_LIST: return COLOR_EXCEPTION_HEAD; case SCREEN_PROFILE_LIST: return COLOR_PROFILE_HEAD; case SCREEN_MANAGER_LIST: return COLOR_MANAGER_HEAD; case SCREEN_STAT_LIST: return COLOR_STAT_HEAD; default: return COLOR_ACL_HEAD; } } /** * editpolicy_color_cursor - Get color to use for cursor line. * * Returns one of values in "enum color_type". */ static inline enum color_type editpolicy_color_cursor(void) { switch (active) { case SCREEN_DOMAIN_LIST: return COLOR_DOMAIN_CURSOR; case SCREEN_EXCEPTION_LIST: return COLOR_EXCEPTION_CURSOR; case SCREEN_PROFILE_LIST: return COLOR_PROFILE_CURSOR; case SCREEN_MANAGER_LIST: return COLOR_MANAGER_CURSOR; case SCREEN_STAT_LIST: return COLOR_STAT_CURSOR; default: return COLOR_ACL_CURSOR; } } /** * editpolicy_line_draw - Update colored line. * * Returns nothing. */ void editpolicy_line_draw(void) { struct ccs_screen *ptr = &screen[active]; const int current = editpolicy_get_current(); int y; int x; if (current == EOF) return; getyx(stdscr, y, x); if (-1 < ptr->saved_color_current && current != ptr->saved_color_current) { move(CCS_HEADER_LINES + ptr->saved_color_y, 0); chgat(-1, A_NORMAL, COLOR_DEFAULT_COLOR, NULL); } move(y, x); chgat(-1, A_NORMAL, editpolicy_color_cursor(), NULL); touchwin(stdscr); ptr->saved_color_current = current; ptr->saved_color_y = ptr->y; } #else /** * editpolicy_color_init - Initialize line coloring table. * * Returns nothing. */ void editpolicy_color_init(void) { } /** * editpolicy_color_change - Change current color. * * @attr: Coloe to use. * @flg: True if turn on, false otherwise. * * Returns nothing. */ void editpolicy_color_change(const attr_t attr, const _Bool flg) { } /** * editpolicy_attr_change - Change current attribute. * * @attr: Coloe to use. * @flg: True if turn on, false otherwise. * * Returns nothing. */ void editpolicy_attr_change(const attr_t attr, const _Bool flg) { } /** * editpolicy_sttr_save - Save current color. * * Returns nothing. */ void editpolicy_sttr_save(void) { } /** * editpolicy_sttr_restore - Load current color. * * Returns nothing. */ void editpolicy_sttr_restore(void) { } /** * editpolicy_color_head - Get color to use for header line. * * Returns one of values in "enum color_type". */ enum color_type editpolicy_color_head(void) { return COLOR_DEFAULT_COLOR; } /** * editpolicy_line_draw - Update colored line. * * Returns nothing. */ void editpolicy_line_draw(void) { } #endif tomoyo-tools/usr_sbin/editpolicy.c0000644000000000000000000027375114116520000016401 0ustar rootroot/* * editpolicy.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2012 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "tomoyotools.h" #include "editpolicy.h" #include "readline.h" /* Window information */ static struct window { /* Domain policy. */ struct domain_policy dp; /* Policy directory. */ const char *policy_dir; /* Policy file's name. */ const char *policy_file; /* * Array of "reset_domain"/"no_reset_domain"/"initialize_domain"/ * "no_initialize_domain"/"keep_domain"/"no_keep_domain" entries. */ struct transition_entry *transition_list; /* Structure for holding domain transition preference. */ struct transition_preference *preference_list; /* * List of * "task manual_domain_transition"/"auto_domain_transition=" part. */ char **jump_list; /* Last error message. */ char *last_error; /* Caption of the current screen. */ const char *caption; /* Currently selected domain. */ char *current_domain; /* Currently selected PID. */ unsigned int current_pid; /* Number of domain jump source domains. */ int unnumbered_domains; /* Length of transition_list array. */ int transition_list_len; /* Length of preference_list array. */ int preference_list_len; /* Length of jump_list array. */ int jump_list_len; /* Width of CUI screen. */ int width; /* Height of CUI screen. */ int height; /* Number of entries available on current screen. */ int list_items; /* Lines available for displaying ACL entries. */ int body_lines; /* Columns to shift. */ int eat_col; /* Max columns. */ int max_col; /* Refresh interval in second. 0 means no auto refresh. */ unsigned int refresh_interval; /* Previously active screen's index. */ enum screen_type previous_screen; /* Sort ACL by operand first? */ _Bool sort_acl; /* Sort profiles by value? */ _Bool sort_profile; /* * Domain screen is dealing with process list rather than domain list? */ _Bool show_tasklist; /* Start from the first line when showing ACL screen? */ _Bool no_restore_cursor; _Bool force_move_cursor; /* Need to reload the screen due to auto refresh? */ _Bool need_reload; /* Use ccs-editpolicy-agent program? */ _Bool offline_mode; /* Use readonly mode? */ _Bool readonly_mode; } w; /* Cursor info for CUI screen. */ struct ccs_screen screen[MAX_SCREEN_TYPE] = { }; /* Currently active screen's index. */ enum screen_type active = SCREEN_DOMAIN_LIST; /* Currently loaded policy. */ struct policy p; /* Namespace to use. */ const struct ccs_path_info *current_ns = NULL; /* Readline history. */ static struct ccs_readline_data rl = { }; /* Domain transition coltrol keywords. */ static const char *transition_type[MAX_TRANSITION_TYPE] = { [TRANSITION_RESET] = "reset_domain ", [TRANSITION_NO_RESET] = "no_reset_domain ", [TRANSITION_INITIALIZE] = "initialize_domain ", [TRANSITION_NO_INITIALIZE] = "no_initialize_domain ", [TRANSITION_KEEP] = "keep_domain ", [TRANSITION_NO_KEEP] = "no_keep_domain ", }; static _Bool is_deleted_domain(const int index); static _Bool is_jump_source(const int index); static _Bool is_jump_target(const int index); static _Bool is_keeper_domain(const int index); static _Bool is_unreachable_domain(const int index); static _Bool select_item(void); static _Bool show_command_key(const enum screen_type screen, const _Bool readonly); static const char *shift(const char *str); static const char *get_last_name(const int index); static const struct transition_entry *find_transition (const struct ccs_path_info *ns, const char *domainname, const char *program); static enum screen_type generic_list_loop(void); static enum screen_type select_window(void); static FILE *editpolicy_open_write(const char *filename); static int add_address_group(const char *group_name, const char *member_name); static int add_address_group_policy(char *data); static int add_number_group(const char *group_name, const char *member_name); static int add_number_group_policy(char *data); static int add_path_group(const struct ccs_path_info *ns, const char *group_name, const char *member_name); static int add_path_group_policy(const struct ccs_path_info *ns, char *data); static int add_transition_entry(const struct ccs_path_info *ns, const char *domainname, const char *program, const enum transition_type type); static int add_transition_policy(const struct ccs_path_info *ns, char *data, const enum transition_type type); static int count_domainlist(void); static int count_generic(void); static int count_tasklist(void); static int domain_compare(const void *a, const void *b); static int generic_compare(const void *a, const void *b); static int profile_compare(const void *a, const void *b); static int show_acl_line(const int index, const int list_indent); static int show_domain_line(const int index); static int show_literal_line(const int index); static int show_profile_line(const int index); static int show_stat_line(const int index); static int string_compare(const void *a, const void *b); static void add_acl_group_policy(const int group, const char *data); static void add_entry(void); static void adjust_cursor_pos(const int item_count); static void assign_djs(const struct ccs_path_info *ns, const char *domainname, const char *program); static void copy_file(const char *source, const char *dest); static void delete_entry(void); static void down_arrow_key(void); static void editpolicy_clear_groups(void); static void find_entry(const _Bool input, const _Bool forward); static void page_down_key(void); static void page_up_key(void); static void read_domain_and_exception_policy(void); static void read_generic_policy(void); static void resize_window(void); static void set_cursor_pos(const int index); static void set_level(void); static void set_profile(void); static void set_quota(void); static void show_current(void); static void show_list(void); static void sigalrm_handler(int sig); static void up_arrow_key(void); #define ccs_alloc(ptr, size, count) \ ({ \ ptr = ccs_realloc((ptr), (size) * ((count) + 1)); \ memset(&ptr[(count)], 0, size); \ &ptr[(count)++]; \ }) /** * find_domain - Find a domain by name and other attributes. * * @domainname: Name of domain to find. * @target: Name of target to find. Maybe NULL. * @is_dd: True if the domain is marked as deleted, false otherwise. * * Returns index number (>= 0) if found, EOF otherwise. */ static int find_domain(const char *domainname, const char *target, const _Bool is_dd) { int i; for (i = 0; i < w.dp.list_len; i++) { const struct ccs_domain *ptr = &w.dp.list[i]; if (ptr->is_dd == is_dd && ((!ptr->target && !target) || (ptr->target && target && !strcmp(ptr->target->name, target))) && !strcmp(ptr->domainname->name, domainname)) return i; } return EOF; } /** * find_domain_by_name - Find a domain by name. * * @domainname: Name of domain to find. * * Returns pointer to "struct ccs_domain" if found, NULL otherwise. */ static struct ccs_domain *find_domain_by_name(const char *domainname) { int i; for (i = 0; i < w.dp.list_len; i++) { struct ccs_domain *ptr = &w.dp.list[i]; if (!ptr->target && !strcmp(ptr->domainname->name, domainname)) return ptr; } return NULL; } /** * assign_domain - Create a domain by name and other attributes. * * @domainname: Name of domain to find. * @target: Name of target domain if the domain acts as domain jump source, * NULL otherwise. * @is_dd: True if the domain is marked as deleted, false otherwise. * * Returns index number (>= 0) if created or already exists, abort otherwise. */ static int assign_domain(const char *domainname, const char *target, const _Bool is_dd) { struct ccs_domain *ptr; int index = find_domain(domainname, target, is_dd); if (index >= 0) return index; ptr = ccs_alloc(w.dp.list, sizeof(*ptr), w.dp.list_len); ptr->domainname = ccs_savename(domainname); if (target) ptr->target = ccs_savename(target); ptr->is_dd = is_dd; return w.dp.list_len - 1; } /** * add_string_entry - Add string entry to a domain. * * @entry: String to add. * @index: Index in the @dp array. * * Returns 0 if successfully added or already exists, -EINVAL otherwise. */ static int add_string_entry(const char *entry, const int index) { const struct ccs_path_info **acl_ptr; int acl_count; const struct ccs_path_info *cp; int i; if (index < 0 || index >= w.dp.list_len) { fprintf(stderr, "ERROR: domain is out of range.\n"); return -EINVAL; } if (!entry || !*entry) return -EINVAL; cp = ccs_savename(entry); acl_ptr = w.dp.list[index].string_ptr; acl_count = w.dp.list[index].string_count; /* Check for the same entry. */ for (i = 0; i < acl_count; i++) /* Faster comparison, for they are ccs_savename'd. */ if (cp == acl_ptr[i]) return 0; *ccs_alloc(w.dp.list[index].string_ptr, sizeof(*acl_ptr), w.dp.list[index].string_count) = cp; return 0; } /** * clear_domain_policy - Clean up domain policy. * * Returns nothing. */ static void clear_domain_policy(void) { int index; for (index = 0; index < w.dp.list_len; index++) { free(w.dp.list[index].string_ptr); w.dp.list[index].string_ptr = NULL; w.dp.list[index].string_count = 0; } free(w.dp.list); w.dp.list = NULL; w.dp.list_len = 0; } /** * is_same_namespace - Check namespace. * * @domain: Domainname. * @ns: Namespace. * * Returns true if same namespace, false otherwise. */ static _Bool is_same_namespace(const char *domain, const struct ccs_path_info *ns) { return !strncmp(domain, ns->name, ns->total_len) && (domain[ns->total_len] == ' ' || !domain[ns->total_len]); } /** * is_current_namespace - Check namespace. * * @line: Line to check namespace. * * Returns true if this line deals current namespace, false otherwise. */ static _Bool is_current_namespace(const char *line) { return is_same_namespace(line, current_ns); } /** * copy_file - Copy local file to local or remote file. * * @source: Local file. * @dest: Local or remote file name. * * Returns nothing. */ static void copy_file(const char *source, const char *dest) { FILE *fp_in = fopen(source, "r"); FILE *fp_out = fp_in ? editpolicy_open_write(dest) : NULL; while (fp_in && fp_out) { int c = fgetc(fp_in); if (c == EOF) break; fputc(c, fp_out); } if (fp_out) fclose(fp_out); if (fp_in) fclose(fp_in); } /** * get_ns - Get namespace component from domainname. * * @domainname: A domainname. * * Returns the namespace component of @domainname. */ static const struct ccs_path_info *get_ns(const char *domainname) { const struct ccs_path_info *ns; char *line = ccs_strdup(domainname); char *cp; cp = strchr(line, ' '); if (cp) *cp = '\0'; ns = ccs_savename(line); free(line); return ns; } /** * get_last_word - Get last component of a line. * * @line: A line of words. * * Returns the last component of the line. */ static const char *get_last_word(const char *line) { const char *cp = strrchr(line, ' '); if (cp) return cp + 1; return line; } /** * get_last_name - Get last component of a domainname. * * @index: Index in the domain policy. * * Returns the last component of the domainname. */ static const char *get_last_name(const int index) { return get_last_word(w.dp.list[index].domainname->name); } /** * count_domainlist - Count non-zero elements in an array. * * Returns number of non-zero elements. */ static int count_domainlist(void) { int i; int c = 0; for (i = 0; i < w.dp.list_len; i++) if (w.dp.list_selected[i]) c++; return c; } /** * count_generic - Count non-zero elements in a "struct generic_entry" array. * * Returns number of non-zero elements. */ static int count_generic(void) { int i; int c = 0; for (i = 0; i < p.generic_len; i++) if (p.generic[i].selected) c++; return c; } /** * count_tasklist - Count non-zero elements in a "struct ccs_task_entry" array. * * Returns number of non-zero elements. */ static int count_tasklist(void) { int i; int c = 0; for (i = 0; i < ccs_task_list_len; i++) if (ccs_task_list[i].selected) c++; return c; } /** * is_keeper_domain - Check whether the given domain is marked as keeper or not. * * @index: Index in the domain policy. * * Returns true if the given domain is marked as "keep_domain", * false otherwise. */ static _Bool is_keeper_domain(const int index) { return w.dp.list[index].is_dk; } /** * is_jump_source - Check whether the given domain is marked as jump source or not. * * @index: Index in the domain policy. * * Returns true if the given domain is marked as domain jump source, * false otherwise. */ static _Bool is_jump_source(const int index) { return w.dp.list[index].target != NULL; } /** * is_jump_target - Check whether the given domain is marked as jump target or not. * * @index: Index in the domain policy. * * Returns true if the given domain is a domain jump target, false otherwise. */ static _Bool is_jump_target(const int index) { return w.dp.list[index].is_djt; } /** * is_unreachable_domain - Check whether the given domain is marked as unreachable or not. * * @index: Index in the domain policy. * * Returns true if the given domain is unreachable, false otherwise. */ static _Bool is_unreachable_domain(const int index) { return w.dp.list[index].is_du; } /** * is_deleted_domain - Check whether the given domain is marked as deleted or not. * * @index: Index in the domain policy. * * Returns true if the given domain is marked as deleted, false otherwise. */ static _Bool is_deleted_domain(const int index) { return w.dp.list[index].is_dd; } /** * string_compare - strcmp() for qsort() callback. * * @a: Pointer to "void". * @b: Pointer to "void". * * Returns return value of strcmp(). */ static int string_compare(const void *a, const void *b) { const struct generic_entry *a0 = (struct generic_entry *) a; const struct generic_entry *b0 = (struct generic_entry *) b; const char *a1 = a0->operand; const char *b1 = b0->operand; return strcmp(a1, b1); } /** * add_transition_policy - Add "reset_domain"/"no_reset_domain"/"initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain" entries. * * @ns: Pointer to "const struct ccs_path_info". * @data: Line to parse. * @type: One of values in "enum transition_type". * * Returns 0 on success, negative value otherwise. */ static int add_transition_policy (const struct ccs_path_info *ns, char *data, const enum transition_type type) { char *domainname = strstr(data, " from "); if (domainname) { *domainname = '\0'; domainname += 6; } else if (type == TRANSITION_NO_KEEP || type == TRANSITION_KEEP) { domainname = data; data = NULL; } return add_transition_entry(ns, domainname, data, type); } /** * add_path_group_policy - Add "path_group" entry. * * @ns: Pointer to "const struct ccs_path_info". * @data: Line to parse. * * Returns 0 on success, negative value otherwise. */ static int add_path_group_policy(const struct ccs_path_info *ns, char *data) { char *cp = strchr(data, ' '); if (!cp) return -EINVAL; *cp++ = '\0'; return add_path_group(ns, data, cp); } /** * add_number_group_policy - Add "number_group" entry. * * @data: Line to parse. * * Returns 0 on success, negative value otherwise. */ static int add_number_group_policy(char *data) { char *cp = strchr(data, ' '); if (!cp) return -EINVAL; *cp++ = '\0'; return add_number_group(data, cp); } /** * add_address_group_policy - Add "address_group" entry. * * @data: Line to parse. * * Returns 0 on success, negative value otherwise. */ static int add_address_group_policy(char *data) { char *cp = strchr(data, ' '); if (!cp) return -EINVAL; *cp++ = '\0'; return add_address_group(data, cp); } /** * add_acl_group_policys - Add "acl_group" entry. * * @group: Group number. * @data: Line to parse. * * Returns nothing. */ static void add_acl_group_policy(const int group, const char *data) { char **ptr = p.acl_group[group]; const int len = p.acl_group_len[group]; int i; for (i = 0; i < len; i++) if (!strcmp(ptr[i], data)) return; *ccs_alloc(p.acl_group[group], sizeof(char *), p.acl_group_len[group]) = ccs_strdup(data); } /** * editpolicy_clear_groups - Clear path_group/number_group/address_group/acl_group for reloading policy. * * Returns nothing. */ static void editpolicy_clear_groups(void) { int i; for (i = 0; i < 256; i++) while (p.acl_group_len[i]) free(p.acl_group[i][--p.acl_group_len[i]]); while (p.path_group_len) free(p.path_group[--p.path_group_len]. member_name); while (p.number_group_len) free(p.number_group[--p.number_group_len].member_name); while (p.address_group_len) free(p.address_group[--p.address_group_len].member_name); } /** * find_path_group_ns - Find "path_group" entry. * * @ns: Pointer to "const struct ccs_path_info". * @group_name: Name of path group. * * Returns pointer to "struct path_group" if found, NULL otherwise. */ struct path_group *find_path_group_ns (const struct ccs_path_info *ns, const char *group_name) { int i; for (i = 0; i < p.path_group_len; i++) if (!ccs_pathcmp(p.path_group[i].ns, ns) && !strcmp(group_name, p.path_group[i].group_name->name)) return &p.path_group[i]; return NULL; } /** * assign_djs - Assign domain jump source domain. * * @ns: Pointer to "const struct ccs_path_info". * @domainname: Domainname. * @program: Program name. */ static void assign_djs(const struct ccs_path_info *ns, const char *domainname, const char *program) { const struct transition_entry *d_t = find_transition(ns, domainname, program); if (!d_t) return; if (d_t->type == TRANSITION_INITIALIZE || d_t->type == TRANSITION_RESET) { char *line; char *cp; ccs_get(); if (d_t->type == TRANSITION_INITIALIZE) line = ccs_shprintf("%s %s", domainname, program); else line = ccs_shprintf("%s <%s>", domainname, program); ccs_normalize_line(line); cp = ccs_strdup(line); if (d_t->type == TRANSITION_INITIALIZE) line = ccs_shprintf("%s %s", ns->name, program); else line = ccs_shprintf("<%s>", program); assign_domain(cp, line, false); free(cp); ccs_put(); } } /** * domain_compare - strcmp() for qsort() callback. * * @a: Pointer to "void". * @b: Pointer to "void". * * Returns return value of strcmp(). */ static int domain_compare(const void *a, const void *b) { const struct ccs_domain *a0 = a; const struct ccs_domain *b0 = b; char *name1; char *name2; char *line; char *cp; int k; if (!a0->target && !b0->target) return strcmp(a0->domainname->name, b0->domainname->name); name1 = ccs_strdup(a0->domainname->name); if (a0->target) { cp = strrchr(name1, ' '); if (cp) *cp = '\0'; } name2 = ccs_strdup(b0->domainname->name); if (b0->target) { cp = strrchr(name2, ' '); if (cp) *cp = '\0'; } k = strcmp(name1, name2); if (k) goto done; ccs_get(); if (a0->target) line = ccs_shprintf("%s %s", name1, a0->target->name); else line = ccs_shprintf("%s", name1); free(name1); name1 = ccs_strdup(line); if (b0->target) line = ccs_shprintf("%s %s", name2, b0->target->name); else line = ccs_shprintf("%s", name2); free(name2); name2 = ccs_strdup(line); ccs_put(); k = strcmp(name1, name2); done: free(name1); free(name2); return k; } /** * find_target_domain - Find the domain jump target domain. * * @index: Index in the domain policy. * * Returns index of the domain if found in a current namespace, * -2 if found in a different namespace, EOF otherwise. */ static int find_target_domain(const int index) { const char *cp = w.dp.list[index].target->name; if (!is_current_namespace(cp)) { if (w.dp.list[index].is_du) return EOF; return -2; } return find_domain(cp, NULL, false); } /** * show_domain_line - Show a line of the domain transition tree. * * @index: Index in the domain policy. * * Returns length of the printed line. */ static int show_domain_line(const int index) { int tmp_col = 0; const struct transition_entry *transition; char *line; const char *sp; const int number = w.dp.list[index].number; int redirect_index; const bool is_djs = is_jump_source(index); const bool is_deleted = is_deleted_domain(index); if (number >= 0) printw("%c%4d:%3u %c%c%c ", w.dp.list_selected[index] ? '&' : ' ', number, w.dp.list[index].profile, is_keeper_domain(index) ? '#' : ' ', is_jump_target(index) ? '*' : ' ', is_unreachable_domain(index) ? '!' : ' '); else if (w.dp.list[index].is_djt) printw(" %c*%c ", is_keeper_domain(index) ? '#' : ' ', is_unreachable_domain(index) ? '!' : ' '); else printw(" "); tmp_col += 14; sp = w.dp.list[index].domainname->name; while (true) { const char *cp = strchr(sp, ' '); if (!cp) break; printw("%s", shift(" ")); tmp_col += 4; sp = cp + 1; } if (is_djs) { printw("%s", shift("=> ")); tmp_col += 3; sp = w.dp.list[index].target->name; } if (is_deleted) { printw("%s", shift("( ")); tmp_col += 2; } printw("%s", shift(sp)); tmp_col += strlen(sp); if (is_deleted) { printw("%s", shift(" )")); tmp_col += 2; } transition = w.dp.list[index].d_t; if (!transition || is_djs) goto no_transition_control; ccs_get(); line = ccs_shprintf(" ( %s%s from %s )", transition_type[transition->type], transition->program ? transition->program->name : "any", transition->domainname ? transition->domainname->name : "any"); printw("%s", shift(line)); tmp_col += strlen(line); ccs_put(); goto done; no_transition_control: if (!is_djs) goto done; ccs_get(); redirect_index = find_target_domain(index); if (redirect_index >= 0) line = ccs_shprintf(" ( -> %d )", w.dp.list[redirect_index].number); else if (redirect_index == EOF) line = ccs_shprintf(" ( -> Not Found )"); else line = ccs_shprintf(" ( -> Namespace jump )"); printw("%s", shift(line)); tmp_col += strlen(line); ccs_put(); done: return tmp_col; } /** * show_acl_line - Print an ACL line. * * @index: Index in the generic list. * @list_indent: Indent size. * * Returns length of the printed line. */ static int show_acl_line(const int index, const int list_indent) { const enum directive_type directive = p.generic[index].directive; const char *cp1 = directive_map[directive].alias; const char *cp2 = p.generic[index].operand; int len = list_indent - directive_map[directive].alias_len; printw("%c%4d: %s ", p.generic[index].selected ? '&' : ' ', index, shift(cp1)); while (len-- > 0) printw("%s", shift(" ")); printw("%s", shift(cp2)); return strlen(cp1) + strlen(cp2) + 8 + list_indent; } /** * show_profile_line - Print a profile line. * * @index: Index in the generic list. * * Returns length of the printed line. */ static int show_profile_line(const int index) { const char *cp = p.generic[index].operand; const u16 profile = p.generic[index].directive; char number[8] = ""; if (profile <= 256) snprintf(number, sizeof(number) - 1, "%3u-", profile); printw("%c%4d: %s", p.generic[index].selected ? '&' : ' ', index, shift(number)); printw("%s ", shift(cp)); return strlen(number) + strlen(cp) + 8; } /** * show_literal_line - Print a literal line. * * @index: Index in the generic list. * * Returns length of the printed line. */ static int show_literal_line(const int index) { const char *cp = p.generic[index].operand; printw("%c%4d: %s ", p.generic[index].selected ? '&' : ' ', index, shift(cp)); return strlen(cp) + 8; } /** * show_stat_line - Print a statistics line. * * @index: Index in the generic list. * * Returns length of the printed line. */ static int show_stat_line(const int index) { char *line; unsigned int now; ccs_get(); line = ccs_shprintf("%s", p.generic[index].operand); if (line[0]) printw("%s", shift(line)); now = strlen(line); ccs_put(); return now; } /** * show_command_key - Print help screen. * * @screen: Currently selected screen. * @readonly: True if readonly_mopde, false otherwise. * * Returns true to continue, false to quit. */ static _Bool show_command_key(const enum screen_type screen, const _Bool readonly) { int c; clear(); printw("Commands available for this screen are:\n\n"); printw("Q/q Quit this editor.\n"); printw("R/r Refresh to the latest information.\n"); switch (screen) { case SCREEN_STAT_LIST: break; default: printw("F/f Find first.\n"); printw("N/n Find next.\n"); printw("P/p Find previous.\n"); } printw("W/w Switch to selected screen.\n"); /* printw("Tab Switch to next screen.\n"); */ switch (screen) { case SCREEN_STAT_LIST: break; default: printw("Insert Copy an entry at the cursor position to " "history buffer.\n"); printw("Space Invert selection state of an entry at " "the cursor position.\n"); printw("C/c Copy selection state of an entry at " "the cursor position to all entries below the cursor " "position.\n"); } switch (screen) { case SCREEN_NS_LIST: if (!readonly) printw("A/a Add a new namespace.\n"); break; case SCREEN_DOMAIN_LIST: if (w.show_tasklist) { printw("S/s Set profile number of selected " "processes.\n"); printw("Enter Edit ACLs of a process at the " "cursor position.\n"); } else { if (!readonly) { printw("A/a Add a new domain.\n"); printw("D/d Delete selected domains." "\n"); printw("S/s Set profile number of " "selected domains.\n"); } printw("Enter Edit ACLs of a domain at the " "cursor position.\n"); } break; case SCREEN_STAT_LIST: if (!readonly) printw("S/s Set memory quota of selected " "items.\n"); break; case SCREEN_PROFILE_LIST: if (!readonly) printw("S/s Set mode of selected items.\n"); break; default: break; } switch (screen) { case SCREEN_EXCEPTION_LIST: case SCREEN_ACL_LIST: case SCREEN_MANAGER_LIST: if (!readonly) { printw("A/a Add a new entry.\n"); printw("D/d Delete selected entries.\n"); } default: break; } switch (screen) { case SCREEN_PROFILE_LIST: if (!readonly) printw("A/a Define a new profile.\n"); default: break; } switch (screen) { case SCREEN_ACL_LIST: printw("O/o Set selection state to other entries " "included in an entry at the cursor position.\n"); /* Fall through. */ case SCREEN_PROFILE_LIST: printw("@ Switch sort type.\n"); break; case SCREEN_DOMAIN_LIST: if (!w.offline_mode) printw("@ Switch domain/process list.\n"); default: break; } printw("Arrow-keys and PageUp/PageDown/Home/End keys " "for scroll.\n\n"); printw("Press '?' to escape from this help.\n"); refresh(); while (true) { c = ccs_getch2(); if (c == '?' || c == EOF) break; if (c == 'Q' || c == 'q') return false; } return true; } /** * set_error - Set error line's caption. * * @filename: Filename to print. Maybe NULL. * * Returns nothing. */ static void set_error(const char *filename) { if (filename) { const int len = strlen(filename) + 128; w.last_error = ccs_realloc2(w.last_error, len); snprintf(w.last_error, len - 1, "Can't open %s .", filename); } else { free(w.last_error); w.last_error = NULL; } } /** * editpolicy_open_write - Wrapper for ccs_open_write(). * * @filename: File to open for writing. * * Returns pointer to "FILE" on success, NULL otherwise. * * Since CUI policy editor screen provides a line for printing error message, * this function sets error line if failed. Also, this function returns NULL if * readonly mode. */ static FILE *editpolicy_open_write(const char *filename) { FILE *fp = ccs_open_write(filename); if (!fp) set_error(filename); return fp; } /** * editpolicy_open_read - Wrapper for ccs_open_read(). * * @filename: File to open for reading. * * Returns pointer to "FILE" on success, NULL otherwise. * * Since CUI policy editor screen provides a line for printing error message, * this function sets error line if failed. */ static FILE *editpolicy_open_read(const char *filename) { FILE *fp = ccs_open_read(filename); if (!fp) set_error(filename); return fp; } /** * open2 - Wrapper for open(). * * @filename: File to open. * @mode: Flags to passed to open(). * * Returns file descriptor on success, EOF otherwise. * * Since CUI policy editor screen provides a line for printing error message, * this function sets error line if failed. */ static int open2(const char *filename, int mode) { const int fd = open(filename, mode); if (fd == EOF && errno != ENOENT) set_error(filename); return fd; } /** * sigalrm_handler - Callback routine for timer interrupt. * * @sig: Signal number. Not used. * * Returns nothing. * * This function is called when w.refresh_interval is non-zero. This function * marks current screen to reload. Also, this function reenables timer event. */ static void sigalrm_handler(int sig) { w.need_reload = true; alarm(w.refresh_interval); } /** * shift - Shift string data before displaying. * * @str: String to be displayed. * * Returns shifted string. */ static const char *shift(const char *str) { while (*str && w.eat_col) { str++; w.eat_col--; } return str; } /** * transition_control - Find domain transition control. * * @ns: Pointer to "const struct ccs_path_info". * @domainname: Domainname. * @program: Program name. * * Returns pointer to "const struct ccs_transition_entry" if found one, * NULL otherwise. */ static const struct transition_entry *find_transition (const struct ccs_path_info *ns, const char *domainname, const char *program) { int i; u8 type; struct ccs_path_info domain; struct ccs_path_info last_name; domain.name = domainname; last_name.name = get_last_word(domainname); ccs_fill_path_info(&domain); ccs_fill_path_info(&last_name); for (type = 0; type < MAX_TRANSITION_TYPE; type++) { next: for (i = 0; i < w.transition_list_len; i++) { struct transition_entry *ptr = &w.transition_list[i]; if (ptr->type != type) continue; if (ccs_pathcmp(ptr->ns, ns)) continue; if (ptr->domainname && ccs_pathcmp(ptr->domainname, &domain) && ccs_pathcmp(ptr->domainname, &last_name)) continue; if (ptr->program && strcmp(ptr->program->name, program)) continue; if (type == TRANSITION_NO_RESET) { /* * Do not check for reset_domain if * no_reset_domain matched. */ type = TRANSITION_NO_INITIALIZE; goto next; } if (type == TRANSITION_NO_INITIALIZE) { /* * Do not check for initialize_domain if * no_initialize_domain matched. */ type = TRANSITION_NO_KEEP; goto next; } if (type == TRANSITION_RESET || type == TRANSITION_INITIALIZE || type == TRANSITION_KEEP) return ptr; else return NULL; } } return NULL; } /** * profile_compare - strcmp() for qsort() callback. * * @a: Pointer to "void". * @b: Pointer to "void". * * Returns return value of strcmp(). */ static int profile_compare(const void *a, const void *b) { const struct generic_entry *a0 = (struct generic_entry *) a; const struct generic_entry *b0 = (struct generic_entry *) b; const enum directive_type a0_d = a0->directive; const enum directive_type b0_d = b0->directive; const char *a1 = a0->operand; const char *b1 = b0->operand; if (a0_d >= DIRECTIVE_ADDRESS_GROUP || b0_d >= DIRECTIVE_ADDRESS_GROUP) { if (a1[0] == 'P') return -1; if (b1[0] == 'P') return 1; } if (!w.sort_profile) { if (a0_d == b0_d) return strcmp(a1, b1); else return a0_d - b0_d; } else { const int a3 = strcspn(a1, "="); const int b3 = strcspn(b1, "="); const int c = strncmp(a1, b1, a3 >= b3 ? b3 : a3); if (c) return c; if (a3 != b3) return a3 - b3; else return a0_d - b0_d; } } /** * add_generic_entry - Add text lines. * * @line: Line to add. * @directive: One of values in "enum directive_type". * * Returns nothing. */ static void add_generic_entry(const char *line, const enum directive_type directive) { struct generic_entry *ptr; int i; for (i = 0; i < p.generic_len; i++) if (p.generic[i].directive == directive && !strcmp(line, p.generic[i].operand)) return; ptr = ccs_alloc(p.generic, sizeof(*ptr), p.generic_len); ptr->directive = directive; ptr->operand = ccs_strdup(line); } /** * read_generic_policy - Read policy data other than domain policy. * * Returns nothing. */ static void read_generic_policy(void) { FILE *fp = NULL; _Bool flag = false; const _Bool is_kernel_ns = !strcmp(current_ns->name, ""); while (p.generic_len) free((void *) p.generic[--p.generic_len].operand); if (active == SCREEN_ACL_LIST) { if (ccs_network_mode) /* We can read after write. */ fp = editpolicy_open_write(w.policy_file); else /* Don't set error message if failed. */ fp = fopen(w.policy_file, "r+"); if (fp) { if (w.show_tasklist) fprintf(fp, "select pid=%u\n", w.current_pid); else fprintf(fp, "select domain=%s\n", w.current_domain); if (ccs_network_mode) fputc(0, fp); fflush(fp); } } else if (active == SCREEN_NS_LIST) { add_generic_entry("", DIRECTIVE_NONE); } if (!fp) fp = editpolicy_open_read(w.policy_file); if (!fp) { set_error(w.policy_file); return; } ccs_freadline_raw = active == SCREEN_STAT_LIST; ccs_get(); while (true) { char *line = ccs_freadline_unpack(fp); enum directive_type directive; char *cp; if (!line) break; if (active == SCREEN_ACL_LIST) { if (ccs_domain_def(line)) { flag = !strcmp(line, w.current_domain); continue; } if (!flag || !line[0] || !strncmp(line, "use_profile ", 12)) continue; } else { if (!line[0]) continue; } if (active == SCREEN_EXCEPTION_LIST || active == SCREEN_PROFILE_LIST) { if (*line == '<') { cp = strchr(line, ' '); if (!cp++ || !is_current_namespace(line)) continue; memmove(line, cp, strlen(cp) + 1); } else if (!is_kernel_ns) continue; } switch (active) { case SCREEN_EXCEPTION_LIST: directive = find_directive(true, line); if (directive == DIRECTIVE_NONE) continue; /* Remember groups for editpolicy_optimize(). */ if (directive != DIRECTIVE_PATH_GROUP && directive != DIRECTIVE_NUMBER_GROUP && directive != DIRECTIVE_ADDRESS_GROUP && (directive < DIRECTIVE_ACL_GROUP_000 || directive > DIRECTIVE_ACL_GROUP_255)) break; cp = ccs_strdup(line); if (directive == DIRECTIVE_PATH_GROUP) add_path_group_policy(current_ns, cp); else if (directive == DIRECTIVE_NUMBER_GROUP) add_number_group_policy(cp); else if (directive == DIRECTIVE_ADDRESS_GROUP) add_address_group_policy(cp); else add_acl_group_policy (directive - DIRECTIVE_ACL_GROUP_000, cp); free(cp); break; case SCREEN_ACL_LIST: directive = find_directive(true, line); if (directive == DIRECTIVE_NONE) continue; break; case SCREEN_PROFILE_LIST: cp = strchr(line, '-'); if (cp) { *cp++ = '\0'; directive = atoi(line); memmove(line, cp, strlen(cp) + 1); } else directive = (u16) -1; break; case SCREEN_NS_LIST: if (*line != '<') continue; cp = strchr(line, ' '); if (!cp) continue; *cp = '\0'; if (!ccs_domain_def(line)) continue; /* Fall through. */ default: directive = DIRECTIVE_NONE; break; } add_generic_entry(line, directive); } ccs_put(); ccs_freadline_raw = false; fclose(fp); switch (active) { case SCREEN_ACL_LIST: case SCREEN_EXCEPTION_LIST: qsort(p.generic, p.generic_len, sizeof(struct generic_entry), generic_compare); break; case SCREEN_PROFILE_LIST: qsort(p.generic, p.generic_len, sizeof(struct generic_entry), profile_compare); break; case SCREEN_STAT_LIST: break; default: qsort(p.generic, p.generic_len, sizeof(struct generic_entry), string_compare); } } /** * add_transition_entry - Add "reset_domain"/"no_reset_domain"/"initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain" entries. * * @ns: Pointer to "const struct ccs_path_info". * @domainname: Domainname. * @program: Program name. * @type: One of values in "enum transition_type". * * Returns 0 on success, -EINVAL otherwise. */ static int add_transition_entry(const struct ccs_path_info *ns, const char *domainname, const char *program, const enum transition_type type) { struct transition_entry *ptr; if (program && strcmp(program, "any")) if (!ccs_correct_path(program)) return -EINVAL; if (domainname && strcmp(domainname, "any")) if (!ccs_correct_domain(domainname)) if (!ccs_correct_path(domainname)) return -EINVAL; ptr = ccs_alloc(w.transition_list, sizeof(*ptr), w.transition_list_len); ptr->ns = ns; if (program && strcmp(program, "any")) ptr->program = ccs_savename(program); if (domainname && strcmp(domainname, "any")) ptr->domainname = ccs_savename(domainname); ptr->type = type; return 0; } /** * add_path_group - Add "path_group" entry. * * @ns: Pointer to "const struct ccs_path_info". * @group_name: Name of address group. * @member_name: Address string. * * Returns 0 on success, negative value otherwise. */ static int add_path_group(const struct ccs_path_info *ns, const char *group_name, const char *member_name) { const struct ccs_path_info *saved_group_name; const struct ccs_path_info *saved_member_name; int i; int j; struct path_group *group = NULL; if (!ccs_correct_word(group_name) || !ccs_correct_word(member_name)) return -EINVAL; saved_group_name = ccs_savename(group_name); saved_member_name = ccs_savename(member_name); for (i = 0; i < p.path_group_len; i++) { group = &p.path_group[i]; if (group->ns != ns) continue; if (saved_group_name != group->group_name) continue; for (j = 0; j < group->member_name_len; j++) if (group->member_name[j] == saved_member_name) return 0; break; } if (i == p.path_group_len) { group = ccs_alloc(p.path_group, sizeof(*group), p.path_group_len); group->ns = ns; group->group_name = saved_group_name; } *ccs_alloc(group->member_name, sizeof(saved_member_name), group->member_name_len) = saved_member_name; return 0; } /** * add_number_group - Add "number_group" entry. * * @group_name: Name of number group. * @member_name: Number string. * * Returns 0 on success, negative value otherwise. */ static int add_number_group(const char *group_name, const char *member_name) { const struct ccs_path_info *saved_group_name; int i; int j; struct ccs_number_entry entry; struct number_group *group = NULL; if (ccs_parse_number(member_name, &entry)) return -EINVAL; if (!ccs_correct_word(group_name)) return -EINVAL; saved_group_name = ccs_savename(group_name); for (i = 0; i < p.number_group_len; i++) { group = &p.number_group[i]; if (saved_group_name != group->group_name) continue; for (j = 0; j < group->member_name_len; j++) if (!memcmp(&group->member_name[j], &entry, sizeof(entry))) return 0; break; } if (i == p.number_group_len) { group = ccs_alloc(p.number_group, sizeof(*group), p.number_group_len); group->group_name = saved_group_name; } *ccs_alloc(group->member_name, sizeof(entry), group->member_name_len) = entry; return 0; } /** * add_address_group - Add "address_group" entry. * * @group_name: Name of address group. * @member_name: Address string. * * Returns 0 on success, negative value otherwise. */ static int add_address_group(const char *group_name, const char *member_name) { const struct ccs_path_info *saved_group_name; int i; int j; struct ccs_ip_address_entry entry; struct address_group *group = NULL; if (ccs_parse_ip(member_name, &entry)) return -EINVAL; if (!ccs_correct_word(group_name)) return -EINVAL; saved_group_name = ccs_savename(group_name); for (i = 0; i < p.address_group_len; i++) { group = &p.address_group[i]; if (saved_group_name != group->group_name) continue; for (j = 0; j < group->member_name_len; j++) if (!memcmp(&group->member_name[j], &entry, sizeof(entry))) return 0; break; } if (i == p.address_group_len) { group = ccs_alloc(p.address_group, sizeof(*group), p.address_group_len); group->group_name = saved_group_name; } *ccs_alloc(group->member_name, sizeof(entry), group->member_name_len) = entry; return 0; } /** * add_condition_domain_transition - Add auto_domain_transition= part. * * @line: Line to parse. * @index: Current domain's index. * * Returns nothing. */ static void add_condition_domain_transition(char *line, const int index) { static char domainname[4096]; int source; char *cp = strrchr(line, ' '); if (!cp) return; if (strncmp(cp, " auto_domain_transition=\"", 25)) return; *cp = '\0'; cp += 25; source = strlen(cp); if (!source) return; cp[source - 1] = '\0'; snprintf(domainname, sizeof(domainname) - 1, "%s %s", w.dp.list[index].domainname->name, cp); domainname[sizeof(domainname) - 1] = '\0'; ccs_normalize_line(domainname); *ccs_alloc(w.jump_list, sizeof(char *), w.jump_list_len) = ccs_strdup(domainname); assign_domain(domainname, *cp == '<' ? cp : domainname, false); } /** * add_acl_domain_transition - Add task acl. * * @line: Line to parse. * @index: Current domain's index. * * Returns nothing. */ static void add_acl_domain_transition(char *line, const int index) { static char domainname[4096]; int pos; /* Chop off condition part which follows domainname. */ for (pos = 0; line[pos]; pos++) if (line[pos] == ' ' && line[pos + 1] != '/') { line[pos] = '\0'; break; } if (!ccs_correct_domain(line)) return; *ccs_alloc(w.jump_list, sizeof(char *), w.jump_list_len) = ccs_strdup(line); snprintf(domainname, sizeof(domainname) - 1, "%s %s", w.dp.list[index].domainname->name, get_last_word(line)); domainname[sizeof(domainname) - 1] = '\0'; ccs_normalize_line(domainname); assign_domain(domainname, line, false); } /** * parse_preference - Parse transition preference. * * @program: Pathname or path_group. * @domainname: Domainname or transition preference. * @index: Current domain's index. * * Returns true if transition preference was found, false otherwise. */ static _Bool parse_preference(char *program, char *domainname, const int index) { struct transition_preference *ptr; char *cp = strchr(domainname, ' '); if (*domainname == '<') goto add; if (cp) *cp = '\0'; if (ccs_correct_path(domainname) || !strcmp(domainname, "keep") || !strcmp(domainname, "reset") || !strcmp(domainname, "initialize") || !strcmp(domainname, "child") || !strcmp(domainname, "parent")) goto add; return false; add: ptr = ccs_alloc(w.preference_list, sizeof(*ptr), w.preference_list_len); ptr->index = index; ptr->domainname = ccs_strdup(domainname); ptr->program = ccs_strdup(program); return true; } /** * make_preference - Create transition preference. * * @ptr: Pointer to "struct transition_preference". * * Returns nothing. */ static void make_preference(struct transition_preference *ptr) { static char buffer[4096]; char *program = ptr->program; char *domainname = ptr->domainname; const int index = ptr->index; const char *self = w.dp.list[index].domainname->name; int i; struct path_group *group = *program == '@' ? find_path_group_ns(get_ns(self), program + 1) : NULL; const int j = group ? group->member_name_len : 0; buffer[sizeof(buffer) - 1] = '\0'; if (*domainname == '<') snprintf(buffer, sizeof(buffer) - 1, "%s", domainname); else if (!strcmp(domainname, "keep")) snprintf(buffer, sizeof(buffer) - 1, "%s", self); else if (!strcmp(domainname, "reset")) { if (*program == '@') { for (i = 0; i < j; i++) { snprintf(buffer, sizeof(buffer) - 1, "<%s>", group->member_name[i]->name); add_acl_domain_transition(buffer, index); } return; } snprintf(buffer, sizeof(buffer) - 1, "<%s>", program); } else if (!strcmp(domainname, "initialize")) { char *tmp = ccs_strdup(self); char *cp = strchr(tmp, ' '); if (cp) *cp = '\0'; if (*program == '@') { for (i = 0; i < j; i++) { const char *cp2 = group->member_name[i]->name; if (*cp2 != '/') continue; snprintf(buffer, sizeof(buffer) - 1, "%s %s", tmp, cp2); add_acl_domain_transition(buffer, index); } free(tmp); return; } snprintf(buffer, sizeof(buffer) - 1, "%s %s", tmp, program); free(tmp); } else if (!strcmp(domainname, "child")) { if (*program == '@') { for (i = 0; i < j; i++) { const char *cp = group->member_name[i]->name; if (*cp != '/') continue; snprintf(buffer, sizeof(buffer) - 1, "%s %s", self, cp); add_acl_domain_transition(buffer, index); } return; } snprintf(buffer, sizeof(buffer) - 1, "%s %s", self, program); } else if (!strcmp(domainname, "parent")) { char *cp; snprintf(buffer, sizeof(buffer) - 1, "%s", self); cp = strrchr(buffer, ' '); if (cp) *cp = '\0'; } else snprintf(buffer, sizeof(buffer) - 1, "%s %s", self, domainname); add_acl_domain_transition(buffer, index); } /** * parse_domain_line - Parse an ACL entry in domain policy. * * @ns: Pointer to "const struct ccs_path_info". * @line: Line to parse. * @index: Current domain's index. * @parse_flags: True if parse use_profile and use_group lines, false * otherwise. * * Returns nothing. */ static void parse_domain_line(const struct ccs_path_info *ns, char *line, const int index, const bool parse_flags) { add_condition_domain_transition(line, index); if (ccs_str_starts(line, "file execute ")) { /* * Chop off condition part which follows pathname. * But check for domain transition preference. */ char *cp = strchr(line, ' '); if (cp) { *cp++ = '\0'; if (parse_preference(line, cp, index)) return; } if (*line == '@' || ccs_correct_path(line)) add_string_entry(line, index); } else if (ccs_str_starts(line, "task manual_domain_transition ")) { add_acl_domain_transition(line, index); } else if (parse_flags) { unsigned int idx; if (sscanf(line, "use_profile %u", &idx) == 1 && idx < 256) w.dp.list[index].profile = (u8) idx; else if (sscanf(line, "use_group %u", &idx) == 1 && idx < 256) w.dp.list[index].group[idx] = 1; } } /** * parse_exception_line - Parse an ACL entry in exception policy. * * @ns: Pointer to "const struct ccs_path_info". * @line: Line to parse. * * Returns nothing. */ static void parse_exception_line(const struct ccs_path_info *ns, char *line) { int index; unsigned int group; for (index = 0; index < MAX_TRANSITION_TYPE; index++) { if (!ccs_str_starts(line, transition_type[index])) continue; add_transition_policy(ns, line, index); return; } if (ccs_str_starts(line, "path_group ")) add_path_group_policy(ns, line); else if (ccs_str_starts(line, "address_group ")) add_address_group_policy(line); else if (ccs_str_starts(line, "number_group ")) add_number_group_policy(line); else if (sscanf(line, "acl_group %u", &group) == 1 && group < 256) { int index; line = strchr(line + 10, ' '); if (!line++) return; add_acl_group_policy(group, line); for (index = 0; index < w.dp.list_len; index++) { char *cp; const struct ccs_domain *ptr = &w.dp.list[index]; if (!ptr->group[group] || ptr->target || ptr->is_dd) continue; cp = ccs_strdup(line); parse_domain_line(ns, cp, index, false); free(cp); } } } /** * read_domain_and_exception_policy - Read domain policy and exception policy. * * Returns nothing. * * Since CUI policy editor screen shows domain jump source domains and * unreachable domains, we need to read not only the domain policy but also * the exception policy for printing the domain transition tree. */ static void read_domain_and_exception_policy(void) { FILE *fp; int i; int j; int index; int max_index; static const struct ccs_path_info *kernel_ns = NULL; const struct ccs_path_info *ns; while (w.jump_list_len) free(w.jump_list[--w.jump_list_len]); clear_domain_policy(); w.transition_list_len = 0; editpolicy_clear_groups(); if (!kernel_ns) kernel_ns = ccs_savename(""); ns = kernel_ns; /* Load all domain transition related entries. */ fp = NULL; if (ccs_network_mode) /* We can read after write. */ fp = editpolicy_open_write(CCS_PROC_POLICY_DOMAIN_POLICY); else /* Don't set error message if failed. */ fp = fopen(CCS_PROC_POLICY_DOMAIN_POLICY, "r+"); if (fp) { fprintf(fp, "select transition_only\n"); if (ccs_network_mode) fputc(0, fp); fflush(fp); } else { fp = editpolicy_open_read(CCS_PROC_POLICY_DOMAIN_POLICY); } if (fp) { index = EOF; ccs_get(); while (true) { char *line = ccs_freadline_unpack(fp); if (!line) break; if (*line == '<') { ns = get_ns(line); index = assign_domain(line, NULL, false); continue; } else if (index == EOF) { continue; } parse_domain_line(ns, line, index, true); } ccs_put(); fclose(fp); } /* Load domain transition related entries and group entries. */ fp = editpolicy_open_read(CCS_PROC_POLICY_EXCEPTION_POLICY); if (fp) { ccs_get(); while (true) { char *line = ccs_freadline_unpack(fp); if (!line) break; if (*line == '<') { char *cp = strchr(line, ' '); if (!cp) continue; *cp++ = '\0'; ns = ccs_savename(line); memmove(line, cp, strlen(cp) + 1); } else ns = kernel_ns; parse_exception_line(ns, line); } ccs_put(); fclose(fp); } /* Create domain transition preference. */ for (i = 0; i < w.preference_list_len; i++) { struct transition_preference *ptr = &w.preference_list[i]; make_preference(&w.preference_list[i]); free(ptr->domainname); free(ptr->program); } free(w.preference_list); w.preference_list = NULL; w.preference_list_len = 0; /* * Domain jump sources by "task manual_domain_transition" keyword or * "auto_domain_transition=" * part of conditional ACL have been created by now because these * keywords do not depend on domain transition control directives * defined in the exception policy. * * Create domain jump sources for "file execute" keyword * now because these keywords depend on domain transition control * directives defined in the exception policy. Note that "file execute" * allows referring "path_group" directives. */ max_index = w.dp.list_len; for (index = 0; index < max_index; index++) { const char *domainname = w.dp.list[index].domainname->name; const struct ccs_path_info **string_ptr = w.dp.list[index].string_ptr; const int max_count = w.dp.list[index].string_count; /* Do not recursively create domain jump source. */ if (w.dp.list[index].target) continue; ns = get_ns(domainname); for (i = 0; i < max_count; i++) { const char *name = string_ptr[i]->name; struct path_group *group; if (name[0] != '@') { assign_djs(ns, domainname, name); continue; } group = find_path_group_ns(ns, name + 1); if (!group) continue; for (j = 0; j < group->member_name_len; j++) { name = group->member_name[j]->name; assign_djs(ns, domainname, name); } } } /* Create missing parent domains. */ max_index = w.dp.list_len; for (index = 0; index < max_index; index++) { char *line; ccs_get(); line = ccs_shprintf("%s", w.dp.list[index].domainname->name); while (true) { char *cp = strrchr(line, ' '); if (!cp) break; *cp = '\0'; if (find_domain(line, NULL, false) == EOF) assign_domain(line, NULL, true); } ccs_put(); } /* * All domains and jump sources have been created by now. * Let's markup domain jump targets and unreachable domains. */ max_index = w.dp.list_len; /* * Find domains that might be reachable via * "task manual_domain_transition" keyword or * "auto_domain_transition=" part of conditional ACL. * Such domains are marked with '*'. */ for (i = 0; i < w.jump_list_len; i++) { struct ccs_domain *ptr = find_domain_by_name(w.jump_list[i]); if (ptr) ptr->is_djt = true; } /* * Find domains that might be reachable via "initialize_domain" * keyword. Such domains are marked with '*'. */ for (index = 0; index < max_index; index++) { const struct ccs_domain *domain = &w.dp.list[index]; const char *domainname = domain->domainname->name; char *cp; /* Ignore domain jump sources. */ if (domain->target) continue; /* Ignore if already marked as domain jump targets. */ if (domain->is_djt) continue; /* Ignore if not a namespace's root's child domain. */ cp = strchr(domainname, ' '); if (!cp++ || strchr(cp, ' ')) continue; /* Check "no_initialize_domain $program from any" entry. */ for (i = 0; i < w.transition_list_len; i++) { struct transition_entry *ptr = &w.transition_list[i]; if (ptr->type != TRANSITION_NO_INITIALIZE) continue; if (!is_same_namespace(domainname, ptr->ns)) continue; if (ptr->domainname) continue; if (ptr->program && strcmp(ptr->program->name, cp)) continue; break; } if (i < w.transition_list_len) continue; /* * Check "initialize_domain $program from $domainname" entry. */ for (i = 0; i < w.transition_list_len; i++) { struct transition_entry *ptr = &w.transition_list[i]; if (ptr->type != TRANSITION_INITIALIZE) continue; if (!is_same_namespace(domainname, ptr->ns)) continue; if (ptr->program && strcmp(ptr->program->name, cp)) continue; break; } if (i < w.transition_list_len) w.dp.list[index].is_djt = true; } /* * Find domains that might suppress domain transition via "keep_domain" * keyword. Such domains are marked with '#'. */ for (index = 0; index < max_index; index++) { const struct ccs_domain *domain = &w.dp.list[index]; const struct ccs_path_info *name = domain->domainname; const char *last_name = get_last_word(name->name); /* Ignore domain jump sources. */ if (domain->target) continue; /* Check "no_keep_domain any from $domainname" entry. */ for (i = 0; i < w.transition_list_len; i++) { struct transition_entry *ptr = &w.transition_list[i]; if (ptr->type != TRANSITION_NO_KEEP) continue; if (!is_same_namespace(name->name, ptr->ns)) continue; if (ptr->program) continue; if (!ptr->domainname || !ccs_pathcmp(ptr->domainname, name) || !strcmp(ptr->domainname->name, last_name)) break; } if (i < w.transition_list_len) continue; /* Check "keep_domain $program from $domainname" entry. */ for (i = 0; i < w.transition_list_len; i++) { struct transition_entry *ptr = &w.transition_list[i]; if (ptr->type != TRANSITION_KEEP) continue; if (!is_same_namespace(name->name, ptr->ns)) continue; if (!ptr->domainname || !ccs_pathcmp(ptr->domainname, name) || !strcmp(ptr->domainname->name, last_name)) break; } if (i < w.transition_list_len) w.dp.list[index].is_dk = true; } /* * Find unreachable domains. Such domains are marked with '!'. * Unreachable domains are caused by one of "initialize_domain" keyword * or "keep_domain" keyword or "reset_domain" keyword. */ for (index = 0; index < max_index; index++) { char *line; struct ccs_domain * const domain = &w.dp.list[index]; /* * Mark domain jump source as unreachable if domain jump target * does not exist. Note that such domains are not marked with * '!'. */ if (domain->target) { if (find_domain(domain->target->name, NULL, false) == EOF) domain->is_du = true; continue; } /* Ignore if domain jump targets. */ if (domain->is_djt) continue; /* Ignore if deleted domain. */ if (domain->is_dd) continue; ns = get_ns(domain->domainname->name); ccs_get(); line = ccs_shprintf("%s", domain->domainname->name); while (true) { const struct ccs_domain *ptr = find_domain_by_name(line); const struct transition_entry *d_t; char *cp; /* Stop traversal if current is domain jump target. */ if (ptr && ptr->is_djt) break; cp = strrchr(line, ' '); if (cp) *cp++ = '\0'; else break; d_t = find_transition(ns, line, cp); if (d_t) domain->d_t = d_t; } ccs_put(); if (domain->d_t) domain->is_du = true; } /* Sort by domain name. */ qsort(w.dp.list, w.dp.list_len, sizeof(struct ccs_domain), domain_compare); /* * Since this screen shows domain transition tree within current * namespace, purge domains that are not in current namespace. */ for (index = 0; index < w.dp.list_len; index++) { int i; if (is_current_namespace(w.dp.list[index].domainname->name)) continue; free(w.dp.list[index].string_ptr); w.dp.list_len--; for (i = index; i < w.dp.list_len; i++) w.dp.list[i] = w.dp.list[i + 1]; index--; } /* Assign domain numbers. */ { int number = 0; int index; w.unnumbered_domains = 0; for (index = 0; index < w.dp.list_len; index++) { if (is_deleted_domain(index) || is_jump_source(index)) { w.dp.list[index].number = -1; w.unnumbered_domains++; } else { w.dp.list[index].number = number++; } } } if (!w.dp.list_len) return; w.dp.list_selected = ccs_realloc2(w.dp.list_selected, w.dp.list_len); } /** * show_process_line - Print a process line. * * @index: Index in the ccs_task_list array. * * Returns length of the printed line. */ static int show_process_line(const int index) { char *line; int tmp_col = 0; int i; printw("%c%4d:%3u ", ccs_task_list[index].selected ? '&' : ' ', index, ccs_task_list[index].profile); tmp_col += 10; for (i = 0; i < ccs_task_list[index].depth - 1; i++) { printw("%s", shift(" ")); tmp_col += 4; } ccs_get(); line = ccs_shprintf("%s%s (%u) %s", ccs_task_list[index].depth ? " +- " : "", ccs_task_list[index].name, ccs_task_list[index].pid, ccs_task_list[index].domain); printw("%s", shift(line)); tmp_col += strlen(line); ccs_put(); return tmp_col; } /** * show_list - Print list on the screen. * * Returns nothing. */ static void show_list(void) { struct ccs_screen *ptr = &screen[active]; int list_indent; const int offset = ptr->current; int i; int tmp_col; if (active == SCREEN_DOMAIN_LIST) w.list_items = w.show_tasklist ? ccs_task_list_len : w.dp.list_len; else w.list_items = p.generic_len; clear(); move(0, 0); if (w.height < CCS_HEADER_LINES + 1) { printw("Please enlarge window."); clrtobot(); refresh(); return; } /* add color */ editpolicy_color_change(editpolicy_color_head(), true); if (active == SCREEN_DOMAIN_LIST) { if (w.show_tasklist) { i = ccs_task_list_len; printw("<<< Process State Viewer >>>" " %d process%s ", i, i > 1 ? "es" : ""); i = count_tasklist(); } else { i = w.list_items - w.unnumbered_domains; printw("<<< Domain Transition Editor >>>" " %d domain%c ", i, i > 1 ? 's' : ' '); i = count_domainlist(); } } else { i = w.list_items; printw("<<< %s >>> %d entr%s ", w.caption, i, i > 1 ? "ies" : "y"); i = count_generic(); } if (i) printw("(%u selected)", i); printw(" '?' for help"); /* add color */ editpolicy_color_change(editpolicy_color_head(), false); w.eat_col = ptr->x; w.max_col = 0; if (active == SCREEN_ACL_LIST) { char *line; ccs_get(); line = ccs_shprintf("%s", shift(w.current_domain)); editpolicy_attr_change(A_REVERSE, true); /* add color */ move(2, 0); printw("%s", line); editpolicy_attr_change(A_REVERSE, false); /* add color */ ccs_put(); } list_indent = 0; switch (active) { case SCREEN_EXCEPTION_LIST: case SCREEN_ACL_LIST: for (i = 0; i < w.list_items; i++) { const enum directive_type directive = p.generic[i].directive; const int len = directive_map[directive].alias_len; if (len > list_indent) list_indent = len; } break; default: break; } for (i = 0; i < w.body_lines; i++) { const int index = offset + i; w.eat_col = ptr->x; if (index >= w.list_items) break; move(CCS_HEADER_LINES + i, 0); switch (active) { case SCREEN_DOMAIN_LIST: if (!w.show_tasklist) tmp_col = show_domain_line(index); else tmp_col = show_process_line(index); break; case SCREEN_EXCEPTION_LIST: case SCREEN_ACL_LIST: tmp_col = show_acl_line(index, list_indent); break; case SCREEN_PROFILE_LIST: tmp_col = show_profile_line(index); break; case SCREEN_STAT_LIST: tmp_col = show_stat_line(index); break; default: tmp_col = show_literal_line(index); break; } clrtoeol(); tmp_col -= w.width; if (tmp_col > w.max_col) w.max_col = tmp_col; } show_current(); } /** * resize_window - Callback for resize event. * * Returns nothing. */ static void resize_window(void) { struct ccs_screen *ptr = &screen[active]; getmaxyx(stdscr, w.height, w.width); w.body_lines = w.height - CCS_HEADER_LINES; if (w.body_lines <= ptr->y) ptr->y = w.body_lines - 1; if (ptr->y < 0) ptr->y = 0; } /** * up_arrow_key - Callback event for pressing up-arrow key. * * Returns nothing. */ static void up_arrow_key(void) { struct ccs_screen *ptr = &screen[active]; if (ptr->y > 0) { ptr->y--; show_current(); } else if (ptr->current > 0) { ptr->current--; show_list(); } } /** * down_arrow_key - Callback event for pressing down-arrow key. * * Returns nothing. */ static void down_arrow_key(void) { struct ccs_screen *ptr = &screen[active]; if (ptr->y < w.body_lines - 1) { if (ptr->current + ptr->y < w.list_items - 1) { ptr->y++; show_current(); } } else if (ptr->current + ptr->y < w.list_items - 1) { ptr->current++; show_list(); } } /** * page_up_key - Callback event for pressing page-up key. * * Returns nothing. */ static void page_up_key(void) { struct ccs_screen *ptr = &screen[active]; int p0 = ptr->current; int p1 = ptr->y; _Bool refresh; if (p0 + p1 > w.body_lines) { p0 -= w.body_lines; if (p0 < 0) p0 = 0; } else if (p0 + p1 > 0) { p0 = 0; p1 = 0; } else { return; } refresh = (ptr->current != p0); ptr->current = p0; ptr->y = p1; if (refresh) show_list(); else show_current(); } /** * page_down_key - Callback event for pressing page-down key. * * Returns nothing. */ static void page_down_key(void) { struct ccs_screen *ptr = &screen[active]; int count = w.list_items - 1; int p0 = ptr->current; int p1 = ptr->y; _Bool refresh; if (p0 + p1 + w.body_lines < count) { p0 += w.body_lines; } else if (p0 + p1 < count) { while (p0 + p1 < count) { if (p1 + 1 < w.body_lines) p1++; else p0++; } } else { return; } refresh = (ptr->current != p0); ptr->current = p0; ptr->y = p1; if (refresh) show_list(); else show_current(); } /** * editpolicy_get_current - Get currently selected line's index. * * Returns index for currently selected line on success, EOF otherwise. * * If current screen has no entry, this function returns EOF. */ int editpolicy_get_current(void) { struct ccs_screen *ptr = &screen[active]; if (!w.list_items) return EOF; return ptr->current + ptr->y; } /** * show_current - Show current cursor line. * * Returns nothing. */ static void show_current(void) { struct ccs_screen *ptr = &screen[active]; if (active == SCREEN_DOMAIN_LIST && !w.show_tasklist) { char *line; const int index = editpolicy_get_current(); ccs_get(); w.eat_col = ptr->x; if (index >= 0) { line = ccs_shprintf("%s", shift(w.dp.list[index]. domainname->name)); if (is_jump_source(index)) { char *cp = strrchr(line, ' '); if (cp) *cp = '\0'; } } else line = ccs_shprintf("%s", current_ns->name); if (w.width < strlen(line)) line[w.width] = '\0'; move(2, 0); clrtoeol(); editpolicy_attr_change(A_REVERSE, true); /* add color */ printw("%s", line); editpolicy_attr_change(A_REVERSE, false); /* add color */ ccs_put(); } if (active == SCREEN_EXCEPTION_LIST || active == SCREEN_PROFILE_LIST) { char *line; ccs_get(); w.eat_col = ptr->x; line = ccs_shprintf("%s", current_ns->name); if (w.width < strlen(line)) line[w.width] = '\0'; move(2, 0); clrtoeol(); editpolicy_attr_change(A_REVERSE, true); /* add color */ printw("%s", line); editpolicy_attr_change(A_REVERSE, false); /* add color */ ccs_put(); } move(CCS_HEADER_LINES + ptr->y, 0); editpolicy_line_draw(); /* add color */ refresh(); } /** * adjust_cursor_pos - Adjust cursor position if needed. * * @item_count: Available item count in this screen. * * Returns nothing. */ static void adjust_cursor_pos(const int item_count) { struct ccs_screen *ptr = &screen[active]; if (item_count == 0) { ptr->current = 0; ptr->y = 0; } else { while (ptr->current + ptr->y >= item_count) { if (ptr->y > 0) ptr->y--; else if (ptr->current > 0) ptr->current--; } } } /** * set_cursor_pos - Move cursor position if needed. * * @index: Index in the domain policy or currently selected line in the generic * list. * * Returns nothing. */ static void set_cursor_pos(const int index) { struct ccs_screen *ptr = &screen[active]; while (index < ptr->y + ptr->current) { if (ptr->y > 0) ptr->y--; else ptr->current--; } while (index > ptr->y + ptr->current) { if (ptr->y < w.body_lines - 1) ptr->y++; else ptr->current++; } } /** * select_item - Select an item. * * Returns true if selected, false otherwise. * * Domain transition source and deleted domains are not selectable. */ static _Bool select_item(void) { int x; int y; const int index = editpolicy_get_current(); if (index < 0) return false; if (active == SCREEN_DOMAIN_LIST) { if (!w.show_tasklist) { if (is_deleted_domain(index) || is_jump_source(index)) return false; w.dp.list_selected[index] ^= 1; } else { ccs_task_list[index].selected ^= 1; } } else { p.generic[index].selected ^= 1; } getyx(stdscr, y, x); editpolicy_sttr_save(); /* add color */ show_list(); editpolicy_sttr_restore(); /* add color */ move(y, x); return true; } /** * generic_compare - strcmp() for qsort() callback. * * @a: Pointer to "void". * @b: Pointer to "void". * * Returns return value of strcmp(). */ static int generic_compare(const void *a, const void *b) { const struct generic_entry *a0 = (struct generic_entry *) a; const struct generic_entry *b0 = (struct generic_entry *) b; const enum directive_type a0_d = a0->directive; const enum directive_type b0_d = b0->directive; const char *a1 = directive_map[a0_d].alias; const char *b1 = directive_map[b0_d].alias; const char *a2 = a0->operand; const char *b2 = b0->operand; if (active == SCREEN_EXCEPTION_LIST) { int ret; if (a0_d >= DIRECTIVE_ACL_GROUP_000 && a0_d <= DIRECTIVE_ACL_GROUP_255 && b0_d >= DIRECTIVE_ACL_GROUP_000 && b0_d <= DIRECTIVE_ACL_GROUP_255 && a0_d != b0_d) return a0_d - b0_d; ret = strcmp(a1, b1); if (ret) return ret; return strcmp(a2, b2); } if (a0_d == DIRECTIVE_USE_GROUP && b0_d == DIRECTIVE_USE_GROUP) return atoi(a2) - atoi(b2); if (!w.sort_acl) { const int ret = strcmp(a1, b1); if (ret) return ret; return strcmp(a2, b2); } else if (a0_d == DIRECTIVE_USE_GROUP) { return 1; } else if (b0_d == DIRECTIVE_USE_GROUP) { return -1; } else if (a0_d == DIRECTIVE_TRANSITION_FAILED) { return 2; } else if (b0_d == DIRECTIVE_TRANSITION_FAILED) { return -2; } else if (a0_d == DIRECTIVE_QUOTA_EXCEEDED) { return 3; } else if (b0_d == DIRECTIVE_QUOTA_EXCEEDED) { return -3; } else { const int ret = strcmp(a2, b2); if (ret) return ret; return strcmp(a1, b1); } } /** * delete_entry - Delete an entry. * * Returns nothing. */ static void delete_entry(void) { int c; move(1, 0); editpolicy_color_change(COLOR_DISP_ERR, true); /* add color */ if (active == SCREEN_DOMAIN_LIST) { c = count_domainlist(); if (!c) c = select_item(); if (!c) printw("Select domain using Space key first."); else printw("Delete selected domain%s? ('Y'es/'N'o)", c > 1 ? "s" : ""); } else { c = count_generic(); if (!c) c = select_item(); if (!c) printw("Select entry using Space key first."); else printw("Delete selected entr%s? ('Y'es/'N'o)", c > 1 ? "ies" : "y"); } editpolicy_color_change(COLOR_DISP_ERR, false); /* add color */ clrtoeol(); refresh(); if (!c) return; do { c = ccs_getch2(); } while (!(c == 'Y' || c == 'y' || c == 'N' || c == 'n' || c == EOF)); resize_window(); if (c != 'Y' && c != 'y') { show_list(); return; } if (active == SCREEN_DOMAIN_LIST) { int i; FILE *fp = editpolicy_open_write (CCS_PROC_POLICY_DOMAIN_POLICY); if (!fp) return; for (i = 0; i < w.dp.list_len; i++) { if (!w.dp.list_selected[i]) continue; fprintf(fp, "delete %s\n", w.dp.list[i].domainname->name); } ccs_close_write(fp); } else { int i; const _Bool is_kernel_ns = !strcmp(current_ns->name, ""); FILE *fp = editpolicy_open_write(w.policy_file); if (!fp) return; if (active == SCREEN_ACL_LIST) { if (w.show_tasklist) fprintf(fp, "select pid=%u\n", w.current_pid); else fprintf(fp, "select domain=%s\n", w.current_domain); } for (i = 0; i < p.generic_len; i++) { enum directive_type directive; if (!p.generic[i].selected) continue; directive = p.generic[i].directive; fprintf(fp, "delete %s %s %s\n", active == SCREEN_EXCEPTION_LIST && !is_kernel_ns ? current_ns->name : "", directive_map[directive].original, p.generic[i].operand); } ccs_close_write(fp); } } /** * add_entry - Add an entry. * * Returns nothing. */ static void add_entry(void) { FILE *fp; char *line; const _Bool is_kernel_ns = !strcmp(current_ns->name, ""); editpolicy_attr_change(A_BOLD, true); /* add color */ line = ccs_readline(w.height - 1, 0, "Enter new entry> ", rl.history, rl.count, 128000, 8); editpolicy_attr_change(A_BOLD, false); /* add color */ if (!line || !*line) goto out; rl.count = ccs_add_history(line, rl.history, rl.count, rl.max); fp = editpolicy_open_write(w.policy_file); if (!fp) goto out; switch (active) { enum directive_type directive; case SCREEN_DOMAIN_LIST: if (!ccs_correct_domain(line)) { const int len = strlen(line) + 128; w.last_error = ccs_realloc2(w.last_error, len); snprintf(w.last_error, len - 1, "%s is an invalid domainname.", line); line[0] = '\0'; } break; case SCREEN_ACL_LIST: if (w.show_tasklist) fprintf(fp, "select pid=%u\n", w.current_pid); else fprintf(fp, "select domain=%s\n", w.current_domain); /* Fall through. */ case SCREEN_EXCEPTION_LIST: if (active == SCREEN_EXCEPTION_LIST && !is_kernel_ns) fprintf(fp, "%s ", current_ns->name); directive = find_directive(false, line); if (directive != DIRECTIVE_NONE) fprintf(fp, "%s ", directive_map[directive].original); break; case SCREEN_PROFILE_LIST: if (!strchr(line, '=')) fprintf(fp, "%s %s-COMMENT=\n", !is_kernel_ns ? current_ns->name : "", line); if (!is_kernel_ns) fprintf(fp, "%s ", current_ns->name); break; case SCREEN_NS_LIST: fprintf(fp, "%s PROFILE_VERSION=20150505\n", line); line[0] = '\0'; break; default: break; } fprintf(fp, "%s\n", line); ccs_close_write(fp); out: free(line); } /** * find_entry - Find an entry by user's key input. * * @input: True if find next/previous, false if find first. * @forward: True if find next, false if find previous. * * Returns nothing. */ static void find_entry(const _Bool input, const _Bool forward) { int index = editpolicy_get_current(); char *line = NULL; if (index == EOF) return; if (!input) goto start_search; editpolicy_attr_change(A_BOLD, true); /* add color */ line = ccs_readline(w.height - 1, 0, "Search> ", rl.history, rl.count, 128000, 8); editpolicy_attr_change(A_BOLD, false); /* add color */ if (!line || !*line) goto out; rl.count = ccs_add_history(line, rl.history, rl.count, rl.max); free(rl.search_buffer[active]); rl.search_buffer[active] = line; line = NULL; index = -1; start_search: ccs_get(); while (true) { const char *cp; if (forward) { if (++index >= w.list_items) break; } else { if (--index < 0) break; } if (active == SCREEN_DOMAIN_LIST) { if (w.show_tasklist) cp = ccs_task_list[index].name; else cp = get_last_name(index); } else if (active == SCREEN_PROFILE_LIST) { cp = ccs_shprintf("%u-%s", p.generic[index].directive, p.generic[index].operand); } else { const enum directive_type directive = p.generic[index].directive; cp = ccs_shprintf("%s %s", directive_map[directive].alias, p.generic[index].operand); } if (!strstr(cp, rl.search_buffer[active])) continue; set_cursor_pos(index); break; } ccs_put(); out: free(line); show_list(); } /** * set_profile - Change profile number. * * Returns nothing. */ static void set_profile(void) { int index; FILE *fp; char *line; if (!w.show_tasklist) { if (!count_domainlist() && !select_item()) { move(1, 0); printw("Select domain using Space key first."); clrtoeol(); refresh(); return; } } else { if (!count_tasklist() && !select_item()) { move(1, 0); printw("Select processes using Space key first."); clrtoeol(); refresh(); return; } } editpolicy_attr_change(A_BOLD, true); /* add color */ line = ccs_readline(w.height - 1, 0, "Enter profile number> ", NULL, 0, 8, 1); editpolicy_attr_change(A_BOLD, false); /* add color */ if (!line || !*line) goto out; fp = editpolicy_open_write(CCS_PROC_POLICY_DOMAIN_POLICY); if (!fp) goto out; if (!w.show_tasklist) { for (index = 0; index < w.dp.list_len; index++) { if (!w.dp.list_selected[index]) continue; fprintf(fp, "select domain=%s\nuse_profile %s\n", w.dp.list[index].domainname->name, line); } } else { for (index = 0; index < ccs_task_list_len; index++) { if (!ccs_task_list[index].selected) continue; fprintf(fp, "select pid=%u\nuse_profile %s\n", ccs_task_list[index].pid, line); } } ccs_close_write(fp); out: free(line); } /** * set_level - Change profiles. * * Returns nothing. */ static void set_level(void) { int index; FILE *fp; char *line; if (!count_generic()) select_item(); editpolicy_attr_change(A_BOLD, true); /* add color */ ccs_initial_readline_data = NULL; for (index = 0; index < p.generic_len; index++) { char *cp; if (!p.generic[index].selected) continue; cp = strchr(p.generic[index].operand, '='); if (!cp) continue; ccs_initial_readline_data = cp + 1; break; } line = ccs_readline(w.height - 1, 0, "Enter new value> ", NULL, 0, 128000, 1); ccs_initial_readline_data = NULL; editpolicy_attr_change(A_BOLD, false); /* add color */ if (!line || !*line) goto out; fp = editpolicy_open_write(CCS_PROC_POLICY_PROFILE); if (!fp) goto out; for (index = 0; index < p.generic_len; index++) { char *buf; char *cp; enum directive_type directive; if (!p.generic[index].selected) continue; ccs_get(); buf = ccs_shprintf("%s", p.generic[index].operand); cp = strchr(buf, '='); if (cp) *cp = '\0'; directive = p.generic[index].directive; fprintf(fp, "%s ", current_ns->name); if (directive < 256) fprintf(fp, "%u-", directive); fprintf(fp, "%s=%s\n", buf, line); ccs_put(); } ccs_close_write(fp); out: free(line); } /** * set_quota - Set memory quota. * * Returns nothing. */ static void set_quota(void) { int index; FILE *fp; char *line; if (!count_generic()) select_item(); editpolicy_attr_change(A_BOLD, true); /* add color */ line = ccs_readline(w.height - 1, 0, "Enter new value> ", NULL, 0, 20, 1); editpolicy_attr_change(A_BOLD, false); /* add color */ if (!line || !*line) goto out; fp = editpolicy_open_write(CCS_PROC_POLICY_STAT); if (!fp) goto out; for (index = 0; index < p.generic_len; index++) { char *buf; char *cp; if (!p.generic[index].selected) continue; ccs_get(); buf = ccs_shprintf("%s", p.generic[index].operand); cp = strchr(buf, ':'); if (cp) *cp = '\0'; fprintf(fp, "%s: %s\n", buf, line); ccs_put(); } ccs_close_write(fp); out: free(line); } /** * select_ns_window - Check whether to switch to ACL list or not. * * Returns next window to display if valid, MAX_SCREEN_TYPE otherwise. */ static enum screen_type select_ns_window(void) { const int current = editpolicy_get_current(); if (current != EOF) { const char *namespace = p.generic[current].operand; enum screen_type next = w.previous_screen; if (next == SCREEN_ACL_LIST && strcmp(current_ns->name, namespace)) next = SCREEN_DOMAIN_LIST; current_ns = ccs_savename(namespace); return next; } return MAX_SCREEN_TYPE; } /** * select_acl_window - Check whether to switch to ACL list or not. * * Returns next window to display if valid, MAX_SCREEN_TYPE otherwise. */ static enum screen_type select_acl_window(void) { const int current = editpolicy_get_current(); const char *old_domain = w.current_domain; const char *new_domain; enum screen_type next = SCREEN_ACL_LIST; if (active != SCREEN_DOMAIN_LIST || current == EOF) return MAX_SCREEN_TYPE; w.current_pid = 0; if (w.show_tasklist) { w.current_pid = ccs_task_list[current].pid; new_domain = ccs_strdup(ccs_task_list[current].domain); } else if (is_deleted_domain(current)) { return MAX_SCREEN_TYPE; } else if (is_jump_source(current)) { if (find_target_domain(current) == EOF) return MAX_SCREEN_TYPE; new_domain = w.dp.list[current].target->name; current_ns = get_ns(new_domain); w.force_move_cursor = true; next = SCREEN_DOMAIN_LIST; } else { new_domain = w.dp.list[current].domainname->name; } w.no_restore_cursor = old_domain && strcmp(old_domain, new_domain); free((char *) old_domain); w.current_domain = ccs_strdup(new_domain); return next; } /** * select_window - Switch window. * * Returns next window to display. */ static enum screen_type select_window(void) { const int current = editpolicy_get_current(); const _Bool allow_acl = active == SCREEN_DOMAIN_LIST && current != EOF && !is_jump_source(current) && !is_deleted_domain(current); move(0, 0); printw("Press one of below keys to switch window.\n\n"); printw("e <<< Exception Policy Editor >>>\n"); printw("d <<< Domain Transition Editor >>>\n"); if (allow_acl) printw("a <<< Domain Policy Editor >>>\n"); printw("p <<< Profile Editor >>>\n"); printw("m <<< Manager Policy Editor >>>\n"); printw("n <<< Namespace Selector >>>\n"); if (!w.offline_mode) { /* printw("i <<< Interactive Enforcing Mode >>>\n"); */ printw("s <<< Statistics >>>\n"); } printw("q Quit this editor.\n"); clrtobot(); refresh(); while (true) { enum screen_type next; int c = ccs_getch2(); switch (c) { case 'E': case 'e': return SCREEN_EXCEPTION_LIST; case 'D': case 'd': return SCREEN_DOMAIN_LIST; case 'A': case 'a': if (!allow_acl) break; next = select_acl_window(); if (next == MAX_SCREEN_TYPE) break; return next; case 'P': case 'p': return SCREEN_PROFILE_LIST; case 'M': case 'm': return SCREEN_MANAGER_LIST; case 'N': case 'n': return SCREEN_NS_LIST; /* case 'I': case 'i': if (w.offline_mode) break; return SCREEN_QUERY_LIST; */ case 'S': case 's': if (w.offline_mode) break; return SCREEN_STAT_LIST; case 'Q': case 'q': case EOF: return MAX_SCREEN_TYPE; } } } /** * copy_mark_state - Copy selected state to lines under the current line. * * Returns nothing. */ static void copy_mark_state(void) { const int current = editpolicy_get_current(); int index; if (current == EOF) return; if (active == SCREEN_DOMAIN_LIST) { if (w.show_tasklist) { const u8 selected = ccs_task_list[current].selected; for (index = current; index < ccs_task_list_len; index++) ccs_task_list[index].selected = selected; } else { const u8 selected = w.dp.list_selected[current]; if (is_deleted_domain(current) || is_jump_source(current)) return; for (index = current; index < w.dp.list_len; index++) { if (is_deleted_domain(index) || is_jump_source(index)) continue; w.dp.list_selected[index] = selected; } } } else { const _Bool selected = p.generic[current].selected; for (index = current; index < p.generic_len; index++) p.generic[index].selected = selected; } show_list(); } /** * copy_to_history - Copy line to histoy buffer. * * Returns nothing. */ static void copy_to_history(void) { const int current = editpolicy_get_current(); const char *line; unsigned int profile; if (current == EOF) return; ccs_get(); switch (active) { enum directive_type directive; case SCREEN_DOMAIN_LIST: if (!w.show_tasklist) { const struct ccs_domain *domain = &w.dp.list[current]; if (domain->target) line = domain->target->name; else line = domain->domainname->name; } else line = ccs_task_list[current].domain; break; case SCREEN_EXCEPTION_LIST: case SCREEN_ACL_LIST: directive = p.generic[current].directive; line = ccs_shprintf("%s %s", directive_map[directive].alias, p.generic[current].operand); break; case SCREEN_STAT_LIST: line = NULL; break; case SCREEN_PROFILE_LIST: profile = p.generic[current].directive; if (profile < 256) { line = ccs_shprintf("%u-%s", profile, p.generic[current].operand); break; } /* Fall through. */ default: line = ccs_shprintf("%s", p.generic[current].operand); } rl.count = ccs_add_history(line, rl.history, rl.count, rl.max); ccs_put(); } /** * generic_list_loop - Main loop. * * Returns next screen to display. */ static enum screen_type generic_list_loop(void) { struct ccs_screen *ptr; static struct { int y; int current; } saved_cursor[MAX_SCREEN_TYPE] = { }; if (active == SCREEN_EXCEPTION_LIST) { w.policy_file = CCS_PROC_POLICY_EXCEPTION_POLICY; w.caption = "Exception Policy Editor"; } else if (active == SCREEN_ACL_LIST) { w.policy_file = CCS_PROC_POLICY_DOMAIN_POLICY; w.caption = "Domain Policy Editor"; /* } else if (active == SCREEN_QUERY_LIST) { w.policy_file = CCS_PROC_POLICY_QUERY; w.caption = "Interactive Enforcing Mode"; */ } else if (active == SCREEN_NS_LIST) { w.policy_file = CCS_PROC_POLICY_PROFILE; w.caption = "Namespace Selector"; } else if (active == SCREEN_PROFILE_LIST) { w.policy_file = CCS_PROC_POLICY_PROFILE; w.caption = "Profile Editor"; } else if (active == SCREEN_MANAGER_LIST) { w.policy_file = CCS_PROC_POLICY_MANAGER; w.caption = "Manager Policy Editor"; } else if (active == SCREEN_STAT_LIST) { w.policy_file = CCS_PROC_POLICY_STAT; w.caption = "Statistics"; } else { w.policy_file = CCS_PROC_POLICY_DOMAIN_POLICY; /* w.caption = "Domain Transition Editor"; */ } ptr = &screen[active]; if (w.no_restore_cursor || w.force_move_cursor) { ptr->current = 0; ptr->y = 0; w.no_restore_cursor = false; } else { ptr->current = saved_cursor[active].current; ptr->y = saved_cursor[active].y; } start: if (active == SCREEN_DOMAIN_LIST) { if (!w.show_tasklist) { read_domain_and_exception_policy(); if (w.force_move_cursor) { const int redirect_index = find_domain(w.current_domain, NULL, false); if (redirect_index >= 0) { ptr->current = redirect_index - ptr->y; while (ptr->current < 0) { ptr->current++; ptr->y--; } } w.force_move_cursor = false; } adjust_cursor_pos(w.dp.list_len); } else { ccs_read_process_list(true); adjust_cursor_pos(ccs_task_list_len); } } else { read_generic_policy(); adjust_cursor_pos(p.generic_len); } start2: show_list(); if (w.last_error) { move(1, 0); printw("ERROR: %s", w.last_error); clrtoeol(); refresh(); free(w.last_error); w.last_error = NULL; } while (true) { const int c = ccs_getch2(); enum screen_type next; saved_cursor[active].current = ptr->current; saved_cursor[active].y = ptr->y; if (c == 'q' || c == 'Q') return MAX_SCREEN_TYPE; if ((c == '\r' || c == '\n') && active == SCREEN_ACL_LIST) return SCREEN_DOMAIN_LIST; if (c == '\t') return w.previous_screen; if (w.need_reload) { w.need_reload = false; goto start; } if (c == ERR) continue; /* Ignore invalid key. */ switch (c) { case KEY_RESIZE: resize_window(); show_list(); break; case KEY_UP: up_arrow_key(); break; case KEY_DOWN: down_arrow_key(); break; case KEY_PPAGE: page_up_key(); break; case KEY_NPAGE: page_down_key(); break; case ' ': select_item(); break; case 'c': case 'C': copy_mark_state(); break; case 'f': case 'F': if (active != SCREEN_STAT_LIST) find_entry(true, true); break; case 'p': case 'P': if (active == SCREEN_STAT_LIST) break; if (!rl.search_buffer[active]) find_entry(true, false); else find_entry(false, false); break; case 'n': case 'N': if (active == SCREEN_STAT_LIST) break; if (!rl.search_buffer[active]) find_entry(true, true); else find_entry(false, true); break; case 'd': case 'D': if (w.readonly_mode) break; switch (active) { case SCREEN_DOMAIN_LIST: if (w.show_tasklist) break; case SCREEN_EXCEPTION_LIST: case SCREEN_ACL_LIST: case SCREEN_MANAGER_LIST: delete_entry(); goto start; default: break; } break; case 'a': case 'A': if (w.readonly_mode) break; switch (active) { case SCREEN_DOMAIN_LIST: if (w.show_tasklist) break; case SCREEN_EXCEPTION_LIST: case SCREEN_ACL_LIST: case SCREEN_PROFILE_LIST: case SCREEN_MANAGER_LIST: case SCREEN_NS_LIST: add_entry(); goto start; default: break; } break; case '\r': case '\n': if (active == SCREEN_NS_LIST) next = select_ns_window(); else next = select_acl_window(); if (next == MAX_SCREEN_TYPE) break; return next; case 's': case 'S': if (w.readonly_mode) break; switch (active) { case SCREEN_DOMAIN_LIST: set_profile(); goto start; case SCREEN_PROFILE_LIST: set_level(); goto start; case SCREEN_STAT_LIST: set_quota(); goto start; default: break; } break; case 'r': case 'R': goto start; case KEY_LEFT: if (!ptr->x) break; ptr->x--; goto start2; case KEY_RIGHT: ptr->x++; goto start2; case KEY_HOME: ptr->x = 0; goto start2; case KEY_END: ptr->x = w.max_col; goto start2; case KEY_IC: copy_to_history(); break; case 'o': case 'O': if (active == SCREEN_ACL_LIST || active == SCREEN_EXCEPTION_LIST) { editpolicy_optimize(); show_list(); } break; case '@': switch (active) { case SCREEN_ACL_LIST: w.sort_acl = !w.sort_acl; goto start; case SCREEN_PROFILE_LIST: w.sort_profile = !w.sort_profile; goto start; case SCREEN_DOMAIN_LIST: if (w.offline_mode) break; w.show_tasklist = !w.show_tasklist; goto start; default: break; } break; case 'w': case 'W': return select_window(); case '?': if (show_command_key(active, w.readonly_mode)) goto start; return MAX_SCREEN_TYPE; } } } /** * save_to_file - Save policy to file. * * @src: Filename to read from. * @dest: Filename to write to. * * Returns true on success, false otherwise. */ static _Bool save_to_file(const char *src, const char *dest) { FILE *proc_fp = editpolicy_open_read(src); FILE *file_fp = fopen(dest, "w"); int c; if (!file_fp || !proc_fp) { fprintf(stderr, "Can't open %s\n", dest); if (file_fp) fclose(file_fp); if (proc_fp) fclose(proc_fp); return false; } while (true) { c = fgetc(proc_fp); if (!c || c == EOF) break; if (fputc(c, file_fp) == EOF) { c = EOF; break; } } fclose(proc_fp); fclose(file_fp); return !c; } /** * parse_args - Parse command line arguments. * * @argc: argc passed to main(). * @argv: argv passed to main(). * * Returns nothing. */ static void parse_args(int argc, char *argv[]) { int i; for (i = 1; i < argc; i++) { char *ptr = argv[i]; char *cp = strchr(ptr, ':'); if (*ptr == '/') { if (ccs_network_mode || w.offline_mode) goto usage; w.policy_dir = ptr; w.offline_mode = true; } else if (*ptr == '<') { if (current_ns || strchr(ptr, ' ') || !ccs_domain_def(ptr)) goto usage; current_ns = ccs_savename(ptr); } else if (cp) { *cp++ = '\0'; if (ccs_network_mode || w.offline_mode) goto usage; ccs_network_ip = inet_addr(ptr); ccs_network_port = htons(atoi(cp)); ccs_network_mode = true; if (!ccs_check_remote_host()) exit(1); } else if (!strcmp(ptr, "e")) active = SCREEN_EXCEPTION_LIST; else if (!strcmp(ptr, "d")) active = SCREEN_DOMAIN_LIST; else if (!strcmp(ptr, "p")) active = SCREEN_PROFILE_LIST; else if (!strcmp(ptr, "m")) active = SCREEN_MANAGER_LIST; else if (!strcmp(ptr, "s")) active = SCREEN_STAT_LIST; else if (!strcmp(ptr, "n")) active = SCREEN_NS_LIST; else if (!strcmp(ptr, "readonly")) w.readonly_mode = true; else if (sscanf(ptr, "refresh=%u", &w.refresh_interval) != 1) { usage: printf("Usage: %s [e|d|p|m|s|n] [readonly] " "[refresh=interval] []" "[{policy_dir|remote_ip:remote_port}]\n", argv[0]); exit(1); } } if (!current_ns) current_ns = ccs_savename(""); w.previous_screen = active; } /** * load_offline - Load policy for offline mode. * * Returns nothing. */ static void load_offline(void) { int pipe_fd[2] = { EOF, EOF }; int fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr = { }; socklen_t size = sizeof(addr); /* * Use PF_INET socket as a method for communicating with child task * so that we can use same method for child task and * ccs-editpolicy-agent. */ addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (chdir(w.policy_dir) || chdir("policy/current/")) { fprintf(stderr, "Directory %s/policy/current/ doesn't " "exist.\n", w.policy_dir); exit(1); } if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) || listen(fd, 5) || getsockname(fd, (struct sockaddr *) &addr, &size)) { fprintf(stderr, "Can't create listener socket.\n"); exit(1); } ccs_network_ip = addr.sin_addr.s_addr; ccs_network_port = addr.sin_port; ccs_network_mode = true; /* * Use pipe as a notifier for termination. * * Sending signals by remembering child task's PID would be possible. * But such approach will not work if main task exited unexpectedly * (e.g. SIGKILL). Since pipe_fd[1] is guaranteed to be closed no * matter how main task exits, pipe approach is more reliable for * telling the child task to exit. */ if (pipe(pipe_fd)) { fprintf(stderr, "Can't create pipe.\n"); exit(1); } switch (fork()) { case 0: if (close(pipe_fd[1]) || fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK)) _exit(1); editpolicy_offline_daemon(fd, pipe_fd[0]); _exit(1); case -1: fprintf(stderr, "fork()\n"); exit(1); } if (close(fd) || close(pipe_fd[0])) exit(1); copy_file("profile.conf", CCS_PROC_POLICY_PROFILE); copy_file("exception_policy.conf", CCS_PROC_POLICY_EXCEPTION_POLICY); copy_file("domain_policy.conf", CCS_PROC_POLICY_DOMAIN_POLICY); copy_file("manager.conf", CCS_PROC_POLICY_MANAGER); if (chdir("..")) { fprintf(stderr, "Directory %s/policy/ doesn't exist.\n", w.policy_dir); exit(1); } } /** * load_readwrite - Check that this program can write to /sys/kernel/security/tomoyo/ interface. * * Returns nothing. */ static void load_readwrite(void) { const int fd1 = open2(CCS_PROC_POLICY_EXCEPTION_POLICY, O_RDWR); const int fd2 = open2(CCS_PROC_POLICY_DOMAIN_POLICY, O_RDWR); if ((fd1 != EOF && write(fd1, "", 0) != 0) || (fd2 != EOF && write(fd2, "", 0) != 0)) { fprintf(stderr, "In order to run this program, it must be " "registered to %s . " "Please reboot.\n", CCS_PROC_POLICY_MANAGER); exit(1); } close(fd1); close(fd2); } /** * save_offline - Save policy for offline mode. * * Returns nothing. */ static void save_offline(void) { time_t now = time(NULL); static char stamp[32] = { }; while (1) { struct tm *tm = localtime(&now); snprintf(stamp, sizeof(stamp) - 1, "%02d-%02d-%02d.%02d:%02d:%02d/", tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); if (!mkdir(stamp, 0700)) break; else if (errno == EEXIST) now++; else { fprintf(stderr, "Can't create %s/%s .\n", w.policy_dir, stamp); exit(1); } } if ((symlink("policy/current/profile.conf", "../profile.conf") && errno != EEXIST) || (symlink("policy/current/manager.conf", "../manager.conf") && errno != EEXIST) || (symlink("policy/current/exception_policy.conf", "../exception_policy.conf") && errno != EEXIST) || (symlink("policy/current/domain_policy.conf", "../domain_policy.conf") && errno != EEXIST) || chdir(stamp) || !save_to_file(CCS_PROC_POLICY_PROFILE, "profile.conf") || !save_to_file(CCS_PROC_POLICY_MANAGER, "manager.conf") || !save_to_file(CCS_PROC_POLICY_EXCEPTION_POLICY, "exception_policy.conf") || !save_to_file(CCS_PROC_POLICY_DOMAIN_POLICY, "domain_policy.conf") || chdir("..") || (rename("current", "previous") && errno != ENOENT) || symlink(stamp, "current")) { fprintf(stderr, "Failed to save policy.\n"); exit(1); } } int main(int argc, char *argv[]) { memset(&w, 0, sizeof(w)); memset(&p, 0, sizeof(p)); parse_args(argc, argv); editpolicy_init_keyword_map(); if (w.offline_mode) load_offline(); if (ccs_network_mode) goto start; ccs_mount_securityfs(); if (chdir(CCS_PROC_POLICY_DIR)) { fprintf(stderr, "You can't use this editor for this kernel.\n"); return 1; } if (!w.readonly_mode) load_readwrite(); start: initscr(); editpolicy_color_init(); cbreak(); noecho(); nonl(); intrflush(stdscr, FALSE); keypad(stdscr, TRUE); getmaxyx(stdscr, w.height, w.width); if (w.refresh_interval) { signal(SIGALRM, sigalrm_handler); alarm(w.refresh_interval); timeout(1000); } rl.max = 20; rl.history = ccs_malloc(rl.max * sizeof(const char *)); while (active < MAX_SCREEN_TYPE) { enum screen_type next; resize_window(); next = generic_list_loop(); if (next != active) w.previous_screen = active; active = next; } alarm(0); clear(); move(0, 0); refresh(); endwin(); if (w.offline_mode && !w.readonly_mode) save_offline(); clear_domain_policy(); return 0; } tomoyo-tools/usr_lib_tomoyo/0000755000000000000000000000000014116520000015272 5ustar rootroottomoyo-tools/usr_lib_tomoyo/Makefile0000644000000000000000000000075114116520000016735 0ustar rootrootinclude ../Include.make BUILD_FILES = audit-exec-param tomoyo-editpolicy-agent convert-audit-log \ convert-exec-param init_policy all: $(BUILD_FILES) install: all mkdir -p -m 0755 $(INSTALLDIR)/$(USRLIBDIR)/tomoyo $(INSTALL) -m 0755 $(BUILD_FILES) $(INSTALLDIR)/$(USRLIBDIR)/tomoyo/ $(INSTALL) -m 0644 ../README.tomoyo ../COPYING.tomoyo $(INSTALLDIR)/$(USRLIBDIR)/tomoyo/ .c: $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< clean: rm -f -- $(BUILD_FILES) .PHONY: clean install tomoyo-tools/usr_lib_tomoyo/convert-exec-param.c0000644000000000000000000000764614116520000021153 0ustar rootroot/* * convert-exec-param.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include int main(int argc, char *argv[]) { char buffer[3][65536]; int line = 0; memset(buffer, 0, sizeof(buffer)); if (argc > 1) { fprintf(stderr, "Usage: %s < /sys/kernel/security/tomoyo/audit\n", argv[0]); return 0; } while (1) { int i; char *cp; char *exe; char *args; int envc; char *envs; /* Find header line. */ i = getc(stdin); if (i) ungetc(i, stdin); if (!fgets(buffer[0], sizeof(buffer[0]) - 1, stdin)) break; line++; if (!strchr(buffer[0], '\n')) goto out; if (buffer[0][0] != '#') continue; /* Check for " argc=" part. */ cp = strstr(buffer[0], " argc="); if (!cp) continue; /* Get argc value. */ if (sscanf(cp + 1, "argc=%d", &argc) != 1) goto out; /* Check for " envc=" part. */ cp = strstr(buffer[0], " envc="); if (!cp) continue; /* Get envc value. */ if (sscanf(cp + 1, "envc=%d", &envc) != 1) goto out; /* Get realpath part. */ exe = strstr(buffer[0], " realpath=\""); if (!exe) continue; exe++; /* Get argv[]= part. */ cp = strstr(buffer[0], " argv[]={ "); if (!cp) goto out; args = cp + 10; /* Get envp[]= part. */ cp = strstr(buffer[0], " envp[]={ "); if (!cp) goto out; envs = cp + 10; /* Terminate realpath part. */ cp = strchr(exe, ' '); if (!cp) goto out; *cp = '\0'; /* Terminate argv[] part. */ cp = strstr(args - 1, " } "); if (!cp) goto out; *cp = '\0'; /* Terminate envp[] part. */ cp = strstr(envs - 1, " } "); if (!cp) goto out; *cp = '\0'; /* Get domainname. */ line++; i = getc(stdin); if (i) ungetc(i, stdin); if (!fgets(buffer[1], sizeof(buffer[1]) - 1, stdin) || !strchr(buffer[1], '\n')) goto out; /* Get "file execute " line. */ line++; i = getc(stdin); if (i) ungetc(i, stdin); if (!fgets(buffer[2], sizeof(buffer[2]) - 1, stdin)) goto out; cp = strchr(buffer[2], '\n'); if (!cp) goto out; *cp-- = '\0'; while (*cp == ' ') *cp-- = '\0'; if (strncmp(buffer[2], "file execute ", 13)) continue; /* Print domainname. */ printf("%s", buffer[1]); /* Print permission and exec.realpath part. */ printf("%s exec.%s", buffer[2], exe); /* Print exec.argc part. */ printf(" exec.argc=%d", argc); /* Print exec.argv[] part. */ if (argc) { i = 0; cp = strtok(args, " "); while (cp && *cp == '"') { printf(" exec.argv[%d]=%s", i++, cp); cp = strtok(NULL, " "); } } /* Print exec.envc part. */ printf(" exec.envc=%d", envc); /* Print exec.envp[] part. */ if (envc) { cp = strtok(envs, " "); while (cp && *cp == '"') { char c = *(cp + 1); char *cp2 = cp + 1; if (!c || c == '"' || c == '=') goto bad_env; while (1) { c = *cp2++; if (c == '=') break; if (!c || c == '"') goto bad_env; } if (!*cp2 || *cp2 == '"') goto bad_env; printf(" exec.envp["); while (1) { c = *cp++; if (c == '=') break; putchar(c); } printf("\"]=\"%s", cp); bad_env: cp = strtok(NULL, " "); } } printf("\n\n"); } return 0; out: fprintf(stderr, "%d: Broken log entry. Aborted.\n", line); return 1; } tomoyo-tools/usr_lib_tomoyo/init_policy.c0000644000000000000000000016306414116520000017772 0ustar rootroot/* * init_policy.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2012 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define _FILE_OFFSET_BITS 64 #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #if defined(__GLIBC__) /** * get_realpath - Wrapper for realpath(3). * * @path: Pathname to resolve. * * Returns realpath of @path on success, NULL otherwise. * * Caller must free() the returned pointer if this function did not return * NULL. */ static inline char *get_realpath(const char *path) { return realpath(path, NULL); } #else /** * get_realpath - Fallback routine for realpath(3). * * @path: Pathname to resolve. * * Returns realpath of @path on success, NULL otherwise. * * realpath(@path, NULL) works on GLIBC, but will SIGSEGV on others. * * Caller must free() the returned pointer if this function did not return * NULL. */ static char *get_realpath(const char *path) { struct stat buf; static const int pwd_len = PATH_MAX * 2; char *dir = strdup(path); char *pwd = malloc(pwd_len); char *basename = NULL; int len; if (!dir || !pwd) goto out; if (stat(dir, &buf)) goto out; len = strlen(dir); while (len > 1 && dir[len - 1] == '/') dir[--len] = '\0'; while (!lstat(dir, &buf) && S_ISLNK(buf.st_mode)) { char *new_dir; char *old_dir = dir; memset(pwd, 0, pwd_len); if (readlink(dir, pwd, pwd_len - 1) < 1) goto out; if (pwd[0] == '/') { dir[0] = '\0'; } else { char *cp = strrchr(dir, '/'); if (cp) *cp = '\0'; } len = strlen(dir) + strlen(pwd) + 4; new_dir = malloc(len); if (new_dir) snprintf(new_dir, len - 1, "%s/%s", dir, pwd); dir = new_dir; free(old_dir); if (!dir) goto out; } if (!dir) goto out; basename = strrchr(dir, '/'); if (basename) *basename++ = '\0'; else basename = ""; if (chdir(dir)) goto out; memset(pwd, 0, pwd_len); if (!getcwd(pwd, pwd_len - 1)) goto out; if (strcmp(pwd, "/")) len = strlen(pwd); else len = 0; snprintf(pwd + len, pwd_len - len - 1, "/%s", basename); free(dir); return pwd; out: free(dir); free(pwd); return NULL; } #endif #define elementof(x) (sizeof(x) / sizeof(x[0])) /** * scandir_file_filter - Callback for scandir(). * * @buf: Pointer to "const struct dirent". * * Returns non 0 if @buf seems to be a file, 0 otherwise. * * Since several kernels have a bug that leaves @buf->d_type == DT_UNKNOWN, * we allow it for now and recheck it later. */ static int scandir_file_filter(const struct dirent *buf) { return (buf->d_type == DT_REG || buf->d_type == DT_UNKNOWN) && strcmp(buf->d_name, ".") && strcmp(buf->d_name, ".."); } /** * scandir_symlink_and_dir_filter - Callback for scandir(). * * @buf: Pointer to "const struct dirent". * * Returns non 0 if @buf seems to be a symlink or a directory, 0 otherwise. * * Since several kernels have a bug that leaves @buf->d_type == DT_UNKNOWN, * we allow it for now and recheck it later. */ static int scandir_symlink_and_dir_filter(const struct dirent *buf) { return (buf->d_type == DT_LNK || buf->d_type == DT_DIR || buf->d_type == DT_UNKNOWN) && strcmp(buf->d_name, ".") && strcmp(buf->d_name, ".."); } /** * revalidate_path - Recheck file's attribute. * * @path: Pathname to check. * * Returns type of @path. * * This is needed by buggy kernels that report DT_UNKNOWN upon scandir(). */ static unsigned char revalidate_path(const char *path) { struct stat buf; unsigned char type = DT_UNKNOWN; if (!lstat(path, &buf)) { if (S_ISREG(buf.st_mode)) type = DT_REG; else if (S_ISDIR(buf.st_mode)) type = DT_DIR; else if (S_ISLNK(buf.st_mode)) type = DT_LNK; } return type; } /* File handle to /etc/tomoyo/policy/current/exception_policy.conf . */ static FILE *filp = NULL; /** * echo - Print a line to the policy file, without escaping. * * @str: Line to print. Must follow TOMOYO's escape rules. * * Returns nothing. */ static inline void echo(const char *str) { fprintf(filp, "%s\n", str); } /* Keyword before printing a line. */ static const char *keyword = NULL; /** * printf_encoded - Print a line to the policy file, with escaping as needed. * * @str: Line to print. Needn't to follow TOMOYO's escape rules. * * Returns nothing. * * If @str starts with "/proc/", it is converted with "proc:/". * If keyword is not NULL, keyword is printed before printing @str. * If keyword is "initialize_domain", " from any" is printed after printing * @str. */ static void printf_encoded(const char *str) { if (keyword) fprintf(filp, "%s ", keyword); if (!strncmp(str, "/proc/", 6)) { fprintf(filp, "proc:"); str += 5; } while (1) { const char c = *str++; if (!c) break; if (c == '\\') { fputc('\\', filp); fputc('\\', filp); } else if (c > ' ' && c < 127) { fputc(c, filp); } else { fprintf(filp, "\\%c%c%c", (c >> 6) + '0', ((c >> 3) & 7) + '0', (c & 7) + '0'); } } if (keyword && !strcmp(keyword, "initialize_domain")) fprintf(filp, " from any"); if (keyword) fputc('\n', filp); } /* Shared buffer for scandir(). */ static char path[8192]; /** * scan_init_scripts - Scan /etc/rc\?.d/ directories for initialize_domain entries. * * Returns nothing. */ static void scan_init_scripts(void) { struct dirent **namelist; int n = scandir(path, &namelist, scandir_symlink_and_dir_filter, 0); int len; int i; if (n < 0) return; len = strlen(path); for (i = 0; i < n; i++) { const char *name = namelist[i]->d_name; unsigned char type = namelist[i]->d_type; snprintf(path + len, sizeof(path) - len - 1, "/%s", name); if (type == DT_UNKNOWN) type = revalidate_path(path); if (type == DT_DIR) scan_init_scripts(); else if (type == DT_LNK && (name[0] == 'S' || name[0] == 'K') && (name[1] >= '0' && name[1] <= '9') && (name[2] >= '0' && name[2] <= '9') && !access(path, X_OK)) { char *entity = get_realpath(path); path[len] = '\0'; if (entity) { char *cp = strrchr(path, '/'); fprintf(filp, "aggregator "); /* * Use /rc\?.d/ rather than /rc0.d/ /rc1.d/ * /rc2.d/ /rc3.d/ /rc4.d/ /rc5.d/ /rc6.d/ * /rcS.d/ . */ if (cp && !strncmp(cp, "/rc", 3) && ((cp[3] >= '0' && cp[3] <= '6') || cp[3] == 'S') && !strcmp(cp + 4, ".d")) { *cp = '\0'; printf_encoded(path); fprintf(filp, "/rc\\?.d"); *cp = '/'; } else printf_encoded(path); fprintf(filp, "/\\?\\+\\+"); printf_encoded(name + 3); fputc(' ', filp); printf_encoded(entity); fputc('\n', filp); free(entity); } } free(namelist[i]); } free(namelist); } /** * make_systemd_exceptions - Exceptions specific to systemd * * Returns nothing. */ static void make_systemd_exceptions(void) { /* allow systemd to re-execute itself */ static const char * const systemd[] = { "/lib/systemd/systemd", "/usr/lib/systemd/systemd", }; int i; keyword = NULL; for (i = 0; i < elementof(systemd); i++) { /* Check realpath because /lib may be a symlink to /usr/lib .*/ char *path = get_realpath(systemd[i]); if (!path) continue; fprintf(filp, "keep_domain "); printf_encoded(path); fprintf(filp, " from /sbin/init\n"); free(path); } } /** * make_init_scripts_as_aggregators - Use realpath for startup/shutdown scripts in /etc/ directory. * * Returns nothing. */ static void make_init_scripts_as_aggregators(void) { /* Mark symlinks under /etc/rc\?.d/ directory as aggregator. */ static const char * const dirs[] = { "/etc/boot.d", "/etc/rc.d/boot.d", "/etc/init.d/boot.d", "/etc/rc0.d", "/etc/rd1.d", "/etc/rc2.d", "/etc/rc3.d", "/etc/rc4.d", "/etc/rc5.d", "/etc/rc6.d", "/etc/rcS.d", "/etc/rc.d/rc0.d", "/etc/rc.d/rc1.d", "/etc/rc.d/rc2.d", "/etc/rc.d/rc3.d", "/etc/rc.d/rc4.d", "/etc/rc.d/rc5.d", "/etc/rc.d/rc6.d", }; int i; keyword = NULL; memset(path, 0, sizeof(path)); for (i = 0; i < elementof(dirs); i++) { char *dir = get_realpath(dirs[i]); if (!dir) continue; strncpy(path, dir, sizeof(path) - 1); free(dir); if (!strcmp(path, dirs[i])) scan_init_scripts(); } } /** * scan_executable_files - Find executable files in the specific directory. * * @dir: Directory name to scan. * * Returns nothing. */ static void scan_executable_files(const char *dir) { struct dirent **namelist; int n = scandir(dir, &namelist, scandir_file_filter, 0); int i; if (n < 0) return; for (i = 0; i < n; i++) { unsigned char type = namelist[i]->d_type; snprintf(path, sizeof(path) - 1, "%s/%s", dir, namelist[i]->d_name); if (type == DT_UNKNOWN) type = revalidate_path(path); if (type == DT_REG && !access(path, X_OK)) printf_encoded(path); free(namelist[i]); } free(namelist); } /** * scan_modprobe_and_hotplug - Mark modprobe and hotplug as initialize_domain entries. * * Returns nothing. */ static void scan_modprobe_and_hotplug(void) { static const char * const files[2] = { "/proc/sys/kernel/modprobe", "/proc/sys/kernel/hotplug" }; int i; for (i = 0; i < elementof(files); i++) { char *ret_ignored; char buffer[PATH_MAX + 1]; char *cp; FILE *fp = fopen(files[i], "r"); if (!fp) continue; memset(buffer, 0, sizeof(buffer)); ret_ignored = fgets(buffer, sizeof(buffer) - 1, fp); fclose(fp); cp = strrchr(buffer, '\n'); if (cp) *cp = '\0'; if (!buffer[0]) continue; cp = get_realpath(buffer); if (!cp) continue; /* We ignore /bin/true if /proc/sys/kernel/modprobe said so. */ if (strcmp(cp, "/bin/true") && !access(cp, X_OK)) { keyword = "initialize_domain"; printf_encoded(cp); } free(cp); } } /** * make_globally_readable_files - Mark some files as globally readable. * * Returns nothing. */ static void make_globally_readable_files(void) { /* Allow reading some data files. */ static const char * const files[] = { "/etc/ld.so.cache", "/proc/meminfo", "/proc/sys/kernel/version", "/etc/localtime", "/usr/lib/gconv/gconv-modules.cache", "/usr/lib32/gconv/gconv-modules.cache", "/usr/lib64/gconv/gconv-modules.cache", "/usr/share/locale/locale.alias" }; int i; keyword = "acl_group 0 file read"; for (i = 0; i < elementof(files); i++) { char *cp = get_realpath(files[i]); if (!cp) continue; printf_encoded(cp); free(cp); } } /** * make_self_readable_files - Mark /proc/self/ files as globally readable. * * Returns nothing. */ static void make_self_readable_files(void) { /* Allow reading information for current process. */ echo("acl_group 0 file read proc:/self/\\*"); echo("acl_group 0 file read proc:/self/\\{\\*\\}/\\*"); } /** * make_ldconfig_readable_files - Mark shared library files as globally readable. * * Returns nothing. * * We don't scan predefined directories if ldconfig does not exist (e.g. * embedded environment). */ static void make_ldconfig_readable_files(void) { /* Allow reading DLL files registered with ldconfig(8). */ static const char * const dirs[] = { "/lib/", "/lib/i486/", "/lib/i586/", "/lib/i686/", "/lib/i686/cmov/", "/lib/tls/", "/lib/tls/i486/", "/lib/tls/i586/", "/lib/tls/i686/", "/lib/tls/i686/cmov/", "/lib/i686/nosegneg/", "/usr/lib/", "/usr/lib/i486/", "/usr/lib/i586/", "/usr/lib/i686/", "/usr/lib/i686/cmov/", "/usr/lib/tls/", "/usr/lib/tls/i486/", "/usr/lib/tls/i586/", "/usr/lib/tls/i686/", "/usr/lib/tls/i686/cmov/", "/usr/lib/sse2/", "/usr/X11R6/lib/", "/usr/lib32/", "/usr/lib64/", "/lib64/", "/lib64/tls/", "/usr/lib/x86_64-linux-gnu/", "/lib/x86_64-linux-gnu/", "/usr/lib/i386-linux-gnu/", "/lib/i386-linux-gnu/", "/usr/lib/arm-linux-gnueabihf/", "/lib/arm-linux-gnueabihf/", "/usr/lib/arm-linux-gnueabi/", "/lib/arm-linux-gnueabi/", "/usr/lib/aarch64-linux-gnu/", "/lib/aarch64-linux-gnu/", "/usr/lib/ia64-linux-gnu/", "/lib/ia64-linux-gnu/", "/usr/lib/mips-linux-gnu/", "/lib/mips-linux-gnu/", "/usr/lib/mipsel-linux-gnu/", "/lib/mipsel-linux-gnu/", "/usr/lib/powerpc-linux-gnu/", "/lib/powerpc-linux-gnu/", "/usr/lib/ppc64-linux-gnu/", "/lib/ppc64-linux-gnu/", "/usr/lib/s390-linux-gnu/", "/lib/s390-linux-gnu/", "/usr/lib/s390x-linux-gnu/", "/lib/s390x-linux-gnu/", "/usr/lib/sh4-linux-gnu/", "/lib/sh4-linux-gnu/", "/usr/lib/sparc-linux-gnu/", "/lib/sparc-linux-gnu/", "/usr/lib/sparc64-linux-gnu/", "/lib/sparc64-linux-gnu/", "/usr/lib/x86_64-linux-gnux32/", "/lib/x86_64-linux-gnux32/", }; int i; FILE *fp = !access("/sbin/ldconfig", X_OK) || !access("/bin/ldconfig", X_OK) ? popen("ldconfig -NXp", "r") : NULL; if (!fp) return; keyword = NULL; for (i = 0; i < elementof(dirs); i++) { char *cp = get_realpath(dirs[i]); if (!cp) continue; fprintf(filp, "acl_group 0 file read "); printf_encoded(cp); fprintf(filp, "/lib\\*.so\\*\n"); free(cp); } while (memset(path, 0, sizeof(path)) && fgets(path, sizeof(path) - 1, fp)) { char *cp = strchr(path, '\n'); if (!cp) break; *cp = '\0'; cp = strstr(path, " => "); if (!cp) continue; cp = get_realpath(cp + 4); if (!cp) continue; for (i = 0; i < elementof(dirs); i++) { const int len = strlen(dirs[i]); if (!strncmp(cp, dirs[i], len) && !strncmp(cp + len, "lib", 3) && strstr(cp + len + 3, ".so")) break; } if (i == elementof(dirs)) { char *cp2 = strrchr(cp, '/'); const int len = strlen(cp); char buf[16]; memset(buf, 0, sizeof(buf)); fprintf(filp, "acl_group 0 file read "); if (cp2 && !strncmp(cp2, "/ld-2.", 6) && len > 3 && !strcmp(cp + len - 3, ".so")) *(cp2 + 6) = '\0'; else cp2 = NULL; printf_encoded(cp); if (cp2) fprintf(filp, "\\*.so"); fputc('\n', filp); } free(cp); } pclose(fp); } /** * make_init_dir_as_initializers - Mark programs under /etc/init.d/ directory as initialize_domain entries. * * Returns nothing. */ static void make_init_dir_as_initializers(void) { char *dir = get_realpath("/etc/init.d/"); if (!dir) return; keyword = "initialize_domain"; scan_executable_files(dir); free(dir); } /** * make_initializers - Mark daemon programs as initialize_domain entries. * * Returns nothing. */ static void make_initializers(void) { static const char * const files[] = { "/sbin/cardmgr", "/sbin/getty", "/sbin/init", "/sbin/klogd", "/sbin/mingetty", "/sbin/portmap", "/sbin/rpc.statd", "/sbin/syslogd", "/sbin/udevd", "/usr/X11R6/bin/xfs", "/usr/bin/dbus-daemon", "/usr/bin/dbus-daemon-1", "/usr/bin/jserver", "/usr/bin/mDNSResponder", "/usr/bin/nifd", "/usr/bin/spamd", "/usr/sbin/acpid", "/usr/sbin/afpd", "/usr/sbin/anacron", "/usr/sbin/apache2", "/usr/sbin/apmd", "/usr/sbin/atalkd", "/usr/sbin/atd", "/usr/sbin/cannaserver", "/usr/sbin/cpuspeed", "/usr/sbin/cron", "/usr/sbin/crond", "/usr/sbin/cupsd", "/usr/sbin/dhcpd", "/usr/sbin/exim4", "/usr/sbin/gpm", "/usr/sbin/hald", "/usr/sbin/htt", "/usr/sbin/httpd", "/usr/sbin/inetd", "/usr/sbin/logrotate", "/usr/sbin/lpd", "/usr/sbin/nmbd", "/usr/sbin/papd", "/usr/sbin/rpc.idmapd", "/usr/sbin/rpc.mountd", "/usr/sbin/rpc.rquotad", "/usr/sbin/sendmail.sendmail", "/usr/sbin/smartd", "/usr/sbin/smbd", "/usr/sbin/squid", "/usr/sbin/sshd", "/usr/sbin/vmware-guestd", "/usr/sbin/vsftpd", "/usr/sbin/xinetd" }; int i; keyword = "initialize_domain"; for (i = 0; i < elementof(files); i++) { char *cp = get_realpath(files[i]); if (!cp) continue; if (!access(cp, X_OK)) printf_encoded(cp); free(cp); } } /** * mkdir2 - mkdir() with ignoring EEXIST error. * * @dir: Directory to create. * @mode: Create mode. * * Returns 0 on success, EOF otherwise. */ static int mkdir2(const char *dir, int mode) { return mkdir(dir, mode) == 0 || errno == EEXIST ? 0 : EOF; } /** * symlink2 - symlink() with ignoring EEXIST error. * * @old: Symlink's content. * @new: Symlink to create. * * Returns 0 on success, EOF otherwise. */ static int symlink2(const char *old, const char *new) { return symlink(old, new) == 0 || errno == EEXIST ? 0 : EOF; } /* Policy directory. Default is "/etc/tomoyo/". */ static char *policy_dir = NULL; /** * make_policy_dir - Create policy directories and tools directories. * * Returns nothing. */ static void make_policy_dir(void) { char *dir = policy_dir; const time_t now = time(NULL); struct tm *tm = localtime(&now); char stamp[20] = { }; snprintf(stamp, sizeof(stamp) - 1, "%02d-%02d-%02d.%02d:%02d:%02d", tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); if (!chdir(policy_dir) && !chdir("policy/current/")) goto tools_dir; fprintf(stderr, "Creating policy directory... "); while (1) { const char c = *dir++; if (!c) break; if (c != '/') continue; *(dir - 1) = '\0'; mkdir(policy_dir, 0700); *(dir - 1) = '/'; } if (mkdir2(policy_dir, 0700) || chdir(policy_dir) || symlink2("policy/current/exception_policy.conf", "exception_policy.conf") || symlink2("policy/current/domain_policy.conf", "domain_policy.conf") || symlink2("policy/current/profile.conf", "profile.conf") || symlink2("policy/current/manager.conf", "manager.conf") || mkdir2("policy", 0700) || chdir("policy") || mkdir2(stamp, 0700) || symlink2(stamp, "previous") || symlink2(stamp, "current") || chdir(policy_dir) || chdir("policy/current/")) { fprintf(stderr, "failed.\n"); exit(1); } else { fprintf(stderr, "OK\n"); } tools_dir: if (!chdir(policy_dir) && !chdir("tools")) return; fprintf(stderr, "Creating configuration directory... "); mkdir("tools", 0700); if (!chdir("tools")) fprintf(stderr, "OK\n"); else { fprintf(stderr, "failed.\n"); exit(1); } } /** * make_path_group - Make path_group entries. * * Returns nothing. */ static void make_path_group(void) { echo("path_group ANY_PATHNAME /"); echo("path_group ANY_PATHNAME /\\*"); echo("path_group ANY_PATHNAME /\\{\\*\\}/"); echo("path_group ANY_PATHNAME /\\{\\*\\}/\\*"); echo("path_group ANY_PATHNAME \\*:/"); echo("path_group ANY_PATHNAME \\*:/\\*"); echo("path_group ANY_PATHNAME \\*:/\\{\\*\\}/"); echo("path_group ANY_PATHNAME \\*:/\\{\\*\\}/\\*"); echo("path_group ANY_PATHNAME \\*:[\\$]"); echo("path_group ANY_DIRECTORY /"); echo("path_group ANY_DIRECTORY /\\{\\*\\}/"); echo("path_group ANY_DIRECTORY \\*:/"); echo("path_group ANY_DIRECTORY \\*:/\\{\\*\\}/"); } /** * make_number_group - Make number_group entries. * * Returns nothing. */ static void make_number_group(void) { echo("number_group COMMON_IOCTL_CMDS 0x5401"); } /** * make_ioctl - Allow ioctl with common ioctl numbers. * * Returns nothing. */ static void make_ioctl(void) { echo("acl_group 0 file ioctl @ANY_PATHNAME @COMMON_IOCTL_CMDS"); } /** * make_getattr - Allow getting attributes. * * Returns nothing. */ static void make_getattr(void) { echo("acl_group 0 file getattr @ANY_PATHNAME"); } /** * make_readdir - Allow reading directories. * * Returns nothing. */ static void make_readdir(void) { echo("acl_group 0 file read @ANY_DIRECTORY"); } /** * chdir_policy - Change to policy directory. * * Returns 1 on success, 0 otherwise. */ static _Bool chdir_policy(void) { if (chdir(policy_dir) || chdir("policy/current/")) { fprintf(stderr, "ERROR: Can't chdir to %s/policy/current/ " "directory.\n", policy_dir); return 0; } return 1; } /** * close_file - Close file and rename. * * @fp: Pointer to "FILE". * @condition: Preconditions before rename(). * @old: Temporary file's pathname. * @new: Final file's pathname. * * Returns nothing. */ static void close_file(FILE *fp, _Bool condition, const char *old, const char *new) { if (fsync(fileno(fp)) || fclose(fp) || !condition || rename(old, new)) fprintf(stderr, "failed.\n"); else fprintf(stderr, "OK.\n"); } /** * make_exception_policy - Make /etc/tomoyo/policy/current/exception_policy.conf . * * Returns nothing. */ static void make_exception_policy(void) { if (!chdir_policy()) return; if (!access("exception_policy.conf", R_OK)) return; filp = fopen("exception_policy.tmp", "w"); if (!filp) { fprintf(stderr, "ERROR: Can't create exception policy.\n"); return; } fprintf(stderr, "Creating exception policy... "); make_globally_readable_files(); make_self_readable_files(); make_ldconfig_readable_files(); make_path_group(); make_number_group(); make_ioctl(); make_readdir(); make_getattr(); scan_modprobe_and_hotplug(); make_init_dir_as_initializers(); make_initializers(); make_init_scripts_as_aggregators(); make_systemd_exceptions(); /* Some applications do execve("/proc/self/exe"). */ fprintf(filp, "aggregator proc:/self/exe /proc/self/exe\n"); close_file(filp, chdir_policy(), "exception_policy.tmp", "exception_policy.conf"); filp = NULL; } /** * make_manager - Make /etc/tomoyo/policy/current/manager.conf . * * Returns nothing. */ static void make_manager(void) { char *tools_dir; FILE *fp; if (!chdir_policy()) return; if (!access("manager.conf", R_OK)) return; fp = fopen("manager.tmp", "w"); if (!fp) { fprintf(stderr, "ERROR: Can't create manager policy.\n"); return; } fprintf(stderr, "Creating manager policy... "); tools_dir = get_realpath("/usr/sbin"); fprintf(fp, "%s/tomoyo-loadpolicy\n", tools_dir); fprintf(fp, "%s/tomoyo-editpolicy\n", tools_dir); fprintf(fp, "%s/tomoyo-setlevel\n", tools_dir); fprintf(fp, "%s/tomoyo-setprofile\n", tools_dir); fprintf(fp, "%s/tomoyo-queryd\n", tools_dir); close_file(fp, 1, "manager.tmp", "manager.conf"); } /* Should we create profiles that restricts file only? */ static _Bool file_only_profile = 0; /* Should we audit access granted logs? */ static const char *grant_log = "no"; /* Should we audit access rejected logs? */ static const char *reject_log = "yes"; /* How many audit log entries to spool in the kernel memory? */ static unsigned int max_audit_log = 1024; /* How many ACL entries to add automatically by learning mode? */ static unsigned int max_learning_entry = 2048; /** * make_profile - Make /etc/tomoyo/policy/current/profile.conf . * * Returns nothing. */ static void make_profile(void) { static const char *file_only = ""; FILE *fp; if (!chdir_policy()) return; if (!access("profile.conf", R_OK)) return; fp = fopen("profile.tmp", "w"); if (!fp) { fprintf(stderr, "ERROR: Can't create profile policy.\n"); return; } fprintf(stderr, "Creating default profile... "); if (file_only_profile) file_only = "::file"; fprintf(fp, "PROFILE_VERSION=20150505\n"); fprintf(fp, "0-COMMENT=-----Disabled Mode-----\n" "0-PREFERENCE={ max_audit_log=%u max_learning_entry=%u }\n" "0-CONFIG%s={ mode=disabled grant_log=%s reject_log=%s }\n", max_audit_log, max_learning_entry, file_only, grant_log, reject_log); fprintf(fp, "1-COMMENT=-----Learning Mode-----\n" "1-PREFERENCE={ max_audit_log=%u max_learning_entry=%u }\n" "1-CONFIG%s={ mode=learning grant_log=%s reject_log=%s }\n", max_audit_log, max_learning_entry, file_only, grant_log, reject_log); fprintf(fp, "2-COMMENT=-----Permissive Mode-----\n" "2-PREFERENCE={ max_audit_log=%u max_learning_entry=%u }\n" "2-CONFIG%s={ mode=permissive grant_log=%s reject_log=%s }\n", max_audit_log, max_learning_entry, file_only, grant_log, reject_log); fprintf(fp, "3-COMMENT=-----Enforcing Mode-----\n" "3-PREFERENCE={ max_audit_log=%u max_learning_entry=%u }\n" "3-CONFIG%s={ mode=enforcing grant_log=%s reject_log=%s }\n", max_audit_log, max_learning_entry, file_only, grant_log, reject_log); close_file(fp, 1, "profile.tmp", "profile.conf"); } /* Which profile number does domain use? */ static unsigned char default_profile = 0; /* Which ACL group does domain use? */ static _Bool use_group[256] = { }; /** * make_domain_policy - Make /etc/tomoyo/policy/current/domain_policy.conf . * * Returns nothing. */ static void make_domain_policy(void) { FILE *fp; int i; if (!chdir_policy()) return; if (!access("domain_policy.conf", R_OK)) return; fp = fopen("domain_policy.tmp", "w"); if (!fp) { fprintf(stderr, "ERROR: Can't create domain policy.\n"); return; } fprintf(stderr, "Creating domain policy... "); fprintf(fp, "\nuse_profile %u\n", default_profile); for (i = 0; i < 256; i++) if (use_group[i]) fprintf(fp, "use_group %u\n", i); close_file(fp, 1, "domain_policy.tmp", "domain_policy.conf"); } /** * make_stat - Make /etc/tomoyo/stat.conf . * * Returns nothing. */ static void make_stat(void) { FILE *fp; if (chdir(policy_dir) || !access("stat.conf", R_OK)) return; fp = fopen("stat.tmp", "w"); if (!fp) { fprintf(stderr, "ERROR: Can't create stat policy.\n"); return; } fprintf(stderr, "Creating stat policy... "); fprintf(fp, "# Memory quota (byte). 0 means no quota.\n"); fprintf(fp, "Memory used by policy: 0\n"); fprintf(fp, "Memory used by audit log: 16777216\n"); fprintf(fp, "Memory used by query message: 1048576\n"); close_file(fp, 1, "stat.tmp", "stat.conf"); } /* Content of /etc/tomoyo/tools/editpolicy.conf . */ static const char editpolicy_data[] = "# This file contains configuration used by tomoyo-editpolicy command.\n" "\n" "# Keyword alias. ( directive-name = display-name )\n" "keyword_alias acl_group 0 = acl_group 0\n" "keyword_alias acl_group 1 = acl_group 1\n" "keyword_alias acl_group 2 = acl_group 2\n" "keyword_alias acl_group 3 = acl_group 3\n" "keyword_alias acl_group 4 = acl_group 4\n" "keyword_alias acl_group 5 = acl_group 5\n" "keyword_alias acl_group 6 = acl_group 6\n" "keyword_alias acl_group 7 = acl_group 7\n" "keyword_alias acl_group 8 = acl_group 8\n" "keyword_alias acl_group 9 = acl_group 9\n" "keyword_alias acl_group 10 = acl_group 10\n" "keyword_alias acl_group 11 = acl_group 11\n" "keyword_alias acl_group 12 = acl_group 12\n" "keyword_alias acl_group 13 = acl_group 13\n" "keyword_alias acl_group 14 = acl_group 14\n" "keyword_alias acl_group 15 = acl_group 15\n" "keyword_alias acl_group 16 = acl_group 16\n" "keyword_alias acl_group 17 = acl_group 17\n" "keyword_alias acl_group 18 = acl_group 18\n" "keyword_alias acl_group 19 = acl_group 19\n" "keyword_alias acl_group 20 = acl_group 20\n" "keyword_alias acl_group 21 = acl_group 21\n" "keyword_alias acl_group 22 = acl_group 22\n" "keyword_alias acl_group 23 = acl_group 23\n" "keyword_alias acl_group 24 = acl_group 24\n" "keyword_alias acl_group 25 = acl_group 25\n" "keyword_alias acl_group 26 = acl_group 26\n" "keyword_alias acl_group 27 = acl_group 27\n" "keyword_alias acl_group 28 = acl_group 28\n" "keyword_alias acl_group 29 = acl_group 29\n" "keyword_alias acl_group 30 = acl_group 30\n" "keyword_alias acl_group 31 = acl_group 31\n" "keyword_alias acl_group 32 = acl_group 32\n" "keyword_alias acl_group 33 = acl_group 33\n" "keyword_alias acl_group 34 = acl_group 34\n" "keyword_alias acl_group 35 = acl_group 35\n" "keyword_alias acl_group 36 = acl_group 36\n" "keyword_alias acl_group 37 = acl_group 37\n" "keyword_alias acl_group 38 = acl_group 38\n" "keyword_alias acl_group 39 = acl_group 39\n" "keyword_alias acl_group 40 = acl_group 40\n" "keyword_alias acl_group 41 = acl_group 41\n" "keyword_alias acl_group 42 = acl_group 42\n" "keyword_alias acl_group 43 = acl_group 43\n" "keyword_alias acl_group 44 = acl_group 44\n" "keyword_alias acl_group 45 = acl_group 45\n" "keyword_alias acl_group 46 = acl_group 46\n" "keyword_alias acl_group 47 = acl_group 47\n" "keyword_alias acl_group 48 = acl_group 48\n" "keyword_alias acl_group 49 = acl_group 49\n" "keyword_alias acl_group 50 = acl_group 50\n" "keyword_alias acl_group 51 = acl_group 51\n" "keyword_alias acl_group 52 = acl_group 52\n" "keyword_alias acl_group 53 = acl_group 53\n" "keyword_alias acl_group 54 = acl_group 54\n" "keyword_alias acl_group 55 = acl_group 55\n" "keyword_alias acl_group 56 = acl_group 56\n" "keyword_alias acl_group 57 = acl_group 57\n" "keyword_alias acl_group 58 = acl_group 58\n" "keyword_alias acl_group 59 = acl_group 59\n" "keyword_alias acl_group 60 = acl_group 60\n" "keyword_alias acl_group 61 = acl_group 61\n" "keyword_alias acl_group 62 = acl_group 62\n" "keyword_alias acl_group 63 = acl_group 63\n" "keyword_alias acl_group 64 = acl_group 64\n" "keyword_alias acl_group 65 = acl_group 65\n" "keyword_alias acl_group 66 = acl_group 66\n" "keyword_alias acl_group 67 = acl_group 67\n" "keyword_alias acl_group 68 = acl_group 68\n" "keyword_alias acl_group 69 = acl_group 69\n" "keyword_alias acl_group 70 = acl_group 70\n" "keyword_alias acl_group 71 = acl_group 71\n" "keyword_alias acl_group 72 = acl_group 72\n" "keyword_alias acl_group 73 = acl_group 73\n" "keyword_alias acl_group 74 = acl_group 74\n" "keyword_alias acl_group 75 = acl_group 75\n" "keyword_alias acl_group 76 = acl_group 76\n" "keyword_alias acl_group 77 = acl_group 77\n" "keyword_alias acl_group 78 = acl_group 78\n" "keyword_alias acl_group 79 = acl_group 79\n" "keyword_alias acl_group 80 = acl_group 80\n" "keyword_alias acl_group 81 = acl_group 81\n" "keyword_alias acl_group 82 = acl_group 82\n" "keyword_alias acl_group 83 = acl_group 83\n" "keyword_alias acl_group 84 = acl_group 84\n" "keyword_alias acl_group 85 = acl_group 85\n" "keyword_alias acl_group 86 = acl_group 86\n" "keyword_alias acl_group 87 = acl_group 87\n" "keyword_alias acl_group 88 = acl_group 88\n" "keyword_alias acl_group 89 = acl_group 89\n" "keyword_alias acl_group 90 = acl_group 90\n" "keyword_alias acl_group 91 = acl_group 91\n" "keyword_alias acl_group 92 = acl_group 92\n" "keyword_alias acl_group 93 = acl_group 93\n" "keyword_alias acl_group 94 = acl_group 94\n" "keyword_alias acl_group 95 = acl_group 95\n" "keyword_alias acl_group 96 = acl_group 96\n" "keyword_alias acl_group 97 = acl_group 97\n" "keyword_alias acl_group 98 = acl_group 98\n" "keyword_alias acl_group 99 = acl_group 99\n" "keyword_alias acl_group 100 = acl_group 100\n" "keyword_alias acl_group 101 = acl_group 101\n" "keyword_alias acl_group 102 = acl_group 102\n" "keyword_alias acl_group 103 = acl_group 103\n" "keyword_alias acl_group 104 = acl_group 104\n" "keyword_alias acl_group 105 = acl_group 105\n" "keyword_alias acl_group 106 = acl_group 106\n" "keyword_alias acl_group 107 = acl_group 107\n" "keyword_alias acl_group 108 = acl_group 108\n" "keyword_alias acl_group 109 = acl_group 109\n" "keyword_alias acl_group 110 = acl_group 110\n" "keyword_alias acl_group 111 = acl_group 111\n" "keyword_alias acl_group 112 = acl_group 112\n" "keyword_alias acl_group 113 = acl_group 113\n" "keyword_alias acl_group 114 = acl_group 114\n" "keyword_alias acl_group 115 = acl_group 115\n" "keyword_alias acl_group 116 = acl_group 116\n" "keyword_alias acl_group 117 = acl_group 117\n" "keyword_alias acl_group 118 = acl_group 118\n" "keyword_alias acl_group 119 = acl_group 119\n" "keyword_alias acl_group 120 = acl_group 120\n" "keyword_alias acl_group 121 = acl_group 121\n" "keyword_alias acl_group 122 = acl_group 122\n" "keyword_alias acl_group 123 = acl_group 123\n" "keyword_alias acl_group 124 = acl_group 124\n" "keyword_alias acl_group 125 = acl_group 125\n" "keyword_alias acl_group 126 = acl_group 126\n" "keyword_alias acl_group 127 = acl_group 127\n" "keyword_alias acl_group 128 = acl_group 128\n" "keyword_alias acl_group 129 = acl_group 129\n" "keyword_alias acl_group 130 = acl_group 130\n" "keyword_alias acl_group 131 = acl_group 131\n" "keyword_alias acl_group 132 = acl_group 132\n" "keyword_alias acl_group 133 = acl_group 133\n" "keyword_alias acl_group 134 = acl_group 134\n" "keyword_alias acl_group 135 = acl_group 135\n" "keyword_alias acl_group 136 = acl_group 136\n" "keyword_alias acl_group 137 = acl_group 137\n" "keyword_alias acl_group 138 = acl_group 138\n" "keyword_alias acl_group 139 = acl_group 139\n" "keyword_alias acl_group 140 = acl_group 140\n" "keyword_alias acl_group 141 = acl_group 141\n" "keyword_alias acl_group 142 = acl_group 142\n" "keyword_alias acl_group 143 = acl_group 143\n" "keyword_alias acl_group 144 = acl_group 144\n" "keyword_alias acl_group 145 = acl_group 145\n" "keyword_alias acl_group 146 = acl_group 146\n" "keyword_alias acl_group 147 = acl_group 147\n" "keyword_alias acl_group 148 = acl_group 148\n" "keyword_alias acl_group 149 = acl_group 149\n" "keyword_alias acl_group 150 = acl_group 150\n" "keyword_alias acl_group 151 = acl_group 151\n" "keyword_alias acl_group 152 = acl_group 152\n" "keyword_alias acl_group 153 = acl_group 153\n" "keyword_alias acl_group 154 = acl_group 154\n" "keyword_alias acl_group 155 = acl_group 155\n" "keyword_alias acl_group 156 = acl_group 156\n" "keyword_alias acl_group 157 = acl_group 157\n" "keyword_alias acl_group 158 = acl_group 158\n" "keyword_alias acl_group 159 = acl_group 159\n" "keyword_alias acl_group 160 = acl_group 160\n" "keyword_alias acl_group 161 = acl_group 161\n" "keyword_alias acl_group 162 = acl_group 162\n" "keyword_alias acl_group 163 = acl_group 163\n" "keyword_alias acl_group 164 = acl_group 164\n" "keyword_alias acl_group 165 = acl_group 165\n" "keyword_alias acl_group 166 = acl_group 166\n" "keyword_alias acl_group 167 = acl_group 167\n" "keyword_alias acl_group 168 = acl_group 168\n" "keyword_alias acl_group 169 = acl_group 169\n" "keyword_alias acl_group 170 = acl_group 170\n" "keyword_alias acl_group 171 = acl_group 171\n" "keyword_alias acl_group 172 = acl_group 172\n" "keyword_alias acl_group 173 = acl_group 173\n" "keyword_alias acl_group 174 = acl_group 174\n" "keyword_alias acl_group 175 = acl_group 175\n" "keyword_alias acl_group 176 = acl_group 176\n" "keyword_alias acl_group 177 = acl_group 177\n" "keyword_alias acl_group 178 = acl_group 178\n" "keyword_alias acl_group 179 = acl_group 179\n" "keyword_alias acl_group 180 = acl_group 180\n" "keyword_alias acl_group 181 = acl_group 181\n" "keyword_alias acl_group 182 = acl_group 182\n" "keyword_alias acl_group 183 = acl_group 183\n" "keyword_alias acl_group 184 = acl_group 184\n" "keyword_alias acl_group 185 = acl_group 185\n" "keyword_alias acl_group 186 = acl_group 186\n" "keyword_alias acl_group 187 = acl_group 187\n" "keyword_alias acl_group 188 = acl_group 188\n" "keyword_alias acl_group 189 = acl_group 189\n" "keyword_alias acl_group 190 = acl_group 190\n" "keyword_alias acl_group 191 = acl_group 191\n" "keyword_alias acl_group 192 = acl_group 192\n" "keyword_alias acl_group 193 = acl_group 193\n" "keyword_alias acl_group 194 = acl_group 194\n" "keyword_alias acl_group 195 = acl_group 195\n" "keyword_alias acl_group 196 = acl_group 196\n" "keyword_alias acl_group 197 = acl_group 197\n" "keyword_alias acl_group 198 = acl_group 198\n" "keyword_alias acl_group 199 = acl_group 199\n" "keyword_alias acl_group 200 = acl_group 200\n" "keyword_alias acl_group 201 = acl_group 201\n" "keyword_alias acl_group 202 = acl_group 202\n" "keyword_alias acl_group 203 = acl_group 203\n" "keyword_alias acl_group 204 = acl_group 204\n" "keyword_alias acl_group 205 = acl_group 205\n" "keyword_alias acl_group 206 = acl_group 206\n" "keyword_alias acl_group 207 = acl_group 207\n" "keyword_alias acl_group 208 = acl_group 208\n" "keyword_alias acl_group 209 = acl_group 209\n" "keyword_alias acl_group 210 = acl_group 210\n" "keyword_alias acl_group 211 = acl_group 211\n" "keyword_alias acl_group 212 = acl_group 212\n" "keyword_alias acl_group 213 = acl_group 213\n" "keyword_alias acl_group 214 = acl_group 214\n" "keyword_alias acl_group 215 = acl_group 215\n" "keyword_alias acl_group 216 = acl_group 216\n" "keyword_alias acl_group 217 = acl_group 217\n" "keyword_alias acl_group 218 = acl_group 218\n" "keyword_alias acl_group 219 = acl_group 219\n" "keyword_alias acl_group 220 = acl_group 220\n" "keyword_alias acl_group 221 = acl_group 221\n" "keyword_alias acl_group 222 = acl_group 222\n" "keyword_alias acl_group 223 = acl_group 223\n" "keyword_alias acl_group 224 = acl_group 224\n" "keyword_alias acl_group 225 = acl_group 225\n" "keyword_alias acl_group 226 = acl_group 226\n" "keyword_alias acl_group 227 = acl_group 227\n" "keyword_alias acl_group 228 = acl_group 228\n" "keyword_alias acl_group 229 = acl_group 229\n" "keyword_alias acl_group 230 = acl_group 230\n" "keyword_alias acl_group 231 = acl_group 231\n" "keyword_alias acl_group 232 = acl_group 232\n" "keyword_alias acl_group 233 = acl_group 233\n" "keyword_alias acl_group 234 = acl_group 234\n" "keyword_alias acl_group 235 = acl_group 235\n" "keyword_alias acl_group 236 = acl_group 236\n" "keyword_alias acl_group 237 = acl_group 237\n" "keyword_alias acl_group 238 = acl_group 238\n" "keyword_alias acl_group 239 = acl_group 239\n" "keyword_alias acl_group 240 = acl_group 240\n" "keyword_alias acl_group 241 = acl_group 241\n" "keyword_alias acl_group 242 = acl_group 242\n" "keyword_alias acl_group 243 = acl_group 243\n" "keyword_alias acl_group 244 = acl_group 244\n" "keyword_alias acl_group 245 = acl_group 245\n" "keyword_alias acl_group 246 = acl_group 246\n" "keyword_alias acl_group 247 = acl_group 247\n" "keyword_alias acl_group 248 = acl_group 248\n" "keyword_alias acl_group 249 = acl_group 249\n" "keyword_alias acl_group 250 = acl_group 250\n" "keyword_alias acl_group 251 = acl_group 251\n" "keyword_alias acl_group 252 = acl_group 252\n" "keyword_alias acl_group 253 = acl_group 253\n" "keyword_alias acl_group 254 = acl_group 254\n" "keyword_alias acl_group 255 = acl_group 255\n" "keyword_alias address_group = address_group\n" "keyword_alias aggregator = aggregator\n" "keyword_alias file append = file append\n" "keyword_alias file chgrp = file chgrp\n" "keyword_alias file chmod = file chmod\n" "keyword_alias file chown = file chown\n" "keyword_alias file chroot = file chroot\n" "keyword_alias file create = file create\n" "keyword_alias file execute = file execute\n" "keyword_alias file getattr = file getattr\n" "keyword_alias file ioctl = file ioctl\n" "keyword_alias file link = file link\n" "keyword_alias file mkblock = file mkblock\n" "keyword_alias file mkchar = file mkchar\n" "keyword_alias file mkdir = file mkdir\n" "keyword_alias file mkfifo = file mkfifo\n" "keyword_alias file mksock = file mksock\n" "keyword_alias file mount = file mount\n" "keyword_alias file pivot_root = file pivot_root\n" "keyword_alias file read = file read\n" "keyword_alias file rename = file rename\n" "keyword_alias file rmdir = file rmdir\n" "keyword_alias file symlink = file symlink\n" "keyword_alias file truncate = file truncate\n" "keyword_alias file unlink = file unlink\n" "keyword_alias file unmount = file unmount\n" "keyword_alias file write = file write\n" "keyword_alias initialize_domain = initialize_domain\n" "keyword_alias keep_domain = keep_domain\n" "keyword_alias misc env = misc env\n" "keyword_alias network inet = network inet\n" "keyword_alias network unix = network unix\n" "keyword_alias no_initialize_domain = no_initialize_domain\n" "keyword_alias no_keep_domain = no_keep_domain\n" "keyword_alias no_reset_domain = no_reset_domain\n" "keyword_alias number_group = number_group\n" "keyword_alias path_group = path_group\n" "keyword_alias quota_exceeded = quota_exceeded\n" "keyword_alias reset_domain = reset_domain\n" "keyword_alias task manual_domain_transition = task manual_domain_transition\n" "keyword_alias transition_failed = transition_failed\n" "keyword_alias use_group = use_group\n" "keyword_alias use_profile = use_profile\n" "\n" "# Line color. 0 = BLACK, 1 = RED, 2 = GREEN, 3 = YELLOW, 4 = BLUE, " "5 = MAGENTA, 6 = CYAN, 7 = WHITE\n" "line_color ACL_CURSOR = 03\n" "line_color ACL_HEAD = 03\n" "line_color DOMAIN_CURSOR = 02\n" "line_color DOMAIN_HEAD = 02\n" "line_color EXCEPTION_CURSOR = 06\n" "line_color EXCEPTION_HEAD = 06\n" "line_color MANAGER_CURSOR = 72\n" "line_color MANAGER_HEAD = 72\n" "line_color STAT_CURSOR = 03\n" "line_color STAT_HEAD = 03\n" "line_color PROFILE_CURSOR = 71\n" "line_color PROFILE_HEAD = 71\n" "line_color DEFAULT_COLOR = 70\n"; /** * make_editpolicy_conf - Make /etc/tomoyo/tools/editpolicy.conf . * * Returns nothing. */ static void make_editpolicy_conf(void) { FILE *fp; if (chdir(policy_dir) || chdir("tools") || !access("editpolicy.conf", R_OK)) return; fp = fopen("editpolicy.tmp", "w"); if (!fp) { fprintf(stderr, "ERROR: Can't create configuration file.\n"); return; } fprintf(stderr, "Creating configuration file for tomoyo-editpolicy ... "); fprintf(fp, "%s", editpolicy_data); close_file(fp, !chmod("editpolicy.tmp", 0644), "editpolicy.tmp", "editpolicy.conf"); } /* Content of /etc/tomoyo/tools/auditd.conf . */ static const char auditd_data[] = "# This file contains sorting rules used by tomoyo-auditd command.\n" "\n" "# An audit log consists with three lines. You can refer the first line\n" "# using 'header' keyword, the second line using 'domain' keyword, and the\n" "# third line using 'acl' keyword.\n" "#\n" "# Words in each line are separated by a space character. Therefore, you can\n" "# use 'header[index]', 'domain[index]', 'acl[index]' for referring index'th\n" "# word of the line. The index starts from 1, and 0 refers the whole line\n" "# (i.e. 'header[0]' = 'header', 'domain[0]' = 'domain', 'acl[0]' = 'acl').\n" "#\n" "# Three operators are provided for conditional sorting.\n" "# '.contains' is for 'fgrep keyword' match.\n" "# '.equals' is for 'grep ^keyword$' match.\n" "# '.starts' is for 'grep ^keyword' match.\n" "#\n" "# Sorting rules are defined using multi-lined chunks. A chunk is terminated\n" "# by a 'destination' line which specifies the pathname to write the audit\n" "# log. A 'destination' line is processed only when all preceding 'header',\n" "# 'domain' and 'acl' lines in that chunk have matched.\n" "# Evaluation stops at the first processed 'destination' line.\n" "# Therefore, no audit logs are written more than once.\n" "#\n" "# More specific matches should be placed before less specific matches.\n" "# For example:\n" "#\n" "# header.contains profile=3\n" "# domain.contains /usr/sbin/httpd\n" "# destination /var/log/tomoyo/reject_003_httpd.log\n" "#\n" "# This chunk should be placed before the chunk that matches logs with\n" "# profile=3. If placed after, the audit logs for /usr/sbin/httpd will be\n" "# sent to /var/log/tomoyo/reject_003.log .\n" "\n" "# Please use TOMOYO Linux's escape rule (e.g. '\\040' rather than '\\ ' for\n" "# representing a ' ' in a word).\n" "\n" "# Discard all granted logs.\n" "header.contains granted=yes\n" "destination /dev/null\n" "\n" "# Save rejected logs with profile=0 to /var/log/tomoyo/reject_000.log\n" "header.contains profile=0\n" "destination /var/log/tomoyo/reject_000.log\n" "\n" "# Save rejected logs with profile=1 to /var/log/tomoyo/reject_001.log\n" "header.contains profile=1\n" "destination /var/log/tomoyo/reject_001.log\n" "\n" "# Save rejected logs with profile=2 to /var/log/tomoyo/reject_002.log\n" "header.contains profile=2\n" "destination /var/log/tomoyo/reject_002.log\n" "\n" "# Save rejected logs with profile=3 to /var/log/tomoyo/reject_003.log\n" "header.contains profile=3\n" "destination /var/log/tomoyo/reject_003.log\n" "\n"; /** * make_auditd_conf - Make /etc/tomoyo/tools/auditd.conf . * * Returns nothing. */ static void make_auditd_conf(void) { FILE *fp; if (chdir(policy_dir) || chdir("tools") || !access("auditd.conf", R_OK)) return; fp = fopen("auditd.tmp", "w"); if (!fp) { fprintf(stderr, "ERROR: Can't create configuration file.\n"); return; } fprintf(stderr, "Creating configuration file for tomoyo-auditd ... "); fprintf(fp, "%s", auditd_data); close_file(fp, !chmod("auditd.tmp", 0644), "auditd.tmp", "auditd.conf"); } /* Content of /etc/tomoyo/tools/patternize.conf . */ static const char patternize_data[] = "# This file contains rewriting rules used by tomoyo-patternize command.\n" "\n" "# Domain policy consists with domain declaration lines (which start with\n" "# '<' ,) and acl declaration lines (which do not start with '<' ).\n" "# You can refer the former using 'domain' keyword and the latter using 'acl'" "\n" "# keyword.\n" "#\n" "# Words in each line are separated by a space character. Therefore, you can\n" "# use 'domain[index]', 'acl[index]' for referring index'th word of the line." "\n" "# The index starts from 1, and 0 refers the whole line (i.e.\n" "# 'domain[0]' = 'domain', 'acl[0]' = 'acl').\n" "#\n" "# Three operators are provided for conditional rewriting.\n" "# '.contains' is for 'fgrep keyword' match.\n" "# '.equals' is for 'grep ^keyword$' match.\n" "# '.starts' is for 'grep ^keyword' match.\n" "#\n" "# Rewriting rules are defined using multi-lined chunks. A chunk is terminated" "\n" "# by a 'rewrite' line which specifies old pattern and new pattern.\n" "# A 'rewrite' line is evaluated only when all preceding 'domain' and 'acl'\n" "# lines in that chunk have matched.\n" "# Evaluation stops at first 'rewrite' line where a word matched old pattern." "\n" "# Therefore, no words are rewritten more than once.\n" "#\n" "# For user's convenience, new pattern can be omitted if old pattern is reused" "\n" "# for new pattern.\n" "\n" "# Please use TOMOYO Linux's escape rule (e.g. '\\040' rather than '\\ ' for\n" "# representing a ' ' in a word).\n" "\n" "# Files on proc filesystem.\n" "rewrite path_pattern proc:/self/task/\\$/fdinfo/\\$\n" "rewrite path_pattern proc:/self/task/\\$/fd/\\$\n" "rewrite head_pattern proc:/self/task/\\$/\n" "rewrite path_pattern proc:/self/fdinfo/\\$\n" "rewrite path_pattern proc:/self/fd/\\$\n" "rewrite head_pattern proc:/self/\n" "rewrite path_pattern proc:/\\$/task/\\$/fdinfo/\\$\n" "rewrite path_pattern proc:/\\$/task/\\$/fd/\\$\n" "rewrite head_pattern proc:/\\$/task/\\$/\n" "rewrite path_pattern proc:/\\$/fdinfo/\\$\n" "rewrite path_pattern proc:/\\$/fd/\\$\n" "rewrite head_pattern proc:/\\$/\n" "\n" "# Files on devpts filesystem.\n" "rewrite path_pattern devpts:/\\$\n" "\n" "# Files on pipe filesystem.\n" "rewrite path_pattern pipe:[\\$]\n" "rewrite path_pattern pipefs:/[\\$]\n" "\n" "# Files on / partition.\n" "rewrite tail_pattern /etc/mtab~\\$\n" "rewrite tail_pattern /etc/tomoyo/policy/\\*/domain_policy.conf\n" "rewrite tail_pattern /etc/tomoyo/policy/\\*/exception_policy.conf\n" "rewrite tail_pattern /etc/tomoyo/policy/\\*/manager.conf\n" "rewrite tail_pattern /etc/tomoyo/policy/\\*/profile.conf\n" "rewrite tail_pattern /etc/tomoyo/policy/\\*/\n" "\n" "# Files on /tmp/ partition.\n" "rewrite tail_pattern /vte\\?\\?\\?\\?\\?\\?\n" "rewrite tail_pattern /.ICE-unix/\\$\n" "rewrite tail_pattern /keyring-\\?\\?\\?\\?\\?\\?/socket.ssh\n" "rewrite tail_pattern /orbit-\\*/bonobo-activation-register-\\X.lock\n" "rewrite tail_pattern /orbit-\\*/bonobo-activation-server-\\X-ior\n" "rewrite tail_pattern /orbit-\\*/linc-\\*\n" "rewrite tail_pattern /orbit-\\*/\n" "rewrite tail_pattern /sh-thd-\\$\n" "rewrite tail_pattern /zman\\?\\?\\?\\?\\?\\?\n" "\n" "# Files on home directory.\n" "rewrite tail_pattern /.ICEauthority-\\?\n" "rewrite tail_pattern /.xauth\\?\\?\\?\\?\\?\\?\n" "rewrite tail_pattern /.xauth\\?\\?\\?\\?\\?\\?-?\n" "rewrite tail_pattern " "/.local/share/applications/preferred-mail-reader.desktop.\\?\\?\\?\\?\\?\\?\n" "rewrite tail_pattern " "/.local/share/applications/preferred-web-browser.desktop.\\?\\?\\?\\?\\?\\?\n" "\n" "# Files on /var/ partition.\n" "rewrite tail_pattern /cache/fontconfig/\\X-le64.cache-3\n" "rewrite tail_pattern /lib/gdm/.pulse/\\X-default-source\n" "rewrite tail_pattern /lib/gdm/.pulse/\\X-default-sink\n" "rewrite tail_pattern /lib/gdm/.dbus/session-bus/\\X-\\X\n" "rewrite tail_pattern /run/gdm/auth-for-\\*/database-\\?\n" "rewrite tail_pattern /run/gdm/auth-for-\\*/database\n" "rewrite tail_pattern /run/gdm/auth-for-\\*/\n" "rewrite tail_pattern /spool/abrt/pyhook-\\*/\\{\\*\\}/\\*\n" "rewrite tail_pattern /spool/abrt/pyhook-\\*/\\{\\*\\}/\n" "\n"; /** * make_patternize_conf - Make /etc/tomoyo/tools/patternize.conf . * * Returns nothing. */ static void make_patternize_conf(void) { FILE *fp; if (chdir(policy_dir) || chdir("tools") || !access("patternize.conf", R_OK)) return; fp = fopen("patternize.tmp", "w"); if (!fp) { fprintf(stderr, "ERROR: Can't create configuration file.\n"); return; } fprintf(stderr, "Creating configuration file for tomoyo-patternize ... "); fprintf(fp, "%s", patternize_data); close_file(fp, !chmod("patternize.tmp", 0644), "patternize.tmp", "patternize.conf"); } /* Content of /etc/tomoyo/tools/notifyd.conf . */ static const char notifyd_data[] = "# This file contains configuration used by tomoyo-notifyd command.\n" "\n" "# tomoyo-notifyd is a daemon that notifies the occurrence of policy violation\n" "# in enforcing mode.\n" "#\n" "# time_to_wait is grace time in second before rejecting the request that\n" "# caused policy violation in enforcing mode. For example, if you specify\n" "# 30, you will be given 30 seconds for starting tomoyo-queryd command and\n" "# responding to the policy violation event.\n" "# If you specify non 0 value, you need to register tomoyo-notifyd command to\n" "# /sys/kernel/security/tomoyo/manager as well as tomoyo-queryd command, for tomoyo-notifyd needs to\n" "# behave as if tomoyo-queryd command is running.\n" "# Also, you should avoid specifying too large value (e.g. 3600) because\n" "# the request will remain pending for that period if you can't respond.\n" "#\n" "# action_to_take is a command line you want to use for notification.\n" "# The command specified by this parameter must read the policy violation\n" "# notification from standard input. For example, mail, curl and xmessage\n" "# commands can read from standard input.\n" "# This parameter is passed to execve(). Thus, please use a wrapper program\n" "# if you need shell processing (e.g. wildcard expansion, environment\n" "# variables).\n" "#\n" "# minimal_interval is grace time in second before re-notifying the next\n" "# occurrence of policy violation. You can specify 60 to limit notification\n" "# to once per a minute, 3600 to limit notification to once per an hour.\n" "# You can specify 0 to unlimit, but notifying of every policy violation\n" "# events (e.g. sending a mail) might annoy you because policy violation\n" "# can occur in clusters if once occurred.\n" "\n" "# Please use TOMOYO Linux's escape rule (e.g. '\\040' rather than '\\ ' for\n" "# representing a ' ' in a word).\n" "\n" "# Examples:\n" "#\n" "# time_to_wait 180\n" "# action_to_take mail admin@example.com\n" "#\n" "# Wait for 180 seconds before rejecting the request.\n" "# The occurrence is notified by sending mail to admin@example.com\n" "# (if SMTP service is available).\n" "#\n" "# time_to_wait 0\n" "# action_to_take curl --data-binary @- https://your.server/path_to_cgi\n" "#\n" "# Reject the request immediately.\n" "# The occurrence is notified by executing curl command.\n" "#\n" "time_to_wait 0\n" "action_to_take mail -s Notification\\040from\\040tomoyo-notifyd root@localhost\n" "minimal_interval 60\n" "\n"; /** * make_notifyd_conf - Make /etc/tomoyo/tools/notifyd.conf . * * Returns nothing. */ static void make_notifyd_conf(void) { FILE *fp; if (chdir(policy_dir) || chdir("tools") || !access("notifyd.conf", R_OK)) return; fp = fopen("notifyd.tmp", "w"); if (!fp) { fprintf(stderr, "ERROR: Can't create configuration file.\n"); return; } fprintf(stderr, "Creating configuration file for tomoyo-notifyd ... "); fprintf(fp, "%s", notifyd_data); close_file(fp, !chmod("notifyd.tmp", 0644), "notifyd.tmp", "notifyd.conf"); } int main(int argc, char *argv[]) { int i; const char *dir = NULL; for (i = 1; i < argc; i++) { char *arg = argv[i]; if (*arg == '-' && *(arg + 1) == '-') arg += 2; if (!strncmp(arg, "root=", 5)) { if (chroot(arg + 5) || chdir("/")) { fprintf(stderr, "Can't chroot to '%s'\n", arg + 5); return 1; } } else if (!strncmp(arg, "policy_dir=", 11)) { dir = arg + 11; } else if (!strcmp(arg, "file-only-profile")) { file_only_profile = 1; } else if (!strcmp(arg, "full-profile")) { file_only_profile = 0; } else if (!strncmp(arg, "use_profile=", 12)) { default_profile = atoi(arg + 12); } else if (!strncmp(arg, "use_group=", 10)) { use_group[(unsigned char) atoi(arg + 10)] = 1; } else if (!strncmp(arg, "grant_log=", 10)) { grant_log = arg + 10; } else if (!strncmp(arg, "reject_log=", 11)) { reject_log = arg + 11; } else if (!sscanf(arg, "max_audit_log=%u", &max_audit_log) && !sscanf(arg, "max_learning_entry=%u", &max_learning_entry)) { fprintf(stderr, "Unknown option: '%s'\n", argv[i]); return 1; } } if (!dir) dir = "/etc/tomoyo"; for (i = 0; i < 256; i++) if (use_group[i]) break; if (i == 256) use_group[0] = 1; policy_dir = strdup(dir); memset(path, 0, sizeof(path)); make_policy_dir(); make_exception_policy(); make_domain_policy(); make_manager(); make_profile(); make_stat(); make_editpolicy_conf(); make_auditd_conf(); make_patternize_conf(); make_notifyd_conf(); return 0; } tomoyo-tools/usr_lib_tomoyo/convert-audit-log.c0000644000000000000000000001064214116520000021004 0ustar rootroot/* * convert-audit-log.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include static char buffer[65536]; static char *cond = NULL; static int cond_len = 0; static void realloc_buffer(const int len) { cond = realloc(cond, cond_len + len); if (!cond) exit(1); } static void handle_task_condition(void) { while (fscanf(stdin, "%65534s", buffer) == 1 && strcmp(buffer, "}")) { realloc_buffer(strlen(buffer) + 7); cond_len += sprintf(cond + cond_len, " task.%s", buffer); } } static void handle_path_condition(const char *path) { const int len0 = strlen(path) + 2; while (fscanf(stdin, "%65534s", buffer) == 1 && strcmp(buffer, "}")) { realloc_buffer(len0 + strlen(buffer)); cond_len += sprintf(cond + cond_len, " %s%s", path, buffer); } } static void handle_argv_condition(void) { int i = 0; while (fscanf(stdin, "%65534s", buffer) == 1 && strcmp(buffer, "}") && strcmp(buffer, "...")) { realloc_buffer(strlen(buffer) + 34); cond_len += sprintf(cond + cond_len, " exec.argv[%u]=%s", i++, buffer); } } static void handle_envp_condition(void) { while (fscanf(stdin, "%65534s", buffer) == 1 && strcmp(buffer, "}") && strcmp(buffer, "...")) { /* * We won't check exec.envp["name"]="value" condition if not in * "name=value" format. * But we check "misc env name" permission even if not in * "name=value" format. */ char *cp = strchr(buffer, '='); if (!cp) continue; realloc_buffer(strlen(buffer) + 16); *cp++ = '\0'; cond_len += sprintf(cond + cond_len, " exec.envp[%s\"]=\"%s", buffer, cp); } } static void handle_exec_condition(void) { while (fscanf(stdin, "%65534s", buffer) == 1 && strcmp(buffer, "}")) { if (!strcmp(buffer, "argv[]={")) handle_argv_condition(); else if (!strcmp(buffer, "envp[]={")) handle_envp_condition(); else { realloc_buffer(strlen(buffer) + 7); cond_len += sprintf(cond + cond_len, " exec.%s", buffer); } } } int main(int argc, char *argv[]) { char *namespace = NULL; char *domainname = NULL; memset(buffer, 0, sizeof(buffer)); if (argc > 1) { fprintf(stderr, "Usage: %s < grant_log or reject_log\n", argv[0]); return 0; } while (fscanf(stdin, "%65534s", buffer) == 1) { if (!strcmp(buffer, "task={")) handle_task_condition(); else if (!strcmp(buffer, "path1={")) handle_path_condition("path1."); else if (!strcmp(buffer, "path1.parent={")) handle_path_condition("path1.parent."); else if (!strcmp(buffer, "path2={")) handle_path_condition("path2."); else if (!strcmp(buffer, "path2.parent={")) handle_path_condition("path2.parent."); else if (!strcmp(buffer, "path2.parent={")) handle_path_condition("path2.parent."); else if (!strcmp(buffer, "exec={")) handle_exec_condition(); else if (!strncmp(buffer, "symlink.target=", 15)) { realloc_buffer(strlen(buffer) + 2); cond_len += sprintf(cond + cond_len, " %s", buffer); } else if (buffer[0] == '<' /* ccs_domain_def(buffer) */) { char *cp; free(namespace); namespace = strdup(buffer); if (!namespace) break; if (!fgets(buffer, sizeof(buffer) - 1, stdin) || !strchr(buffer, '\n')) break; free(domainname); domainname = strdup(buffer); if (!domainname) break; if (!fgets(buffer, sizeof(buffer) - 1, stdin)) break; cp = strchr(buffer, '\n'); if (!cp) break; *cp = '\0'; if (!strncmp(buffer, "use_profile ", 12) || !strncmp(buffer, "use_group ", 10)) { cond_len = 0; continue; } printf("%s%s", namespace, domainname); printf("%s", buffer); if (cond_len) { printf("%s", cond); cond_len = 0; } putchar('\n'); } } free(domainname); free(namespace); free(cond); return 0; } tomoyo-tools/usr_lib_tomoyo/tomoyo-editpolicy-agent.c0000644000000000000000000002232114116520000022223 0ustar rootroot/* * tomoyo-editpolicy-agent.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef MS_REC #define MS_REC 16384 #endif #ifndef MS_PRIVATE #define MS_PRIVATE (1<<18) #endif static _Bool wait_data(const int fd) { struct pollfd pfd = { .fd = fd, .events = POLLIN}; poll(&pfd, 1, -1); return 1; } static void show_tasklist(FILE *fp, const _Bool show_all) { int status_fd = open(".process_status", O_RDWR); DIR *dir = opendir("/proc/"); if (status_fd == EOF || !dir) { if (status_fd != EOF) close(status_fd); if (dir) closedir(dir); return; } fputc(0, fp); while (1) { int ret_ignored; FILE *status_fp; pid_t ppid = 1; char *name = NULL; char buffer[1024]; char test[16]; unsigned int pid; struct dirent *dent = readdir(dir); if (!dent) break; if (dent->d_type != DT_DIR || sscanf(dent->d_name, "%u", &pid) != 1 || !pid) continue; memset(buffer, 0, sizeof(buffer)); if (!show_all) { snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/exe", pid); if (readlink(buffer, test, sizeof(test)) <= 0) continue; } snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/status", pid); status_fp = fopen(buffer, "r"); if (status_fp) { while (memset(buffer, 0, sizeof(buffer)) && fgets(buffer, sizeof(buffer) - 1, status_fp)) { if (!strncmp(buffer, "Name:\t", 6)) { char *cp = buffer + 6; memmove(buffer, cp, strlen(cp) + 1); cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; name = strdup(buffer); } if (sscanf(buffer, "PPid: %u", &ppid) == 1) break; } fclose(status_fp); } snprintf(buffer, sizeof(buffer) - 1, "%u\n", pid); ret_ignored = write(status_fd, buffer, strlen(buffer)); memset(buffer, 0, sizeof(buffer)); ret_ignored = read(status_fd, buffer, sizeof(buffer)); if (!buffer[0]) continue; fprintf(fp, "PID=%u PPID=%u NAME=", pid, ppid); if (name) { const char *cp = name; while (1) { unsigned char c = *cp++; if (!c) break; if (c == '\\') { c = *cp++; if (c == '\\') fprintf(fp, "\\\\"); else if (c == 'n') fprintf(fp, "\\012"); else break; } else if (c > ' ' && c <= 126) { fputc(c, fp); } else { fprintf(fp, "\\%c%c%c", (c >> 6) + '0', ((c >> 3) & 7) + '0', (c & 7) + '0'); } } free(name); } else { fprintf(fp, ""); } fputc('\n', fp); ret_ignored = fwrite(buffer, strlen(buffer), 1, fp); while (1) { int len = read(status_fd, buffer, sizeof(buffer)); if (len <= 0) break; ret_ignored = fwrite(buffer, len, 1, fp); } fputc('\n', fp); } fputc(0, fp); closedir(dir); close(status_fd); fflush(fp); } static void handle_audit(const int client) { int ret_ignored; const int fd = open("audit", O_RDONLY); if (fd == EOF) return; /* Return \0 to indicate success. */ ret_ignored = write(client, "", 1); while (wait_data(fd)) { char buffer[4096]; const int len = read(fd, buffer, sizeof(buffer)); if (!len) continue; if (len == EOF || write(client, buffer, len) != len) break; } close(fd); } static void handle_query(const int client) { int ret_ignored; const int fd = open("query", O_RDWR); if (fd == EOF) return; /* Return \0 to indicate success. */ ret_ignored = write(client, "", 1); while (wait_data(client)) { char buffer[4096]; int len = recv(client, buffer, sizeof(buffer), MSG_DONTWAIT); int nonzero_len; if (len <= 0) break; restart: for (nonzero_len = 0 ; nonzero_len < len; nonzero_len++) if (!buffer[nonzero_len]) break; if (nonzero_len) { if (write(fd, buffer, nonzero_len) != nonzero_len) break; } else { while (wait_data(fd)) { char buffer2[4096]; const int len = read(fd, buffer2, sizeof(buffer2)); if (!len) continue; if (len == EOF || write(client, buffer2, len) != len) { shutdown(client, SHUT_RDWR); break; } if (!buffer2[len - 1]) break; } nonzero_len = 1; } len -= nonzero_len; memmove(buffer, buffer + nonzero_len, len); if (len) goto restart; } close(fd); } static _Bool verbose = 0; static void handle_policy(const int client, const char *filename) { int ret_ignored; char *cp = strrchr(filename, '/'); int fd = open(cp ? cp + 1 : filename, O_RDWR); if (fd == EOF) goto out; /* Return \0 to indicate success. */ if (write(client, "", 1) != 1) goto out; if (verbose) { ret_ignored = write(2, "opened ", 7); ret_ignored = write(2, filename, strlen(filename)); ret_ignored = write(2, "\n", 1); } while (wait_data(client)) { char buffer[4096]; int len = recv(client, buffer, sizeof(buffer), MSG_DONTWAIT); int nonzero_len; if (len <= 0) break; restart: for (nonzero_len = 0 ; nonzero_len < len; nonzero_len++) if (!buffer[nonzero_len]) break; if (nonzero_len) { if (write(fd, buffer, nonzero_len) != nonzero_len) break; if (verbose) ret_ignored = write(1, buffer, nonzero_len); } else { while (1) { char buffer2[4096]; const int len = read(fd, buffer2, sizeof(buffer2)); if (len == 0) break; /* Don't send \0 because it is EOF marker. */ if (len < 0 || memchr(buffer2, '\0', len) || write(client, buffer2, len) != len) goto out; } /* Return \0 to indicate EOF. */ if (write(client, "", 1) != 1) goto out; nonzero_len = 1; } len -= nonzero_len; memmove(buffer, buffer + nonzero_len, len); if (len) goto restart; } out: if (verbose) ret_ignored = write(2, "disconnected\n", 13); } static void do_child(const int client) { int i; char buffer[1024]; /* Read filename. */ for (i = 0; i < sizeof(buffer); i++) { if (read(client, buffer + i, 1) != 1) goto out; if (!buffer[i]) break; } if (!memchr(buffer, '\0', sizeof(buffer))) goto out; if (!strcmp(buffer, "proc:query")) handle_query(client); else if (!strcmp(buffer, "proc:audit")) handle_audit(client); else if (!strncmp(buffer, "proc:", 5)) { /* Open /proc/\$/ for reading. */ FILE *fp = fdopen(client, "w"); if (fp) { show_tasklist(fp, !strcmp(buffer + 5, "all_process_status")); fclose(fp); } } else handle_policy(client, buffer); out: close(client); } int main(int argc, char *argv[]) { const int listener = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; socklen_t size = sizeof(addr); char *port; if (access("/sys/kernel/security/tomoyo/", X_OK)) { if (unshare(CLONE_NEWNS) || mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) || mount("none", "/sys/kernel/security/", "securityfs", 0, NULL)) { if (errno != EBUSY) fprintf(stderr, "Please mount securityfs on " "/sys/kernel/security/ .\n"); } } if (chdir("/sys/kernel/security/tomoyo/")) return 1; { int i; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--verbose")) continue; verbose = 1; argc--; for (; i < argc; i++) argv[i] = argv[i + 1]; break; } } if (argc != 2) { usage: fprintf(stderr, "%s listen_address:listen_port\n", argv[0]); return 1; } port = strchr(argv[1], ':'); if (!port) goto usage; *port++ = '\0'; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(argv[1]); addr.sin_port = htons(atoi(port)); if (bind(listener, (struct sockaddr *) &addr, sizeof(addr)) || listen(listener, 5) || getsockname(listener, (struct sockaddr *) &addr, &size)) { close(listener); return 1; } { const unsigned int ip = ntohl(addr.sin_addr.s_addr); printf("Listening at %u.%u.%u.%u:%u\n", (unsigned char) (ip >> 24), (unsigned char) (ip >> 16), (unsigned char) (ip >> 8), (unsigned char) ip, ntohs(addr.sin_port)); fflush(stdout); } close(0); if (!verbose) { close(1); close(2); } signal(SIGCHLD, SIG_IGN); while (1) { socklen_t size = sizeof(addr); const int client = accept(listener, (struct sockaddr *) &addr, &size); if (client == EOF) { if (verbose) fprintf(stderr, "accept() failed\n"); continue; } switch (fork()) { case 0: close(listener); do_child(client); _exit(0); case -1: if (verbose) fprintf(stderr, "fork() failed\n"); close(client); break; default: close(client); } } close(listener); return 1; } tomoyo-tools/usr_lib_tomoyo/audit-exec-param.c0000644000000000000000000000550314116520000020567 0ustar rootroot/* * audit-exec-param.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include int main(int raw_argc, char *raw_argv[]) { int i; int argc; int envc; char *filename; char **argv; char **envp; if (1) { int fd = open("/sys/kernel/security/tomoyo/.execute_handler", 0); close(fd); if (fd == EOF) { fprintf(stderr, "FATAL: I'm not execute_handler.\n"); return 1; } } else { int ret_ignored; char buffer[1024]; int fd = open("/sys/kernel/security/tomoyo/.process_status", O_RDWR); memset(buffer, 0, sizeof(buffer)); snprintf(buffer, sizeof(buffer) - 1, "info %d\n", getpid()); ret_ignored = write(fd, buffer, strlen(buffer)); buffer[0] = '\0'; ret_ignored = read(fd, buffer, sizeof(buffer) - 1); close(fd); if (!strstr(buffer, " execute_handler=yes")) { fprintf(stderr, "FATAL: I'm not execute_handler.\n"); return 1; } } if (raw_argc < 7) return 1; filename = raw_argv[4]; argc = atoi(raw_argv[5]); envc = atoi(raw_argv[6]); if (raw_argc != argc + envc + 7) return 1; for (i = 5; i < argc + 5; i++) raw_argv[i] = raw_argv[i + 2]; raw_argv[argc + 5] = NULL; for (i = argc + 6; i < argc + envc + 6; i++) raw_argv[i] = raw_argv[i + 1]; raw_argv[argc + envc + 6] = NULL; argv = raw_argv + 5; envp = raw_argv + argc + 6; /* * Check parameters passed to execve() request. */ if (1) { openlog(raw_argv[0], LOG_NDELAY, LOG_USER); syslog(LOG_INFO, "Domain = %s\n", raw_argv[1]); syslog(LOG_INFO, "Caller Program = %s\n", raw_argv[2]); syslog(LOG_INFO, "Process Status = %s\n", raw_argv[3]); syslog(LOG_INFO, "Requested Program = %s\n", filename); syslog(LOG_INFO, "argc=%d\n", argc); syslog(LOG_INFO, "envc=%d\n", envc); for (i = 0; i < argc; i++) syslog(LOG_INFO, "argv[%d] = %s\n", i, argv[i]); for (i = 0; i < envc; i++) syslog(LOG_INFO, "envp[%d] = %s\n", i, envp[i]); closelog(); } /* * Continue if filename and argv[] and envp[] are appropriate. */ if (1) execve(filename, argv, envp); return 1; } tomoyo-tools/usr_share_man/0000755000000000000000000000000014116520000015053 5ustar rootroottomoyo-tools/usr_share_man/Makefile0000644000000000000000000000021214116520000016506 0ustar rootrootinclude ../Include.make install: mkdir -p -m 0755 $(INSTALLDIR)$(MAN8) $(INSTALL) -m 0644 man8/* $(INSTALLDIR)$(MAN8) .PHONY: install tomoyo-tools/usr_share_man/man8/0000755000000000000000000000000014116520000015716 5ustar rootroottomoyo-tools/usr_share_man/man8/tomoyo-checkpolicy.8.gz0000644000000000000000000000431214116520000022247 0ustar rootroot‹Î]Y\­kSÛHò»E—s‰l@²eÈa ǘ÷»0°IEÙC–F¶‚¤Q4#;ÞÍñÛ¯{F²eÃ&ù°TA©{úýš,§Ý\òØ•¡çFѦ,a™+™“%Œ¸tté&б:h(pÆiÄ`ß²÷›5Ë©Ó/Œ¥›ønæCš17žDìH¡_ÿC?5Ëg0NEÞ²L™ "u=ÅŒ%°`้!! ¬Ñ `‰¬—s%Õ,Kɺ¬·l&0gÙÝA²¯²fz¿×¬$À_Žó/{ÅĈ©ŸøO°\áßPR4Z’‚à1Ú5s3ד,™¹‰ˆ'0\+Ÿa‚±2 “©°œ†iÂ"Œ"%jÎCžL0²÷.†|WÌöˆp4P„š( î¢^Á¹±@*A>ω÷KÎ%ÛSê‘øj“8 §3¹A© êínIè¡K½Ý]¤è¹i(1#èîÔ…PP|%=²›)ו$9c xÆ`Á)w”;wÈh ½w¥¡=Ø×”{g¦-Éxì)I —3 a¥ÂŠUµ°å zÇ'˜™ÓØùÝÄT 4zÎÜ0-{n83ôSÃfg·øÞuD+§XB KÈùË©¡m(ÓT,˜Ž4ÔÖ[ÃIÖù냼ù¢aǯ;øÖšMÉíäFõÛ¤<…è?»²¤7û[qíï‹;ÔÒVâ:UqȆeR¯¯ « Ã^… ‚ŒIæü¯f±h;ßœ‹oÕ   iUÑÝ]U‘a”ŠÙÑž›2˜Á²ì¥2 ±k¨Ñuo@ñ¦”_CÀ  ÇžTM…ÙŽ-•! ÒTÅ¥ûíp¿É  ¬±R<¨á 2œ‚ZkWæ5$OöpªÔÅ@D“|öX‚æ oîÁŒ¹xJà˜@‘Oótï#jŒ(t/&`6uC*JT©$v³{4FUñûãŠÊhxŠÁàñ<ØÊKž“™3û›#͸DŽ Ï”(žË4—D…ÔQ@"Ô\Š™›`Xƒ<‚û +#xV©rƒ÷úSÆ0 ûŽh":Òq’玬#Щ«±G4IÏ¡­¿10gh€ª™G-}Rä¡ëyè°rÔ@ ‹8½iØÚ­§F¾Óií7i·°9‘­=¸.eˆÝ1w +ˆg¿õÒ(eñRÉŠÓŦ-5¸H} Š‘©³.ƒnyÖ]õWãñ,+¯!êøùªÏçê~|\¾;¯ªÅHvæŽOVWƒ²ð£SnÄØhèÖÁfdË)ŒvE|·²àQN¶â­@#o/“ªÜ£4Ó;¾ÚŠN:ûP·'ö+pjÕárlõ}B¬îê+JU‘õ4]£UèÙr…¦ àÆ$]·ñµÂ\ŒÖÊÓZ1e¦¯›7‹i8ãDÆ!§vï~-ÑÅ:x×´‘@ýzx9ü04{çýÞo£áÅ ÷‘àúž:z§m¿2Û³ý×B|ò%7%ç‘Àgà/V‘ã%®K1tý/kÜÏÔ£ndˆ;î-úZ?+o”=µT¤Ï¹a€ï7bÀÐt£…»ëóÙ2Å÷œ:ý$Ü î™PÒ׎˜d 1*¥µñ3o–¨¡Ï½<¦‰g•>+Ÿq¸öŽÏ¡þ®{ٯ״CŽé͘wŸò(ô–à˜P|ÍÝ(ô]IÏåÍR•£Ž\„IþUËx7cèsµìU±Î 'x[Ä®¢É ®à£ xJ."ôI‹;í{WƒÑõ`ønSâÆÁõ T\ð¦™c{¸T¶øtÇ Ö£©ì«Ç”ð§ö$Q¾Ï÷A½n*³ôÀ57 nðÉ$I‰ŸÓÎ]ŠÀöcO)¶´b²Däºj¯µL±S¦`·´‹CåÄVÀ*ÈÔ1d>ƤŠh@5V÷ëµA‚Ë9¾–¥6Ü-¿­;{ÄΞdߎRacÿ}÷rtÑß2²ŠÕjzºJä¨7,zdÆ9Pøíl,ܵŸ(QޡŤ×Ò‡-ÍüßÂ|Óÿ+nÙw-Üvú§ŒÜfú‘ì'í¤p¿½ù÷V¨5fTWøðÚ}’O>¡˜¡Àˆêð¸ÔO{½pL–¼‰p0‹ ?±bvRdµ{s}>¼ÚÒTAbÄ®™9‡sê8NY2ÍÃÄ1ï>£7ÇŒøœYãîo7W]+aÖçôd3tO Xñ?Å^»¤ä»¹œñ¬(àÿ¸1¾ÀÞMó%Kàø3Ao´MŒž•ßo)þIžÚi1õ¨¦þ_°‰À7h¤q¿Ý‹ñpk ­°ÕÆüP®J¨qˆ7ñhT3LýLÊTµŠäëD ³ª¦búWO˜Ðc]ÿÚÿlÊÿ~tomoyo-tools/usr_share_man/man8/tomoyo-selectpolicy.8.gz0000644000000000000000000000445314116520000022457 0ustar rootroot‹Ï]Y\­X{SÛHÿߟ¢Ë¹D6 ùÙ",KÅ1æð’T´{ÈÒØVÐ+ Çw9>ûýzF²e`³{UK”z¦ß¯éƲëÔ˳8t2ßu‚`I3‰ÔÉ„G“%bïàà܉¨ku»ÔPàØ“@ЮÕÙmÖ,»Î¿4ΜÈsR’T8á$êø—¿è§fy‚Æ åH•ª$ÇÔXÌED A®åR5A1JY2!ëõ½‚"†j–¥xÝL˜×;1ó#ºé懔‰oYÍšfÔÿP³¢)~Ùöß:+"ÁDƒÈ{†ä}…ÈÞÐ$!‡Ðk›‰”²Ô‰d²8’w±¯<1õ#øZf©ͤEdo5L“~(V3ÿ^™òhÏÞ9p-yŽœï0âh¨5R⫳³zåÌ¡@L3ÅÈ‹s¦ýšÇ™ØQâ|¹‰œú³y¶©êo?Vˆ"ß…Iýím`ôÄϘ;sÈ—²œžè-”éŠS6©˜Æ© E̱›€ï½ãŒ[Ƚ-í$¾% €í­A[”ÆÓéŽâÅÙîãÓLت\XøÙœú‡GˆK–’ÝØú`"”ŠSß¾7L«soØsÃì$†-Íîvñ½mË6®+·H!²ÿc× x LÃâáH| !ßvÔ°NÙË›¯ð—.>ÈZ“)¾Ýܨ~›'öÇÔiƒWæÎ—]ûÇìö5·»n•È&õú ºÜ€àö*d0dLRû¿5K=ðÝnˆÐþ^utHª‚no«‚ £dVôŽtDDË´“ÊÀGÕp¡ëÚ i‡4ãø’®‘€1jR¢Z*B줙òKï+ôp¾²Ê¤ŠSc%x8å¤JÑ$—(r7ËS.È8ÚAW1¸6І•<ñDu `A1O¤)çšÎg? pѰ®N›;4n3(󉮮}qóB†IS¤ X•BB'½ƒ2*‹?±WFÇpæÅ”Ü8O%Jyç¬æÜA]¢8’4v…”l™bçY’gŒì`Ê,T_ …Á­Ó< )ꊕ<©d¹ áGý™…4dý¸#Ú™mG/í¬ [Wmq¢”^R[Ã1'P@å̆–¾)âÐs]¬Œ%ÕýÂOo/š¡´×¥Žõšö÷[ín«½OãóáÏ:®ûïhÏê6uß=N 1—y„¿cvHi6à÷1w„ÔÄï=š—êH‰“f²PôÓÜ› ¸ÃÍbDŽ{ˆj2*8ª±Iµ^œ–3pCÖ~¸OÈÚ­€ŸÉžvÖà¯GÚêµzĶÑèäfÃÆ’zš¿ìæÍæ^ݰ¹!ï§ yí i¯6„½Ò² c¥~Çåü?k­Qåy[~«;UàVß­ß1|í»n\ eî"ßÚouÚ&Zÿ‹Ó¦a£ ×¿ãæ ¯WÅÿæWšÆ¨jù˜¦Ónu:%ÑoÏÐì.‰;P¹´<tB£ê~{+i5YAÜÊíÎãCGÛ å4Àqhä[ÝÖn“g‹NÌhk ®Jr»ËÄÝB ¦Ùm½6J^q)dEé hK °÷T2 u×Ô+ïz«»Á*aÜ8MËgˆ+þ~Uç÷ê}|š¾[oªÉÈzæ¶ÇZW²bðG¦ÌÚ Qh0koÓ³e†^A¼À«,ã g]ñ*pËÆ‹à¦™J÷ IõŒ¯¦¢£î.•ÀÍQç Ùµjs9 ±úÞ')W@LÎꥲ¨ƒ|š­UŽÇóåê˜3 6&ɺŒ¯ÔÉÙh}ÂqZ æÈ tñ¦!7gtd4ytptíþ_¼-ñÃ:üHW<‘PýêâüâÓ…9œ úW£‹³aÿíãꔞ½¢z·Ýyc¶»fû5Cl‡ñ26³8$ÁŸ¬6ÇK L!õ¼Ï5&4µÖÐuæcjÃä¢ö“òMÙQcqSú’ËÌŸbƒc8§,œ¥\ßÏ— 6:uû3F$LwB*nÀÀã$ZR¡|£f~áÎ#µz±›‡Üó¬rås+Xä0øŽO©þ¾w>¨×´A6&µÄï.É6©øÒÇlN®ÌYªŒÔž¢3?Ê¿ifãOï/FãáXûúTÍ{ÕSû•=}W8¯*Êž^Òg{:ŒU‡yzØÂý舯-žXÆñ`Ü¿Ž®†ï7Ål\\Í1¸bð›¥Nˆ²q8³Ò$5+ÉrG÷#L„zäÔÓ¡ÜÀã™X&ÂE”0{Œ0Y®è5Q¡á…Òá‘*‡#ªÃ ¶™Â®:í)´!§NÝL뵑ïÞñªìA!IÒbcs+äFfUHÆœ¦ÇàMíôn`•rªNÜV^°³DAF!r‘×NµsÀ®”„ÚEÔ–éÜǾÇyæGX °«g:Ç'K­Ã\îØ䣃íJ-·XT1@–"ò((¦qĸÑÿ ¶Zöï´j¼y¬¬*<;øØ; ¹¶zª}[úðQÈa”sOoðžÛ&xTÇïüÿ, þfB$9Vƒçë)å®÷õðN`£ ލ•Ë´%'~Ôb¦êŽ©%—²¥QZØò×Ò[ZiŽ(´cÞ]ÿý‘Sô ö9Ìû„‚U^Ò$ŸIl^B$À+àÀ–úò: mSDotiÅÒ‹¬Pþï]_^\>’T9„÷¯D&ó˜N¹0è0Ñ,÷#ÛÔæ¼Úfß kÜûçõeÏŠ„õ%9Útü³ VôÏ‘×Î9BNžÍã´Èû8!·÷³|)":üÂÐ[mcÀÝÊrc+¿{$øOÒÔŽ‹ŽªsŸa!&«ká¤ñ`@½³ñÅ£&¸:Ýh‚qºn}<à£Qm,γ,‘­"î:0Ve`Èÿ!ò#Þñõ›QûíX_uµtomoyo-tools/usr_share_man/man8/tomoyo-loadpolicy.8.gz0000644000000000000000000000504214116520000022112 0ustar rootroot‹Ï]Y\­YûsÚHþ¿¢K¹D`°“rˆã Æöš;;PÆv’Šv7ƒ4ÅzE#¹Ëùo¿îâáGêâªliýõ×=Ý==¬aiÐÊÒ(`©g3ߟ‡šMå, ¬Œ…‰—Ž }pˆç’&`•·>Uñ(‡¶5Ö«†9Ö­‘^5cÝÕÆvþ½m‰:.V1„8†õ«„Ü)Ì'ð8bO0ÞÊVX6ÎÞïe•We3xßÀ0b·‘éÅï*“‡öG`Ö+µGÂÕ‡ÛWhs¸FÅ0L4m>º\¡Û‹#Fú ±þ[2¸¿êŸV™ÖÏ¢C\Tôí[Q‘®ÏÀòÚq"lsx‚³°t”¾‡YC‰®rÜ$ `Hç« ¸ÆŒ0'eRái†«Ïw\ŠA8…뀠ÅØM³„2 w°ªè”yADJ¿"$…HÌáIB±¦âÙK}\(Wg•q†«4ìÓPdÁm•û8ÕÇ)4/ A'®¨„” $ÔLIÀ’[$#£øóÁ!y¥×=Fgv]°£,˜ÊÓ(#š#†y‰É'‘Í… Ë$T”¥q–Ò.Üí»!ëRÀYˆnu3\ÌK$6óài!Ê9t>«Ï4€ñkRE´RË _Z©†ƒ†&Ëí x uõŽ9E2fÖ µ’ŸC˶Ñ`i,È‚èå~úP~Q „ÁlLã5ìï×êZ}úw*®ÛG°g4*ªîžr– šË,ÄÿöÉ!3³qü1¢ŠTñß‹—¬H1KR‘“Gy7s†Ýa§žÕYdäáÈÂ’_$Åxq6³˜7`ì‹á)»…áW°\s1ü‡=å y[­À–ËfV-[ø‡Aíf/Y¥²…·nPYÒ÷fI_}IÛ«%e¯”®ÜX¡îq&ÿ\kõ"æ·âà¯â`§8¸‡ûùwís׺mRá*Ks'ÙÖ~ͬW±ô¿8«è–aí'®43­¨þQ™o¸¤dô"ËU³^3Í™Ð_dv6ë©è;6߯mVÐxÓý~ƒDí 3þm•…_”+œa:?°²Ã<1­b7ÄýÊì ÕÑÊ ¯ù˜–m$·M_§¨oEú¼AÍî7ù’n,Hà&µGbï®H º~¤Däj´¦yÛšàF‡ë¹o*µ†„¬»¡ÄùJF;œn`šY¨Åµ?%¨S-ꌩs¢ßë9Pãu [nÃ*¦˜c$g–™WáxÆ …g¦H·£,õf ÝïPmE“âªØ6;Ø>˜Ê¥§Œˆ=ÛjÔv+Ô[˜m[Xp5ÃÛ nä,Hf·öZŸaE3%sI†I;ÓÀp÷ž F.×ZZ³µÖ|íd0v”$³kˆ2~<Ïó±¼×Ãwëm1‰gvo9ĺè”9ÀS &ÍÚ 0ÑЬ½eÏΪ0òò£ ÞÊ"ò3⊷•l¼ì$•áîljêñeWtØØ…ÙàæÐ| V©X\šÀçßû Ä|›cDÉ(21ž†‹i#ùôh:Ÿ¦ˆôA¼Hã+9sÞ[ÌÐ9-ÓÉœ¨äM*ÎX‘±ÈcǪÝþͯ%ºX;ŸáŠ:ЮºÝ/Ýêy·uÜëžwÚ_`ׯÎ`Ãhºù¶ZoT믱)Ä—a4ªiùoŒ:Nö§Ø,Ðr¼ª±;“O¸N=ìØ°kQ—úéì>Ù‘-PAúž‰ÔsñõF蘖?aS±XMc|ÍÉÕwØaGpË…DÃ}ØÜD8N!@¥´"û}nBùt"; ¨Þ³çÃ8ÁG6½ý3Ð>¶.N´’2ȪúsâÈ÷ì)XU Qî 8÷ÂìŽeôV²ý/»½~§¯Üz&[»â¬õÊrr_--÷¾Zn'ŠÉ"aþözÏÞ,‡ °þÛ‹›ùW%©‚"bÇ'ýöe§wÕé~\æ¶´p5ÂÆÃaÂL+†áHIŒOܿܲ±³½Rû(s."A</Ä>“:ç[޽²o(*]©lÅE…Éhh·UåÈ^ƒ=¹­Cq¤U¹VjÅ1W¯;4º&¦¢¦ÀkØ5g‰—NkÊc5~gs颿ç~3ŠèîxW+uñÉ>Aþâ;ëðÎ/°w¢€yAo îü õÇÀƒuìàxc:°!OV@ãuÐø@1"]Ïç+ bTü(nZD|0ƒ–ulÜ¥•>I¿Ó³JåÖ9jUÇ£hÐ=ø†™0,áX  ý¹VB Üëô(ï˜ã$ôX+ÒTä)£N>·.zç'+)UœU>Ë}ÃðÅJ²ó†y,,[û¤‚ß ÀÄZ€6ÙþßøøÎb¨š¬#òµÙ¬o7Òà'l¬¯x Þð9ÝK´Tý6EÊeµy’ð3dÖ);Üç)½p~ó5·©Œ{¶——¶ù–sÆ*¨µwù„äËï0ÆÑšxažÇTbì¬ø:öégRÌ‘ãÓŸÊ*Î'˜?O¬hÁf..@§öÆ eØýȺîßEº©¤ðtíúΧ9ù¶a˜oö Ó0›fÿVHÖ$, |$ qVÓžQP5Yórë¸ÿ8d¹}ô¬}ôœôy¹‡2ª–‰DãÌÅ?2žLÓ(P^£¢vtýÇJAS3—~¬Á¥ŒÁ&o Å™@!p<ŸŽû`†O¿åLÐðƒí 0"á„FÀóÚÙº¾:ë^®h*L¢›¯x*²ΨÑŒ§aæ…VUùäC‡Ls£ßú×õe˹ñ=>\öêF€¹ü&ñÒeËÒQ”ä7Ô?Y€Áôq˜MyßiôAÙèSS†1gd·+ŠŸ)S:Î[bÕ S7á7Zî¤þÉ ´ÎûÝ•¶v>[ìTóyµ(ïWv`±Æ/}z­*¯ÈõTÐÔ¬lûœÃÁ(McѬåñ¥(߆ý/ªÚI ¥ÿÝêöÆtomoyo-tools/usr_share_man/man8/tomoyo-editpolicy-agent.8.gz0000644000000000000000000000461714116520000023223 0ustar rootroot‹Î]Y\­XmsÓHþž_Ñed“X¶œ@…lHag㻼¸âÀB­vÙÛ"’FhF ¾ãøí÷ôŒlË&°|ØT%¥éž~™î¸~º…–I £qÇsšŠTä!æ4áÁÁERÇít¨nŽÃ(ÉbA»®·ÛØrýÿÒPiä!e¹’Q, øåßô³å†‚†å[‘UIeÁXPý~&Rº4RGS¡¹ƒ‹&¤ÉU¹ÏîÌ)åÓ–ë^oGÌ뵘F)݉|óÒâ³Þr'šŽÛrÓ ~ùþ?¼%‘`¢^>@r¿‘¹ÈÞÐ$#%è5 ò`¬EN:RƒL¦Šà.öU(&Q _+GéT¹DþÓz³I÷QVÓèNà2éž½ àZ 5Ûዃ¾¹h/e‘×*°€b1цQ( ¦ýTH-vŒx\¾^¿œGÓ™^»i:ÞÞTˆÒh “Ž··qã8È"ˆÀÜi@‘â(„¤%8}£·0¦Nz&r1‘¹ {ɱï]Å|·”ûa¡è±Câsưýà–ær2Ù1œR©gpCµØš\¸ôŒŽ“_ú[¡TPœŽý;§ézwŽ?sš^æøªÙÙ.¿·}Õº‚E ¤ÿ_ ºÂT2,GÙò­î§u÷ìå^ÑxR÷’—|»"3|;…Sýnrœ"Ø/Ékƒ—Ͼˮýcvû–Û’]§ÊdH“Zmyº^;ÁíÕ“Ã'g”ûÿÛrE¼é/~]$þ—ª CVôáCUã,˜•½£§ÆA&H!‚‹´SÊ8BÕp¡ÛÚ I.šr|Eo€5iŠ ÑN\!vÒÔø¥û zŸXe²?%ÔY îO8é”rôÅ%ŠÜÕEÎ)Ót‡k£lˆP)ŸI¤P ÊŠ…"Ï9×l>G:¢îÞœ5vh&`ù8ä£*FJŒmí4æ%|èg [F€aµ’ù-”1Yüîðˆ½2¸:3¯&4–E®PÊsY°š³u‰âÈr9J±e†•,tVh¾…Ûñ„Y˜¾”ˆ …['ELÔ%[xð´’å‚úïì§N¨ÏúpGôµï§}]áS3mï¤9=¦¶ý†cN¡€É™oºSÆ¡;Ã`c,™†•~zUÔH”ŒÇä¹Ïh¿Õî´Úû4¼èÿbÓáÍñkÚs; ÛwOECÌu‘âï²0çKÉ!oâ÷ÍËt¤,ȵ*•ý¤§îk‰Èq1MÆÇ4–ò!©ÖÀ£³…Å|xKî~²:ž’»[9þNþÄ[ÿÀq`=a^« ¶õºW4ë>~Ô“âq§h4žâÕMkòž¯Ék¯I{²&쉕U«ì;çÿ¬µN•ç‡êáÏêa§zøJ_—ß­ï˜ ¾þí7®º1÷¾xºßòÚM´þGg ÇG®}æ ¨UÅÿæP–Æ©j¹Iãµ[ž· úóš‡å4œ.ýæ²e—&i8_ hý…ÿñë*®ÒUb¨—«‡A¤æMLC"n,iCk¼ƒIÙ†rÛüu yxùó-$»¸ïvJ”ã®”À%{ÇðÞ'` #~~F,Äž V~#yÛ¿ÇÅP8¥o­ŽaÙtwSÃçw6:ü3d%¸? Ó°Y•™…Fæ½óÕ)už%Ž?éøõÙ5ÆŒ d]³“&,tñÉ‚ØóãvÐò à%NÕý!÷V²j²‚Àªm¯ñÁ³6X9upì;ÅÓNk·Á³…'ùÚÊ‚›µÝaâN©Óì¶ž9 ^r!dI hÜÞ3É( ®+¨»Àu—¸Þ2aÆ2ÏÏWüݲÎïÌûømú>}QMFÖ³øê‡¬uÕ)KU`Ƭ½…³öÖ=»èÂÐ+–÷x••Œ Ö¯·l¼ã\›t³ÜÎøf*:êìÒâðöÈ{AþVµ¹X~ï“R˃¤`ùŒ2Yä!Ÿ¦+°É‘<›/ÁœÒe«2¾1óÁ ÂqZ æÈôlñæ 7gtd4ytptíã¿y[⇵ÿŽnx"¡ÚÍÕÅÕû«fï¤3¸:ï¿oví]ÞÐ>nÝœÑwÑTë´½Ív§Ù~†[¢œË¦–2VXŸ»m‡s N uÃÏ6&5³ÞÐazÃcøÓÅÛ²cÆ+âæô±P:š`“c8©ßsµÂÏæ6;ƒý£¦ƒ[¡ 7Üà#Iç”@(cÌì/ƳÔ송 ÷>w±úÈ,t€‡gT»ì^ôj[Ö ¿)ÂHg2ŽÆs¿Lyñ›d?8s‘`$ÅjÌ÷xͰ£ó(->“%´l‡ï/¯ÃþÐzÿÌL€U¨ÿÄŸ¼.ݸjeú“kŒý˜ÑôßQvP~e2gœpÒ_÷7ý«Ëukˆ›æXÌÓˆP©Ddˆ¼ÀO…È硚-n‰ Hf¢3lõ5Y[ÍP͹ÊĹɗg€mŒ‚0Ìy® ôÆvet"y¤¼`.iÌ”˜é6Ec¹Ÿf ¦»g(R— D£ë°·d:OdaÖXŒ˜©ÙSe a‹‚ƒ£ €*©.“˜]‰œ—Ž„BES^(Ø÷Ê‹²ó<··" Ÿ`}¶=Y³jœêœòa4A±f¬UÂxÄ& {Åæ QÔü;ħK¥'x6ž0ÃôâÒêVË ¾†>…Ai½÷Úîâkž#uF¼ùÜIì!f× …[Ù˜4W·ãZ·ËSܦS䑞·lÀ[¨T$`n#ÎyÜ{×½œ÷6 ¥ P snò‚Í÷^t\ïù¾ë¹§;2~À´F{†OŸ;Qí'h àíˆ<Ô#öšï6 ù!•°ú¿~óë†î‚’]„&$îN£bª°û s$‘QÌŠ.„šÀAvúŠ+@¹R…©›ˆ£ÒMÝ77gW×’*@8éFhUH:ã¿Ña&Òi¥~ÓâUßG1ß wØýכ뮛 ÷cv´î®,é"ߺrÎ}™»V‹ VÇËi1‡ç?ò镵1æuÇÒ-n7ÿ$ ªÚör[cÜIîÅHay.4ìõ¨{>¼ÚhºKèÃMiSßo|·ëmàÖßnÙû6àËö·_v?ÀMá … Ã™Ö™:h•cÎ6•›p]F)7ûZnýÈð~‡·tomoyo-tools/usr_share_man/man8/tomoyo-pstree.8.gz0000644000000000000000000000440714116520000021261 0ustar rootroot‹Ï]Y\­XmSÛHþî_Ñå\"° I–¥âð²ø.6[Qö­‘­ idÇw9~û==’lH²–*(uO¿¿M–Ó¤^žÉÈÍ‚±† šˆX¤n&<-h ½ýýs7¦®ÕíRKƒÃ JBA;–½ÓnXN“i˜¹±ç¦%©p£Q(ö5ú׿é§ay‚† AäHµ©¤w,¨5ŸŠ˜æ‚Ænld”+AÖ`ß2²TBÖË™†b†–¥eÝŒXÖ[1 bš‰t÷#ÊÄ׬aùýÞ°b¿‚çö’I0ÓIì=Ár‰¿&ähX’’욺©;ÎDJYêÆ*›Œ!\+OøAŒX«, ≲ˆœ–iÒ<C-j̈)Gˆì‹Ð’çªéúš° J{׬á\ …ŸiAžÌ™÷K.3±¥Õƒør8 &Ól²0èhó¡Ac¸t´¹ Š#7 2dîN\ gÁ£LBÒ#»…v]Kʦ"¾LÍ%çn¹37™¶Ô{[zdøš0±·!mq*}KKŠe6Eø›i,A¬®…yMéèàyÉRrZ¿›H¥‚átäÌ Ó²g†35L;1ev7ËïMGup\;E ”ó_§Ûˆ i*–¤# õÖrâ–uöënÞ~Ѳ£_»ø kŦåvs£þmržø/Éî@V6ž~W\çÇâö iKqݺ8°¡LšÍ%t¹!ìuÈ`È¥Îÿ–Fà›Ó‘ó­ØÔÝÞÖF%¬œ'jì&‚2X•âT†º†½è òSÑ„ók(ºFJô¤n*d;²t†8H—ÞØá~a“©ø)±ÆRqßç¤SJ1·(j7ËSnHoaªÜå@„IžøJ"†90PÆ0ÌiʵVÔs…8hYWgí-š § TùH‰qÑû@ ‚{ý¤]4¤V EUJ"7½ƒ1ºŠ?rTÇæ…Oc™§ ­¼9›9uÑ—hŽ$•c¡{¦EÉÛ·ÏÑÉ'~îdMݦ{L§ôœ:Å7s tÍ<h'ezã1ÖÎ’ˆA§7­gíHYîxL¶õ’öö¶;ÝíÎ Ïû¿åp}ô–v­n»˜»§ÂM¡æ2ñwÈ©Üü^òDHMüÎ0¼ôDJÜ4S¥ñà÷so"Žq&‘9ž!zÈèäèÁR^$õxvVyÌÀ Y{Ñ <%k§~$Ç·Wà'€ƒ"ú¶z ¶Õ²s³åàEíçÏ»y»½[7j¯é{µ¦¯³¦íÅš²…®ÒYUÜã®þ_õ֨˼­Ö­:pO÷Ëïíï¸ ¹ÎÝ>®–vwžoìmÛ£ÿÙYÛp0†›ßp²Ÿ7ëêÈs‹£‚Ǩ[ùÇîlÛvÅôç<[Oëiß?".Dã¦‰ÚÆýÛ?qã?NK…u¾Z³eÂZž¨…‰mH„í*‘Ejõ…·ÿ#-›0n“¿N¡·"Þ@³z«[ÖÊ4ZöáD âëgÄJ HŸÊGš79=a”±iowµHÓÚ‰µœì´'øfÌJ-Î>i¡žY×™xZçܸ7JAÝ—‘áø]§5] ÇXƬ[vlÒqe˜+fÛŽtØÁË;€õð{<[©0“ ĩڴûXì‡BO ûF¾ÑÝÞiónaK&[ypUÉP›]fî–V0ÏÎöK£’%+%KNM[ipA½«‹Q賞 ^uÖ[ž, f,Ó´º†¸ãgË>Ÿéûñqùn¼®#Û™ß;[]ÊRÀÏL»µ¡ÑàÖîzd«) »B9Ç­¬d˜³­¸xdãF§™.÷0I‹_oE‡Ýª€›Cû59úpÙ'±üÞ#¥–€$wùŠÒUd£ž&+´®‘=],Ñ\Ò%«6¾Ò˜wƒ†ó´RÌ™9)š7x8c"cÈc‚cjýͯ%¾XûèŠ7j^]œ_üqa†W—''´‡³«3z€¤f·c¿6;]³óË ^„r!ÍLÊPáñ÷Êê9\`IЍçE¸¢±•é§ ]g65l+Åe~ZÝ#[z•"DŸs•>^mÌ€€ô¹»P«óé"Á+NŸþ‚µ›ÀPZè°ÔH`âEPÊ'zÏãi¬ßžçÏ9«z湨<Þ°ìϨù¾w~Òl9f»¶sµ(1< Äû_®}Yè,Dï‚8ÿZHþñþb0ì‹àžé¯Žu^8þÛ2r…Ç¿¤Žß— ûèS!éødxtÙ\õ/Þ¯ [;¸šb…™“Ôx5Å×QÊ[-5´lì#ìxj*çzñCL"—ŸHn$ª§¦ôÔBœG#ÞJ%öÇ£ ÎðäOó˜WÁ*"‚#ÉV^h;¸[C¨ ;Ó…MÚÕd}®¦é6CXC.6Ó¥X¬¼ñ8Ì=Öu'°f‡0¯CÒú©ˆ°ÿÿ;Hö˯D¦Ù#ùOR•*—êxEÇ›V*jw¸üšv'¼pÎ]Ô-lq3b~Z©/•²ÝýÞƒäz^ÊS·De°N>ôÎïND«Ž-ÂU·‘Ê ®;÷ˆÌ7#²Q_XÃ×ê¢&y.׫šË¸ô?‘a0^<¡ë§<ß×Nöë®e¿Ú³lËÞ·;ø)ìáp¼½þíA( Þ\ØÉ K¾GÜÓ£|‚ºPBƒ$P¶!×÷A¥‰Wvå˜"~¢û•%•[‘8,£Þ»¾:»¸| ©†Dd®D¦rIgü*:HD<ɃØ1‹|ÓwÌP΄5ìýëú²gÅÂúœ®ÇèIKþ§ØçÜ}nžMeZ–ö?Ý«÷“|!b:øÌЛÂÇŒ5–V~÷@ñ_äi—°˜ÇÜîs1Rx^–AbÀ÷Þ /Ì­%¶>·”ÈÊYÁ“e“eu&¼ +jÂÔ TP4¬Á 1D5L³,QûÛÛy‘*„C—TÄÿç b~©·@ãÿ×»ji{tomoyo-tools/usr_share_man/man8/tomoyo-init.8.gz0000644000000000000000000000465414116520000020726 0ustar rootroot‹Ï]Y\­XmsÓHþî_Ñgn‘M"ù%°•ÍB Ç$ßqÅ…Zí‚llI#4£ßqüö{zFvä$¼\Õ¦* ™é~ú½§'žß¤A©eèx$ÉŠæ"E EHÓexpð"ȨïõûÔ2ËIœæ‰ =¯·×nx~“i¢ƒ, ŠòBé4fûÉ_ôÓðBA“œùZFURy0ÔZ.DFKA³ s4•J7C±8"MžÊÉ{teV¯žg°^OëHÌ㌮D1…ù)iñI7¼HÓð·†—Eøäûïm˜3gá,çø76„ì MrR2…^‹ fZ¤‹ S Ød¦îb_…"Š3øZé"ÎæÊ#ò´\—–q’¨y|%@Le6…g/¸–Â@-v™p<2„–(ÍÞófm/ DDÚ…²dÞ¥Ôb׈ñù6qÏz‹Ò*4ܹ©eñ & wv@1 òX#"0wP¬8 !i ¤[z cºAÒ QˆH‚–’c7îU'L[É}¿Vtèø”ó°ïBزBFÑ®Aʤ^À}¼«Í.ÖäÂ2Ö >>D\tA~ëÁo.B© 8 ý+ÇõzWŽ¿pÜ^îøÊíïTß;¾êâ¸vŠH!ÿ?~ºaª« „#í ùÖò³–wúäaÙ¾ßê¥Oúø ïšÍàöK§þírœbØ/©×–ž-¾ ×ý6ܾEÛÀõëp`Cš4››ÕùÖ n¯¯^9ÓÂÿoÃÉM|ö["õ?×òº ÷ïë‚g VõŽc5 rA \§âP&1ª† ÝÖE…LiÎñu½BJÔ¤)*D;õL„ØIsã—ÁGè|d•ÉþT»ÎFð(â¤*З(rW—¤ÌvÑU®ª!B¥P|"‘A((3(Š¢à\³ùë-ïâ´½K à”—^ªrªÄÌÖ>¶&Ø‚y)/FyÛ¤` ÖBÒ ¸„2&‹ß<>d¯ŒÏžÁ™gÍdY(”òJ–¬æ"@]¢8òB΄Rl™’¥ÎKÍT N"†0})A·FeBꊭ=xRËrA£7öS§4bý¸#úÚ÷³Ÿ|ÝÄ¢ß4mi²‚~¢®ý†cN €É™[€ž=©â0˜Í`°1–LCŒ+?=mÝk§Ê f3êyh¿Óíwºû4y1úզëá=ôúmÛwODP@Ìy™áß ;dm6Ö/%w„ÂÅïš—éHyPhU)þ¨ çî˜i‰Èq1MÆÇ4–ê"©×À½ÓµÅ¼xMÞ~z½ø¥žŒ¬gùÅYëºS6ß+0cÖÃ…³n{vÝ…¡W"—¸••LJÖ··lܳB›tOòÂÎøf*:ìïÑzñú°÷ ùzs9 ±ùÞ'¥6 IÁæe²¨‡|š_o›©¶«Í6g€t¦ùu_˜çãëŽÓµ`ŽÌ±-Þ"åæŒŽŒ&Ž®=ü‹_K|±ŽÞÐO$Ô¼8{qöö̽]Ð>N.Nik‹šýnï·Ûw»0â5(WÒÕR& ¿Ÿ½.6'+ H) Â×3&2óŒ¡W:Æ”†IÅ^ä'ë;d׌QÄMèC©táÅÆ pÆ Y+u}¾XåxÁ™Ó_1a ¸Ê ÄN¶¢BùÄÌøb¶ÈÌ0”³2åç­Ÿxr7 º“Sj¾¼8n6¬A¾Ëƒù.’ TÖô<ÎÊO”Ë$ž­(¨¿„-Âäí˳ñd4±=5C]}׿ïGG•ÇßÎ-ã³ãÉð|4¾½ÜæÝ:ø_¯~4ê¨iœu* –<—ìšéôR`M¶U#þ—“ó*æ½y¤ÞêÆ†(æ!T/mG©€…/û"‹Õ%xr™yôc ߎ"xþE%/©,Vpëxܸ¨ÉAÉc#ƒ’LP§üð2B ±‡§un!ê=ëX×tħ™È9Æï¬«½™Ì"zrHµR+³ƒ¹,b½úPo‡ðJü¡nqÜ KÙž«hï‚AÚsQüLE{ rAÿÂûÂDOΪκr©´¿NU<ªQWö¯ˆ±øLÍ“±É½ŒëÑü} H”ä÷¹%¡G&'Ž?þ C3±£ ¡(f’„äR}i“0ÏÿÁCÛ´.s›Toei±ñxÇ`^š¼Î„MÓ8»’—b+Á¹ "ÏÖ×ñ›Á‹ñóã…YÛ5ïuo4¦&*t½Ì˜+Ù1GÍæ·]Ódk§w&3·ÐÓZG£f3DqX~Q°Â¨ºÜÚ¤‡æÙs·ôÖðè{ùŠìáKþŽ\xcÄ­ºY—z[ ‘g[âmY,æ.MSÞ{&Iïý­Ã=kŠÇ£Y£)›^D~7]½Ö;ƒõ£™O€%º¡{nåƒÍæ9§ÁÑ«ÜH»ƒÀÛ ™Í÷Ç´œ£¡*a–$жíñÚûl¬ò]‘=M¸"<©ÂÌKÅa•mƒW§gç7$Õ6ç ¡U)é”ÿJ‘óó2Î|×ðtä» ŠÂ› þõê|àeÂûnÇëN€ ÿ]ìèd|5,dáY-þ¤Hü—ór…ò}üWO­ ßtè$^yyCðò4žU·­½û¹g,ÅTÅZTNšÓàùäìÆ]¹Ù­ß•|ålò¶µß6=a"=^h«ƒNikÆÂ”ÿZgü÷;O4þVíÁtomoyo-tools/usr_share_man/man8/tomoyo_init_policy.8.gz0000644000000000000000000000532714116520000022365 0ustar rootroot‹Ï]Y\ÅYysÛ6ÿ_Ÿâº ¥Ø¢Û×u­¾ÛŸQt\¸ZÝyñx¿Ýí´Pú¿;mZÊpýOœõ*û/â¼Ã‘Á±ªRÞÅévÚÝîé÷p¶æÓ´¶þtØÆM7­O`´¿¢ÆNCEU¼Šó•þªE ݈šKG×ê ïàK\¶ Ü€nEþ| Î6àí^ydÙk!d`4í}‰&Ä×Ï„™˜•>•÷8o9súÂ*mÓl÷4É–½“h:¯Yi_ð Ì;k¶8{£‰ú­*ÏÔ×<çÖ'«$ÔÛ‹-'è9Ù9Æ„ôΦdÏ[ô|)Ÿ/‘»ÝX›¸Ütc«j~Ÿk+1Y@œª­îíC×è`ø4@qh{í&÷]É`k ®—4ÔV‘{¥Œ³ÓÞ³–´ä’É ÓEÒ.9¸€ÞÕÁ(ôY_PyÖ_¯Æ“Y¶¼†8ãoVy~£ïÇûáûøÇj0²œÅ'Çg©«FYøZ‚iµvc$ÔÚÝ´ì² C®HÎq++,+n.Ù¸¼,×ᥙéñuWtÔÛ¡åâåQ÷GrjÕâr@bõ½OJ­’ÜÕ7"JGQñ4]oë)·g‹Õ6G€´&é:¯õÎÙh½Ã~Z3fÏ›äÍb.ΨÈ(ò¨à¨Úƒ¿yZâ‹uøŠ®¹#¡úðbxývty6üFû8¹>¥-ª÷:Ý[^«³‡FÓ \ÈV.e¤0øý`w°9^ AŠ©ïǸžÑ‘é1†^ä!º4t*æ"?YÞ!Ûº".Bï •‡&6F€1úÑÜ]¨õùl‘b‚Ó§?¡%BðA(M phh$v’Å`Ê'ºÇÞ,Ñ3 /½"æg/G<±Á îø”êýóãzŠ·©ŒBoAN‹táFá‚®/Ï/»¤³0)nÉ@Äño—£ñplìxª{¹ê®óÈ žUè:Á½v‚¡LY¬Þ:Ïǃ«áèzxy±Ijãàz†ÆÛ4sãU¿  ÜÐú‹SÓy)"!M›Nå\`ÝÖ]lÙç©™,"Ÿg© 9#æ<Ç ÏåYÈýÌœ/n=¡Å^’axO&¹ [ûn2™,,HúÆ|ôjeƒ ³F›âËÄ!8I¤–£€£OØÆ"—Zç;†­lލÓ:-§ÅZ:-¦ç´@]/ »uÈáXoi( ´„©×˜8ѯ—keÚg-—ÎD¤õŠDWB„B¾€FÜúö¦E}‘9ο—Ýö0'zf×-¢üÍO¸êmIâ X"¨Â$pÂü«°%T½Æ¯%õ¥<”ñDèqEë·‚ÃãNtdÞ|Äç—Ô*2Wð`¢Ic"š…ÞŒb¤4{v"ò¹ utIîíí­UzÒ¹¯F·"ý&• ägÒÇÿWub÷ö­[øHÿHN¿ªÒ&ôgÕX£¨iPÜ€ÓÕ˜Ï\jÔ3o6*EÖ嵆mµPmÐÆ¨Ydˆµ¶©åmM’k3Ï0à›tÖÞ¸Q!Vp“µmØ6°­XP » Ä®„Ãý– ~yAUøX„ô9kPĘ-c™-Ì3Óp#¥K•LF~º ÝoEÿ®Ü+Ÿt;½Ýûn‰0ò`û–·ø&ßÜAù²ƒ ^y úºþàÌQ•¯6«¬ëûæ)L¸ˆ´2 '‹ gòfÁ¯´„béÿIöF^~xcÏp¾Kô¿ð‘ê/yiÃ-¸’j¦h•ðÅNCÀ<üPHúÐÞúŽÓÑÈ‘W1+#B3aɃ_õSâB(ýÈ‚¢f¬¹Ü¯W"7C+ïÑ7ƒÀ´0§ÿ;5]v"ǯú磳ã;ou×(;\wG«ÖfSÇ{@~9¡.š"¢v¡²vN–¡Qé‘%Œ,Ï^ü|G³34Ñ„ˆlnú&ÅTm#¸õ’’'â0?4´õ­’)§%’§ÚCeKå'v,ŽÊV£ÿâúôòê§Ê&¾¹*$ò_/è0É´§eâüéÐiEòFØãþ?_\õíDØïÓ£Mcî°È/©ßVæ-î„ë}nß Upƒ:®ÁMšÍjv±6ƒÚë3‹gÖ4uþÛ°E¸©?–ˆœ?ëJIÐçÏuB–U‚¹ãTyn"HÁ‚¥Û)6e j8ÐMlÐ,•ÍÙ¾–¢wp@‰˜ÔAkG¶¶+i®õ2ü|¸¿3Ëd~ŠU«"<š±Ò+J‘‡(|7ËSHï"«XEBK¾øB";`PÆ`Ìiʾfü9ÈBl´ì˳ö.-„‹]žNxªò©ž‰},M°ñ"žŒ’¶ HM@C•D"7½3Ú‹?±VÆç'PæùŒ<™§ ¡¼”9³¹p—Ž$•žPŠ%ÓP2Ï’<ãS8ÎBç¥H¸1Ô:ËCš!.ÁX©ÁW5/4ú`†YD#æï€3¢“9Nü½“514uÚã3qJßSÏŒ¡˜W`@ûÌ mv ; =kaI'Ä ÐÓ‹ÖwíHÙ®çQß~JûûÝÞ ÛÛ§É›ÑÆÞ¿¤'ö mòî+ᦠs‘Çø;a…”bcþVrFH;ø½FòÒ)qÓLÌãþ,÷çêð2 ËqÑIFG'–â!©ÇÀwg¥ÄaGñó3e"f¦wååç}aºiw²cïÅç í ~yeE{¿hP¿S§™øšæõÕ*€O#Ë™ œÖb‰c ½²ÎÙI‡NJÞpù¤¼ÜïGZí¸Ë5@?²êê÷9·’a“Ä®ÚéP>ô †N ˆ#+<èîµ¹¶èK>¶’à²ÄP;¾<(¸à;{ݧV‰%K"ÕMA[Rpqú‰vF¡÷†‚†åÞ°Ú;­Æ“iZ>Cñ×Uœ_ë÷qÛ}?«;#ó™u|溮” à¾Ób=‰hëɺfË, ¾ByƒWYÉ0g^ñ*pÊÆ‹à¥™v÷0IM¯«¢£Á•“÷Gýgä4êÉå€D5Þ'¥ª‰$·ã´õáOóÕ²ö‘by±¬–Ù¤5MVa|©W^W+l§a¶Ì© Þ4â䌌Œ$ ެ}ü7wKü°Ž>Ð%W$Ô¼<sþñ¼sz2ºŸ¿¤}ì_žÑ-ÔôúÏ:½A§÷E!:C¹”LÊP¡ üÁîaq²D±ÑÐðT£:Ó- ½ËTl¨ZÌ£þª|OvuIEœ~ËUÌнñ(fÞ¸KµÚ_,tsz÷G”G¨®„Òh8‡âFb%^R¢¼£ë}á-bÝúÒË#ÎwvÙî¹ð4q(z'gÔ|;|sÚlœŽðƒ,‘aà-ÉéP1âE© ºB5ô:ˆó/æþäãÛóñd41ª=Óå]}ÕyäÌ^úZ¡;³ úäÌF2ÑA‡)‚x<~øi=÷ŽZ™.ÿâýTD¨ªÿ$Å(‘if°X´“ÓÉñÅh|9:».ÝÚÆåå1ÊËyêFºRF휤Ê6m›Pë­Ð$ìäΛæQÆ·®_÷~{\’±ñPqϸ‡ç{,·p"à¶°„Q¨áL'¨«]Zá#x¸D¦«\;ãÔ•@í¢´ *[ë 5~Ì× ý1„Q¹¾Ÿr¡ IT"<ø¥ðwP†F…¼Žk:f4«Ì²ð ™—¦‘Íê*ò ì”Û†kyÅШE)BÔÚŒÑaŸ®Zª®¡ÒE›§A¶ìëv*SX˦áL_ãÙ ƒ?Xa†Ù]CZ÷Þ¹þŽãªªÂS 8•Rë—åS:~mcûsmÝ ¯®-Ž© O ߤ'úЈƒ¿)š þÌQðBÊK…ˆÙTS¡‰œ~ñ„öBužêà²+H Ò¿òDF.ôuÉ-Ÿn Lú„®ÁÍÃì—z²…žÜƒ>F²  ®M>£-¤è¤7Æb[Hj I݃4É ‘3=µB‰·Pâ{PÞº‘0_Ë&"Ôq°BKaq‡Ë-Ðrغ¤z`i¦˜¦òF‰Ÿ£í¸Í]VÊpFx¢¼Ñ1R‘›!è önÏ)À¸ö&m}êy±ßl\˜ã½F ÖÆ’nˆaé§7­gíX9žïSÏyI»»n¿ÓÝ¥ñéðG“WoiÇé·Mß=^1E‚ÿcvHe6à3É!³ñ»EóÒ)õ²\•ʃ~Z3wø¹D万è&£ƒ£KùÔkàÙIe1×äìÆ+ð˜œíøÜio~82žÐ¯Õ¶­V¯°[.þÔÓây¿h·7ðêÆí5y?¬Éë®I{±&ì…‘U«Ì;îiçÿUk­:ÏOuà·:°Uîé~ùÝùsÁ×½ÙãÆÕÒæÞ»^×FëvÒ¶\´áæ7ÜìͺøïÒ|•¡±êZ>¤éu;½^EôÛ4[OËi[[O ß?B6¬ñÒÄmëþ ŠÎŸ˜ñ»ÛRQ®Ã|°Và…jacQ» ¤ ­~ðö¾'eÊmò×1äáUäÏkHv€ïôË+ËY)$ƒ£yïn4#~~&,Ä@úV>’¼éÞ1Vé›v§¯YÚÎv¢ù|`£Á/0Ÿ¬Äâî£fØu™i eÞY÷Vɨÿ2¶Üißmͨ1f¤OÖ5;´é°Ò ćq¯k·ƒ–g€^lÕÝpo%£&+ˆ[µÙb|茜8­b£ßÙnólÑ“Œ¶²à²â¡6ûLÜ/µ`šíÎK«â%+!KJE[Ið€½£“Qè» Au7XÞ-Æ—YV=C\ñ·Ë:¿ÕïããôÝxUOFÖ³¸wÖºî”%ƒ?+0mÖNŒBƒY;ëž­º0ôŠä^e%£‚uÅ«À-/‚Ÿå:Ý£433¾žŠ^÷·©®_÷^‘Û¨7—=Ëï]Rj Hò–ßÈ(E=äÓlu¬s¤<ž/–ǜҚ¤«2¾Ô'ïF«ŽÓJ0GæÈossFGF“GG×>ø›·%~X‡ïé’'j^žŸžÿznÏ/NG´‹ÛËztLÍ~·÷ÊîöíîK „Ø åBÚ¹”‘ÂøƒÓÅáxA)¦Aã™Æd¦×ºÊCLk˜X̃~\½%[zœ"nFŸ •‡SlnL§ ¢;o¡V÷óEŠMNßþˆÑÓÀPšð0ØHœ$ Š!”oô¬/üy¢wÁ@úE̽ΩV=9‚ïø„šgƒÓ£fÃäÚ˜0u1à’ƒ2ó²F‚|žx±‘`\Dï¤øJ©ŒBaŽ=;‡cãç=ëÕOÝîôméÀJœ;½ ît(S6ÐGÃëðh|p1]ÏÏÖÙ­]\Î1œb¸›e^ŒÒð8Á0"«Þ«)­§"Umãa³Œ?þž ±Hñ°›`y–zVbEï`LJì#¯ñõ°¦Eáˆ8¼ª­<6 #³Õa°Ôx)æõÜl€KMÌêКØÙ™•^½HbÈÞÙ¢Àgl|èŠ,+X3œË ªq<Á„ÓEǸó\;ìAdj‡#j"6®íÚ–bbz¨Pp/b`6MCjZT©$ò²[£«øãþGex~ˆ`žOi’ä™D+/’œÍœ{èK4Gš%!%{¦E%¹JsÅT §,BÏ¥Hx1Â:ÍCš¢/aXÁãJ• |4Ÿ*¢Û·ÇÑU®¿tU@·®ÇÓĽ¤ŽùF`Ža€®™'msRä¡7™Àaí,éqz×xÑŒ¤íM&䨝iw·Ýé¶;»4:ülÊáªÿžvìnÓÌÝcáePs‘Çø;—nþðDÈZø½ÃðÒ)õ2% ãÁ?Íý™@8&*Aæx†è!£“£Kq‘T{àÅIé1×dïF+ð˜ìí ø¹Sgþph"¡o«Gb 'o5\ü ¨§ùËnÞlnàÖškú~ZÓ×YÓöjMÙ+£«pVš{ÜÓÁÿQo­ªÌ›*ðGتô°ünÿ‰»ëÞîñàjhwïóݶÓiaô¿8iZ.ÆpýNöòzUýwynpdx¬ª•yœNÛqJ¦?žáÙz^OÓÚz†øá ±›&jZÏp´ÿÂÿ¸ Vù*9TË„5|/‹¶!6ËDšÔê oï{Z6aÜ&CnEþ¼†fôv·8²ì• 24Zö.áD âëgÌJ ¤O“'š7Ý{úÂ*bÓlwµÈ–½k9¿±Ó¾à˜1+µ8û] õ[U©¯uÞ[V!¨û:²Üi×mÌè1¤1ë–¶è°´ ̇%³ãD:ìàåÀ‰¬jø}ž­dÌdq*7ÖÇø`ô4 q`åÝöv“w 'a²•—¥ ¹Ùeænaól·_[¥¬¤T²äôд¥Ô;º…>ë ê•g½åÙѲ`&I–•×wüݲÏïôýø´|7ÞT‹‘íÌ\Ÿ­®e)à¯L»µ¡ÑàÖÎzdË) »Âä·²LœmÅ­À#7Â$SºÜÃ43;¾ÞŠºÛT×ÎrkÕá²Gbù½KR.„¼å7*JW‘ƒzš­ÐºF ô|±Ds$Ö8]µñ¥ÆœWÎÓJ1gæÈ4oñpÆDÆÇÇÔîÿͯ%¾Xé’7ª_žŸ:oÎ/.‡ç§ƒþ'ÚÅùå =s@õnÇyÓêt[×X ñ2LIK%I(ñüÉî9Z`YЍçG¸ª±é' ]©¶s©—÷É–^©ˆÒç\ª`Š×3 0½ðÞ[ÈÕù|‘â5§OÆz„àVH- tXn`âEPÊ'zß“y¬ßƒ~2É#žwvùÜóP'xÄaéPýCïì¨^3¹-™d*MÂ`² ™Ç Ÿñs"×-tAšPÑiç_¼Ñ§çÃÑ`dB}¢×½*Ö}åNßñ[is§†ýðhÔ¿ /çÖ%¬\α’b¥›e^„†ð¸P×lÕ[,_ßAÌ»7ˆ|‹c»eÕX!'I¬ô=qInvC›´üÃJ€çWÆ4°9anÃÁz*¾NDª ÐSËðl žÈ)¬6F!òÃaíS’ókË•YCKÝH1ÄÚBMÚ&.mÃöoã‹ º)ÂÍtr!Û·ÛxØÆêœgHų|Ì’óëòƒb¼±4H…vXÇa=F ïþ«ØÚ&)G{gÃÓ£G9­b‡TGhFÁz"`Dv4Û€›£þ”ì×crPئŸ–á>ý°Ç$ŒÁï¯~yd¬ÁàiƒÕ—°KûÄ-3ÎgrË2# +äÀì—vðf,Ý–ˆß…h.i'ÒíHqé]]žœ_<ÒTA"*—BÉ<¡Ž2í§"žåAì¶Œ;ïn+Lî„=êýëê¢gÇÂþœ¬ìYKþçØkg\/Wó$³ÿô"¼a>Ìò…ˆiÿ3CïŒ!w.JÌÎo)þAžÚa1`̸ã&»c‰W\¤ÑÑõNGç«Bµ©*óA„(Ûe%5v›º“FBÐþ\©TB0™÷z*Eü¿'AÌï_3SkÿÐ`&Ñtomoyo-tools/usr_share_man/man8/tomoyo-queryd.8.gz0000644000000000000000000000503214116520000021263 0ustar rootroot‹Ï]Y\­X}sÓFÿߟâsE6‰eË&¤”Áä¥ñ_œP:U{¬¥µ-´B+9˜ëñÙï÷ìʶìÚ»if’ѳ»Ïû{\¿Iƒ²P‰(¢@Äñ’f2•¹(dH“%TxtôJ¤Ôwû}jp%Y,éÀõÚ ×oò/ ‘†")Ë¥H&±<2Ç?üE? 7”4Î$ßÈ܈J:¤ÖÍ\¦t#)©SP©%¹£‹¦T«3r- ”2Ôp]CëÍ„i½³(¥…Ì'P?¡B~*î´ ãŸn:ů$ßÿ›·F’Œtš†w \âod²5$$ÉH«rÍE.‚BæTä"Õ1ÐTª æb[…r¥°µ.ò(i—ÈÐêtè&ŠcCj-$S™N`Ù¦¥Pèù>? ÍCû(‹ÌÙËfíLP,§…!ª’q?–ªû†=_n?ΣټØzi:ÞÛˆÒ(€JÇ{{xq,²¨€G îLP¤Ù ! ”nÉ-ê†R1—¹œª\ÒbßM@w!¢˜ßV|ß­=vH~ÊÙwÁmi®¦Ó}C)UÅæãӜȚX¸‰Š9?}¿9ù­?uàJ ÁéØ_8×[8þÜéx™ãëN¯úÞóu×µ[„Dùÿö4ঊ`uwd‘…o-?m¹ç?<,Û÷[^òCänÐ Ý~éÔ¿;ì§ú+òz Uó¯’ë}›Ü¡¥¶&ׯ“¤Ù\C—[Ì^‡†œIîÿ§áÊx׿û-™ø¿×²:£wïêŒgE¬ª§:™$ ®ÂN³+ãYÉnsƒ¦¹JhÆþu4]#rÒ$¼¸ÆCl¤™±Ëà#äYd²?Õ©³f<œr Ò娚S±[”9'¤J÷QUΪ B¤P~"™B¨RÊ<çX³ñ1.ZîÕy{ŸæRà–Á1ƒºœhØÜÇÑGP/a`˜µmB†ÔŠI"òÆDñÛ§ÏØ*£‹óbJ*sT^ª’Åœ ä%’#ËU µfÍ )UYYð+¼Ž§LÂÔ¥DŠf–1M‘—leÁ³Z”K¾µŸEBC–ïˆ+¢_ø~ú_4ô›¦ìñ›4§ï¨g¿a˜3`bæA×ÞT~6Ê’)ˆQe§ç­{íD»"ÈsÑáa·×ïöiüjø½ ‡ëãôÐí·mÝ=“"›Ë2Åß1d¥6à׊+BÞÁïÅËT¤Lä…®„þ´ gæ Ïq 1EÆ8Ç–ª‘ÔsàÞùJcÞ{˜lÀ3rjà/äO½ ø+À‘µ„éV;d[-¯ì´|ü ¨§åwý²Ý~€®›´·ø=Þâ×Ûâv‹Ù}Ë«RVÛ>.Œñÿ¬¶Næ»:ð[د_èËú»ûuA×ÿpÄ…«eÔ½)v½^¥ÿÞyÛñQ†›¿ãæ¨lÖÙç®,ŽS—rÇëu=o…ôÛ8ûwói;ûw<þrë±%N“´/w`tÿ@Ï~KÇu¼š‹µÃZ¡ˆô²ƒiHÆí•#­kMÃ;ú—=·Ç_gà‡®ÈŸoÀÙÅ{·_]9îF<²o íCÂ!ÄígÂL,dnÕ-Î{þ †Ò©lÓîö ÉŽ{:¿°Ò¡äÌ'¶¸ûÕ ;užYhxÞ8_œŠPÿQâøÓ¾ßš/‘cLÈœlKvÒ¡“•l@>Y!{^bÌ\ž¼Ä©›?äÚJVL·zÏb|ð¬–O ‡Nù ß=hólá)~¶ÑàjECïõ¹_IÁ8ÝGΊ–Z1Yc $튃Àë‡&¥¹H¬îë»ÓuÀ*ÏWmˆ3~±Îó…é·Ã÷Á“z0²œå?d©ëFYø£3j=LhPëá¶eWUrÅê]Y«¸dYѸd£#yaÂ=Îr;㛩èYÿ€VÀ›gÞòõârDrý}HZ¯Ebýˆ2Qä!žf›c#Õñ|¹>æPÎ$Û¤ñ•9y9Úœ°Ÿ6ŒÙ3§6yó„‹3*2Š<*8ªöñ_¼-qc¾¥+žH¨yuñêâç‹Î?¯O/>¡CÜ]ÓÎ!5û=ïI§×ïôaÄF¨–ªS(k,ÝÇK I Â-S™Ye躈0©aZ±ÍülÕGöÍ(E\ˆÞ—ºˆ¦ØÚÄ7b©7÷óe†-ÎÜ~±“À© 5¼ÃP£p’.)S¾1s¾ æ©ÙC” ×9wµæ Ä–7 »ãsj¾¼:m6¬B~çc)ó%VŠÂKÄ~Kœí™r‰[ÍìS1“L“J£ÜÒĤµ½ŒÒò“%=þùõÅh<[kŸ›‰¯~êß÷§/*SZÆþ‹âht÷ ýâO‡¹L0ÿ+ÊŽª¯Lå.µ,ONÇÇ—ÃÑÕðâõ6×­‹«9&YL‚³\$H >Sq,i©ÕZÌEA*ÊœMªDD8ÕØ^1HÊJ¼V%´]¸Üàò¤F:‹Åó)g$VÂ2™@5%•UK.v;ÞSC/â…¼€]aÎ\¾‡X!Z[Þ¥‘•Ù¢bÂû,yõ…x;•:UIV·EÚRð /61©r×˜ØØÀ.£<äšÿ”¨‡¬S&‚p²™ã™€6±9\fzÿÂÒÌÓØßÓ™¹LæAmT °“›…U±–ã1„«x ƒˆxùy7°40̾‹©´²X[¦øXòH‡L6P iF\Áôk®Ç{0ðg¤J«( Ž¬a™õ¨ÄÒ…ÂBa–J•µÕ§`€]˜¢ b©Œ»XYÊñßµ¡Úµ™‘#]L ëä³1ª‰°}ËÚ˜¾4ÿS‚~«i“1Qªàç»»6¸/Løî¤SípDÍodH“Ä!©æ¯ša ~(ˆR£ Ë+®#·Òc¿*d lþ‡ .à¿UGLŸ6âUBq|q÷Ž|ÝC˜†9{¯®ÓªúvðjôòtGÿú©1À‹sä[,ïÈç*–­>Ö²·ìò¿ ƒå› y¨VØÿ¶ (.䟒G¬¬÷ˆ³ƒûuiÈ{Òw½Ç‡®çzG^?V>6ê‹ëw jO°üs`Ûäúµ¤I9ƒÇµ4 I”˜ÓÿéŠïŽÚïÈôyŒ„Ñ®Òaê&òYå»ÁõÕùÅå§Ú!,u% ]*b=Íd:+£ÔïØT{>ô;±ZHw<øÇõåÀM¥û>{¶m«; ¬ñïBo¼BE'QsSYŠ¿‹[þëY¹D=|úž¡çVǘ›(·ü°ÃøOâ4NªVl[w…9ÑQ!+#OOiðr|±Ó/×§õ®(è°QÑ1©†Èi¶±‹­_¤¨"SÓ8qa*ßXJz:/ŠLu«’eý[˜žpaŒ¸±%viüëH‰htomoyo-tools/usr_share_man/man8/tomoyo-auditd.8.gz0000644000000000000000000000467014116520000021233 0ustar rootroot‹Î]Y\­XëSÛHÿÎ_Ñå\"°ü€l–¥âðX|—†l¶¢ìE–F¶‚^ÑŒL|—ão¿_ÏHF6Nv?,UPêžé÷cº± •Æ® =7Š4‰È]%|š,h”ú‡‡oÜ„úv¿OM ŽÃ8‹íÙ½½Ö–í4ø—ÆÊM|7÷)Ë…O"q¨Ñ¿üM?[¶/hœX¾¹V•dæz‚šw3‘Ð ÏM,E…dFP, H‘-3²ŸÏ5”0´eÛš×» óz%¦aBs‘O`~LJ|U[v èä·-; ð+ÈqþÑ[ &:Kü $Wøê‹ì M2’i ½fnîzJä¤r7‘ÈÒDÜžòE&ðµTy˜L¥Mäl7Ûmº £H³š†sËT$xöÖ…kÉwål—/ކú¢¹”…÷ºQù‰@iF~Z0í—"UbW‹Çå«ÕËy8©•›F¡“u…( =˜t²³ƒ'n*DæN] %GÁ'•‚Ó#½…6]sR3‘‹ ÍÝ¥» øÎÝ0⻥ÜO•¢'‰¯`ûÉ"„-ÉÓ ØÕœ’TÍà>Æ*%°Õ¹pª#.*'§¹ý[¡”PœNœ¹Õ¶{sË™Yí^f9²Ýß)¿wÙÅqí)$BÎ-èFSɰD Yh ä[ÓIšöÅ/ûEëY³ÿÒÇÙdšo¿°êßmŽSûSêuÁKy³ï²ëþ˜Ýá¶dׯ³Ò¤ÑXBW+Ü^‡,†¬IîüoËѺ¾9M;ßêN€Y]ЧOuA–U1+{Ç™ôÜLD«´“Ê(DÕp¡›Ú  Ocšr|-I7HÀ5©‹ ÑŽm!vÒTûeðz¸_Xe2?%ÖZ œƒtN9ú€äEîª"ç‚L“]t‹k£lˆPÉ_I$P ¦ óEžs®™|U„ƒ¦}}ÑÚ¥™pqÊà˜AYL¤ðLí5 æÅ ³–)H-@³ª„Än~ et¿?:f¯Œ.OáÌË€¼´È%Jy‘¬æÌE]¢8²<õ„”l™f•*+ßÂí(`º/ÅÂMàÖ ˆ(@]B±Êƒçµ,4|o>ULCÖï;¢£'yꨀ~C·=¾“äô”ºæŽ9‡:g1´ÍI‡çÁ`m,醖~zÙ|ÒŠ¥ízõìçtpÐéö;Ý¿þlÒáæäíÛý–é»çÂÍ!æªHðwÌ©Ìü6厷ñ;GóÒ)ss%KåAþTÀžJ9î!ºÉèàèÆR>$õxrQYÌÀ;²âðœì½øœ ÷~82žÐ¯ÕÛf³W´›~ÔAñ´_´ZÛxuãÖŠ¼ŸVäuW¤=[öÌÈ*•æwµóÿªµVç§:ðGØ­÷t¿üî|Ç\ðun¹q5µ¹wÅöA§×m£õ?¹hYÚpãN‹F]üi>áÈÐXu-×izÝN¯Wý±fw³œ–µ»áòý£Ë†5^š¸eÝo èü‰ÿqš2ªÓÕb¨–kún(mLC"jU4¡ÕÞá¤ì@¹þ:‡<¼Šüù’mÜ·ûå‘e?(KæŽæ}@8ÑŒøù™°éÓô‘äç}a•¾iuúšeÛÞK4Ÿl´/øf̃Xœ}ÔLýv]fæk™wÖ½U2ê?-'è;ÍÙ5ÆŒ4fU³Ó6Vºø´"îõbívÐò Ћ­ºû}î­dÔdq*wzCŒ=cƒ‘ÓÇ¡Ul÷;{-ž-z)_{°àºâ!wúLÜ/µ`š½Îs«â•VB–”.ж’àâö¾NF¡Ï‚ÕÙ`yv¶L/Íóê⊟/ë|®ßÇÇé»ý¢žŒ¬gqïø¬uÝ)KV`Ú¬ý…³öW=[uaè¥wx•e¬+^nÙx¼\ét²ÜÌøz*:îïQ¼;î½ g«Þ\I,¿HÊ%’»üFFé,ê!Ÿ¦h#%z¶X¢9Rk’=”ñµÆ¼=`8N‚92g¦xó˜›3:2š<:8ºöÉß¼-ñÃ:|O×<‘PãúòÍåï—íÁÍéðú”pv}AkHjô»½ín¿Ý}ŽaaºHÛ*M#‰åï'» äx!)¦ã‰ÆT¦WºQ!&5L+æ1?¯Þ‘]=J7¢Ï…Ta€­ àAtç.äÃùl‘a‹Ó§?c,Â$p+¤æ†{jR`’ÅÊ'zÎÞ,Ñ{ ŸzEÌ}ήÖ<ùå Ãîø‚ooÎ[Æ §í~¨°R´uwÄœ”‹/… 7Å7êÃçMÂwE Û8£èu˜_ Çñïo/GãáØ8ùBzu¬óÌ ^•4òœûáh´ù„>8Á0‡<%þf‡åW–æ ‡ÈÓ³ñÉÕpt=¼|»*uåàz†à4wcãr>²²¤yò½˜u#]PwyÈc7H>ˆR¯ÜSe&<„NøÚé8òÒ$§Eü ä]íÕ‚ò"áqWj*¸ 32F¬"ÛݬÜ+BÞÉ8’±B&°˜ÙЦ µÂa½çQb2¾­y mxò‡”ÔLòæÔð†˜p°Mæfe6”¥Qè-0k3×j_‰(ã^#¢4+ïQ'¥ÑÂØWf"r¡¼Ž‰iG×KÇDÖfO!‚6ï³æ¨íém®‹of‡: %’*!à5ב^Ó^f¨Ã~ÇE¤Bžø˜ƒ´é2‰šæ•>AV§Zâµ§¸ôÊålCÍlPÚ³¯a\Ä—4ÕbTßè æä§pVy£Š˺Èßø!¶Åܯr`Ì鵚t:»YBL–‡¼º! 1Øû/’UJRSØS{Áܳ9ë#DŒ· .ºK]Vke^CލñƒÊmо&rÏll¼Õغ¼èÍÅ÷ëù¡ Hšî;]mÖRµËÕ˜ð{Ε·ž-ÅŠ£’–àtGoaL7œôB"’… ɱ›ïÒ¾[ÉýX+zìø’3¶BزBFÑžá”I½€û« –ÀÖäÂM¬tüì⢠òÚ~ì"” ŠÓ±·tºî`éx §;ÈOu‡»Õ÷®§ú8nœ"…RÈû¯·݈ÀaªV„#-„|k{YÛ=~Pv¶éó!>ÈݾÃÒi~w9N1ì—4胗È®ÿçì-·5»a“È&­ÖºÜ‚àö&ä0äÌ ï;®Hn{à«×©÷µéè7}üØä85³ªwœªÀÏ)D°N;Å¡LbT º­ Š ™Òœãë(z‡”¨ISTˆvêš±“æÆ/£ÏÐÃÿÌ*“ý©°ÎZð8â¤3*З(rW—¤ÌöÐU®ª!B¥P|!‘A((3(Š¢à\³ùëm÷ê¼³G áã”Á)ƒªœ)ØÚj ÌKç[F€aU Iýâ”1YüþÙ öÊäâμˆ(e¡PÊ+Y²š u‰âÈ ¥Ø2ÃJ–:/5ßÂí$b¦/¥ÂÏàÖ¨L(B]B±Úƒg,4~o?uJcÖïˆ;¢§=/{àé€aË´=¾“ô€úöŽ9ƒ&gî0tíI‡QÀ`c,™†W~zÙþ®“*׸éð°×öú‡4}3þަûãWtà;¶ïž ¿€˜Ë2ÿSvHm6à·’;BÑÅïÍËt¤Ü/´ª”}T†swZ"rÜCL“1Á1¥zHš5ðÝym1×ä¦ðŒÜýøyÑ`þ pb=a^«[lÛíAÙm{øARGåƒaÙé<«›v¶ä=Ù’×ß’öpKØC+«2VÙwÜ7Îÿ»Ö:Mž›À/M`¯ |£oëïÞ˜ ¾Þ§#n\mcîMùè°7èwÑú¿;ï8Úpë+NŽÊVSüŸÒ|Ä‘¥qšZÞ¦ô{ƒAMôË=4{÷Ëé8{÷\þvç²e—&í8ßî¡èý…¿{m•4é1Ô뀵C?V«.¦!‘tê@ÚКïèϤìB¹]þ:ƒ<¼Šüy É.î»ÃêÈq7Jà’½cxN #~~f,ÄBæTÞ‘¼ëÝàb(œÊ7Þаìºû™áó ~³‹³Ÿ Ó°Û”™‡FæóÍ© §Ž ½öb…cF³­ÙI—NjÝ@|R©q;hy¤NÓý!÷V²j²‚8U»ƒ1ƇµÁÊiƒãØ) {ûž-’¯m,¸ªy¨Ý!+-˜f¿÷Ø©yÉZÈšÒGÑÖ|Ü>0É(ÌÙHШ>­ÏN× È¢¨Ÿ!®øåºÎ—æ}¼›¾ž6“‘õ,¿y!kÝtÊšÁ_˜1ë E¡Á¬ƒmÏÖ]z%ò¯²’IɺâUà–!(´I÷$/ìŒo¦¢Ã}ªëƒ§äí4›Ë‰õ÷!)µ$ùëod”É¢òi¾A›©Ð‹ÕÍ Y¾)ã+ƒy=Ù`8NÁ™S[¼EÊÍM]ûøÞ–øa¿§+žH¨uuñæâÃE÷íÅÕøìà âðêœnc©5ìžvûÃnÿ1ÆAì„r%»ZÊDaý{âöœ®0&¥4 S<Ò˜ËÌ2CïtŒY óŠ}ÎÏê—dÏ SÄ­è·Ré8ÂÞÆpÉ(¹ñWjs¾XåØãÌé÷Œ0 |ÊpÃ=Œ5˜lE)„ò‰™ôE°ÈÌ&Ê L¹Ó¹õ¢ç#C°¾aÜžSëíèÍikÇäu±,ÄÑ [E—r™ÄÁŠ–±´k™³JK¬+"ÅNGë+zgåËsúáíÅd:žZGŸ›a¯‰õzѫʇ•D/º´´'§ÓãËñäj|ñv›|ëàjiÓܼðÓJ1aÆ:R6 þ& PQFwÌáÛ¾&eÁ ±æÇÀ*¬§˜EÓÞ›RLÒðÝd¡bÛ ©Ð òvmö3ÄÀ/f1d+4“Q y¸ ä3ã,4ü°Å¹t,³(ž—Øï4›R¹zxѸ'tгîé™ëUNrPÁS.ûSm Û”Šydƒâ³ZGŒÆþ î=–³6âð·&Å ¯°‚fÿ\òÔé›nX±åäʬ'¦³¡Õ¼‚™8á?&/b^ ò0l™‹[¸swmf¸‰DžÂªŽåŃ)Æzµ`ÁQ!Äï¼T׳{É» µµ4›°ŽSñ«–¿Þø±6 1Øa»3Ý|i×JQe›efK÷ÊíHÞ' 'éaÖzU‡¡„)ûÞ¦ééûÑ›ÉëÓ[)ÞÄN¨…,Ÿ"ê&älK!¥‹ª1'jëÎ _ÏhU‚Å¢aÆWFÙ˜0’b)½®Â¿ÉLOôúóq»¸YÈKãõ…TÚ0åbIýä×8ƒs–hO0+_ l!Œ»ƒ*emîG1òË?åÿ¤ˆ¹\R{b³ÎÖb¦ý/•Ç^½ûá–·,¦Š76›Ð”Ьœ«=S-œN¢öÌ³Ú ÞS”×ÙË¡T®Taæ¦âE%fôîêüâò–¤a¹Z•’ÎùåèY.²yg^׆þåØë&r)Üéè?ï.Gn&ÜßòÛ!»—Ášþ>ò7h.ä—è…kµø·Ÿb£|;/W"£g¿1ôÒÚ˜p'E­»å§[‚ÿ&ÍÎIÕô«€À•7b¦°SWNšžžÒèõôâVƒ^c›  0ý¹}ØYçó…Ö¹:êUõaƒCïÍwçÿ\à~Jtomoyo-tools/usr_share_man/man8/tomoyo-setprofile.8.gz0000644000000000000000000000463414116520000022135 0ustar rootroot‹Ï]Y\­XëSÛHÿ˹D6`Ù2a‹°,Ìâ;.L[ÑnE¶Æ¶½¢Ùñ^Ž¿ý~=#Ù2!K”z¦ß¯éÆvëÔËUy*˜xa¸¢™ˆEæ)áÓxEÃÄ?:ºòbêÚÝ.548 ¢4´o;ûÍšíÖù—FÊ‹}/ó)Í„Cq¤û‡~j¶/h”X¾™V•dêM5–sÓRÐÄ‹-E¹d‡P,˜’"[¦d,43T³mÍëõ˜y½³ ¦…ÈÆ0?"%>«š=Utú¦fÇSü rÝ9k"ÁDýØ€äÈÞÐ$%™DÐkîeÞD‰ŒTæÅ2YK‚»ØW¾˜1|-UÄ3i¹;V‹–AjV³`!€Ly<†g?zp-ùžœï1âp  Rè³ËzåÌ£PL•fä'9Ó~Ê%ö´x ßl#gÁl®¶0B§»÷¢8˜À¤ÓÝ]`œzi ˜;ó(ŸTN_é-´éš“š‹LL“LÐ2áØÁwá!ãrß—ŠžZ$>§ €í{‹¶8K¦Ó=Í)NÔîãS¥O lu.,5§ÓãÄEeä6vÞ´J ÅéÔ]X-ÛYXîÜj9©åÊVw·øÞueו[¤@ ¹ÿukÐ<¦‚aq€p¤o 7nØ¿=Í›ONô[doÈ4ßnnU¿[§ö'ätÀKMæßd×ù>»CÃmÍ®[e2¤I½¾†n¶ ¸½ Y YãÌý_Íá}|q"r¿TÒª ÷ï«‚,«dVô޾œx© ‰–i'9”a€ªáB7µAÓ,‰hÆñµ$½B&¨I]Tˆvd뱓fÚ/½OÐÃûÄ*“ù)N­µàÁ”sÎ)C\¢È]•g\I¼‡®bqm *ùâ3‰ê@Á$†b¾È2Î5“Ï qѰo/š{4n1(ó±Sû8áæE Ò¦)H-@³*…D^öÊè,~{|Â^^ŸÁ™×Sš$y&QÊ«$g5çêÅ‘fÉDHÉ–iVI®Ò\1°Ã)³Ð})^ ·Nó¦¨K(Vzð¼’å‚oͧŠhÀúqGt•ëÆ]UЭë¶Ç8qF©c¾á˜s( sæ+†¶¹)âЛL`°6–tC ?=oŒ6à9Ùûð¹Sgþ ph<¡_«{l 'o5\ü ©§ùãnÞlîàÕš[ò~Ù’×Ù’ödKØ#«0VšwÜÓÎÿYk­*Ï÷Uà¯*°WîènýÝþ†¹àë~<âÆÕÐæ.óöÓi¡õ?ºhZ.Úpý nŽòzUüwiÞãÊÐXU-ïÓ8¶ã”D=@³÷°œ¦µ÷òÝWȆ5^š¨iÝ=@Ñþ» Vé*1Të€5|/«¦!6Ë@šÐêïè{Rv¡Ü.C^Eþ| É6ðínqeÙ%€dp4ïCÂfÄÏϘ…Hß&_IÞu—@ô…Uø¦Ùîj–-{?Ö|ޱѾà˜O6bq÷§fê·ª2S_Ë\ZwVÁ¨{Yî´ë6æ+Ô3Ò'Ûšµè¬Ô Äg%±ãDÚí åÀ‰¬ªû}î­dÔdq+wÆÇØ`ä4Àq`å;Ýö~“g 'a´·%¹Ûeân¡Óì·¬’WR YSz(ÚR‚ì§:…¾ë ê•w½õ]0“$ËÊgˆ+~±®ó…~¿NßgÕdd=ó;×g­«NY3øQi³žF(4˜õtÛ³e†^a²Ä«,“0g]ñ*pËÆ‹0É”N÷0ÍÌŒ¯§¢“î>•Àëç¹µjs9"±þ>$)×@BÞú¥³ÈA>Í6Ç:GŠãùj}ÌXãtSÆ·úär¸9á8msdú¦x³ˆ›3:2š<:8ºöé?¼-ñÃ:xK·<‘PýöúêúëÖ¨;¼¹>\öé÷·ôÀÕ»çY«Ómu0b3LVIK%I(±þbwp8ZaXЍçGxª1镆^©¦󨟗ïÉž©ˆÒ‡\ª`Ší à˜^¸ôVrs?_¥Øæôí¯0|Rs†›'ñŠ"å=ï‹É<Öû ŸLòˆû]®{òK†ÞÑÕ_ö®úõš1ÈÅ 0)MøÇmQùéIÌbæB¹6g¥³Ñø‰.ƒ8ÿl˜þxy= FÆÏzÖ«žºOÜé‹ÂyQîô†Þ¹ÓA’²‰€xÄy4ß8ö±‹ñI‰ZÂdÛ6ñgýÑéÍ`x;¸~¹­ÁÖÅíó,DÏ2/*LÓs!Vg1A„¿6ÜèÀóãö½‘‡µfYSU®Øºyo âE‚AU£!Ê•‘Zq˜`I[®dû£Àt¶1 ç¼Û6.j#¬ÞLûÀ¦ÞT“ñè…Áß¼×¥ILV{F´Þ/sý¿ O®§ ‰ã$QŒ®MÐ9jw]k‡Ü VåpHu„Ëm±uzªÑœâõVV¯õÒ²~ä7(TxKÒrž`÷4Ŧm$,&M3Èos2Û]j±É†mEÌ1ÇTÜ®ý=Öÿ%b%àG@j)DLÝ,»þ•Û–P^F­5o½•a+ÊHèmMïáÞ" |®F›¥™P¦Œ oÍEÚàŽ] Œ<ìŸzýÇ*»‘Ça±¯ K§oôÞϱୟËÀT Ç}íµ"®ý·½«áeÿ^`«§&²=ûk¿uîÅë+WüŒ_ÉA‘c-z¨Ÿd ú½OÇ&ßO4*ñ-•!åÜ×›+•ú?«ÝwH ¨³­$µs™µå8ˆÛÌNß}CË©˜Åáxñê÷{¡0'س¹/`±cW4Îg±Ð ¨² Ç¥Š¼¦I·%âç!zˆ´éÇv$NЍ÷^Ý^\ßÜ“T9„ƒo…’yBüOI:NE<˃Øm3žÜV˜,„=êýçÕMÏŽ…ý!=Ùöñƒ Öô‘×®¸Ø½\Í“¬¨¶{ê—³|…R<þÀÐsccÈ/‰=Iìüã=Á?IS;+^;Sqù¥Ë@‰ÂI£~Ÿz—£ë{Ôú´ú@¥¨DÁSã°©;ýH:æË£vÑ¢M`¨~#~‚˜ÿïbÞòÚÿk aÆItomoyo-tools/usr_share_man/man8/tomoyo-savepolicy.8.gz0000644000000000000000000000542114116520000022132 0ustar rootroot‹Ï]Y\­X{sÚVÿŸOq†n#ˆ-p’qhš ñ£f7Žã¸ÉTm#Ш֫º»Ý|öý{%˜ÄÎl™±ç¾Îû-ËiÒ È“È̓©†Kš‰Xdn.<š,i”xýþ¹SÏêõ¨¥¶ã JCA–}ÐnXN“ÿhœ»±çf¥™p£I(úêøÇ¿é×°ådÉ”¬§ µ‹y×°,…ëz¸^‹YÓBdˆQ.>å ËÏéèç†ûøä8ÿ°W@‚NboÈ%þê!kC€“”d¯¹›¹Ó\d”gn,C€%±$¨‹uå ?ˆ¡k™gA<“‘ó¸ešt„¡B5 ©ˆ'Ðì Õ’çÊù>? ÕCý( ÔÙ›fíÌ¥Pø¹Bä%ÃþY$¹ØWäñøróqÌæùÆKÍÐÑÞ6CSˆt´·‡Gnä°ĹH¶‚GyLwøJt…)Ÿ‹LøI&è6aÛM€wá!¿-é~¬=2H|Jy´ ‚Ùâ,ñý}…)Nò9Ôǧ¹:% U¾päs:zñvÉ3rZ6aJ ÆéÈY¦e/ gn˜vj8Òìí•ë=Gvq]»… ¸ó§Þˆ€f*–0Gèü­åÄ-ëìÇ'EûQËŽ~ìaAÖLáíF}m²ÈŸÝ®|:ÿ"ºî×Ñjl+t½::€ÁMšÍÕîrcµ×wïŒIæü·a‰p[9-9Õ•Ò:¡ë„ £BVæŽ9uSA¬ÜN²)ÃQîcƒü,‰hÆö5$½ƒ&ˆIT°vd) ±’fJ/ƒ?Á‡û'³LúWž+ÂCŸ}N)C¢ðݼÈ8 “xYÅàØ("XòÄ'1؃I Æ<‘eìkÚŸƒ<ÄE˺:kïÓ\¸¸å혷²˜H1Õ±£1Ž ^Ä›aÚÖ©(T‘ÈÍnÀŒòâ÷/^²VFÇPæ…OÓ¤È$By™ÌæÜE\"8Ò,™ )Y2…*)ò´Èù^‡>£Py)n µúEH>âŒU<­y¹ á{½Ì#2}ΈNî8ñ÷NÞĦ×TißÄ}O]½†bNÁ€ò™;-}SÚa0B`%,©„”zzÕú®IËNɶžÒáa§Ûëti|>üA»Ã»£×ôÄêµuÞ=n2—EŒÿcVH%6öoΙ‰¿’—ÊH©›å²dð~áÍÔ1ÍXŽsˆJ2Ê8*±”…¤ßUóæš¬Ãh½=%ë ¶ý…ß^oÅv¤5¡ªÕÚVË.Ì–ƒœÚ/¾ïíöcTݨ½AïÙ½îµGÄiZ¥°R×qW)ÿ¡Òuœë›ßê›ýúæ3}^­;_x›>'®–÷¶x|ر»&RÿwgmÃAnþ…›~Ѭ“ÿ*ÌG\i£Îå6ŒÝíØvôÛ˜ýÝtÚÆþŽÇŸï<Ö¨Qi¢¶ñyDç1þí´dX‡«Ù0_¬å¹\šè†DØ® ©M« ^ÿkTöÀܯNAU‘—× lá½Õ+¯ kÍé7 ÷!áF!âò3a"z§n“;”÷œ[<ô„Qê¦Ýé)”¦u+<¿°Ðžà Ì'k²¸ûU!õÌ:ÍÔS4oÏF‰¨÷42¿ç´æKÄ#R'›œ›t\ñàã Ø¶#¥vÀr`GF]ýçVÒl2ƒ¸•{ö탭eÐtZÀ84ŠÇ½ÎA›{ ;ágk ®*r¯ÇÀ½’ †9è<5*\IEdé"h+ .^?QÎ(ÔÝ@Рº¬îNV3M²¬*Cñ‹Uœ/T}¼ë¾Ÿ×‘ù,>;s]WÊ Á}¦Äz!Ð Ö“MÍVY|…É-ª²L‚yEUà”Š0Íråîašé_uE/{Tm®_ÚÏÉiÔ“KŸÄj}HR®6 ¹«5íh©:KçÃÇïGQ_J–ŒL1æUgë£I#?•Eeà‰>ãó’È âß5*kšÄ>`÷ùF|šŠ”d×%DdTë35AúCxŽ;Yun —Ñ„Q`Öˆoä¤À˜¾’B±^"ÛñjZ ³Æ¹z„‘¶H=% wäI€î 6*d S ÃÙkuÁ«Í3ÒbÊ)|!š)ü$DÎä!xK&9OŠÐÃôŒ Qêæ À¦u"˾Âj]Oè|ÙëŽ%3/iSðÎÝW x§±vÁï|¨PÔMº ²~¯ê¶ÞP¿×6îë ;\¸±2ç= ÖµPJýü¥ªö탛4=®­Ü¡ŒAl2Ìp<ß±u9,…D_Çã"6˜+kΡ½}¦[dkA·ó`:¸Ã-ÕH Þ¢îƒ3𧮞K^ó%&x)Ô–ònÈJ~Q±Áþ$”¿ ÑI+‘^lEâeƒwWg—[”j‡Pú•Èe‘Ð[^¤"žAì˜Ú⯆Ž& aÿzw9°baý‘¾ÜÔïN+ø]às—Õ\ ˜geÒù§!vÞΊ¥ˆéż{¥e ¹{GCc7[„Ó8.ç'=Í©>BLd‹RI㓼_lMA«ÓúT#¼ ×f7Uã:Øæþ¼z&®·ªŒ¸S=ÅXz1ÏóTö;¥«hSAê³@ÄSó÷~=C6þàÁÈüÁtomoyo-tools/usr_share_man/man8/tomoyo-setlevel.8.gz0000644000000000000000000000436414116520000021604 0ustar rootroot‹Ï]Y\­X{SÛÆÿߟâŒsÙ€å¤C(0qŒ)îåáÁ@Ó©ÚA¶Ö¶‚¤U´’‰Û”Ï~gW²Å£iîL™ÑÙ=ïמƒíT©›¥2tSâÁ’f"‰› ÆKJooį̈cw:TÓàÈã@жÝÞ®Wl§Ê¿4JÝÈsâD¸á8{úøà_ú©Øž QL`y#­*©ØªÝÏED÷‚&nd¥”)AöpÅü)¥d«˜ì7 E Ul[óº3¯÷bæG´É懔ŠÏiÅž¦Ôû¹bGSü rœÿ´WD‚‰ú‘÷É%þú‘½! ILJ†Ðkî&î$ ¥‰©d2Rw±¯<1õ#øZ¥‰Í”MälÔ º÷ƒ@³šù dÊ¢1<{çµä¹j¾ÅˆÃF4H±¯ÏN«¥3—1M5#OfLû)“©ØÒâ|ù9ñgóô¦Q¨·ùT!Šü Lêmn£çÆ~ŠˆÀÜ™K¾â(x”Jpz¦·Ð¦kNé\$b*A÷’c7߅댛˽-íY$>Ç €í­E[”ÈétKsŠd:‡ûø4Õ§¶:îýtN½ýCÄ%MÈ©müÜ@(§ž³°v{a9s«ÑŽ-G5:›ù÷¦£Z¸.Ý"…RÈùÓ©@7"ð@˜r†ùÂûB¾Õœ¨fŸìdõ×µvxÐÁÙk2Í·“YåïÇɇý’Ú-ðJ'ó¿e×ú:»]ÃmÅ®Sf2¤Iµº‚.Ap{²²Æ‰óWÅÁS|qj"t¾”â² ÛÛ² Ë*˜å½£¯&n,H!‚EÚ)eà£j¸ÐMmÐ4‘!Í8¾–¢k$ DMê¢B´C[Gˆ4Ó~é~‚î'V™ÌO~j­¦œƒtL ú€âEî¦YÂ)£-t‹k#oˆPÉŸIDP ÊŠy"I8×L>ûi€‹š}uRߢ¹pqËàˆA••˜˜ÚÇÑG0/d`×MAjšU!$t“;(£³øÃþ!{exqg^Li"³D¡”—2c5ç.êÅ'r"”bË4+™¥q–2°ƒ)³Ð})n·N³€¦¨K(Vxð¸”å‚ÌgÒ€õÛãŽè¤Ž}ç¤Uªn{Œ%ôµÌ7s tÎ¥i·šívAôû 4[/Ë©[[/ ?´ FN V¶Ñin×y¶hKF[[pUðP›&îäZ0ÍvóUð’…¥‹¢-$¸ÀÞÑÉ(ô]WP·¸ë®îú«„™È$)ž!®øÅªÎú}|ž¾oËÉÈzfŽÇZ—²bðO¦ÍÚ Qh0kç±g‹. ½yWYÉ c]ñ*pËÆ‹0IRîAœ˜_OE‡m*€›Ãö[r*åæ²Gbõ½KJ­Iîꥳ¨|š­uŽäÇóåê˜3@Zãx]ÆWúät¸>á8­sdú¦x“›3:2š<:8ºvï_Þ–øa| +žH¨zuqvñËEcÔ¿:íßôOi·W'ô옪Vûm£Õi´Þ` ÄV(—²‘J(,€ßÛ-Ž–”Bêz!žiLfz¡ëÔÇ´†‰Å<èÇÅ[²¥Ç)âfô1S©?ÅæÆpJ7¸w—j}?_ÆØäôí0 Ü ¥¹ƒÄI´¤BùFÏúb2ô.èÉIr¯³‹UÏEŽ`ÃÀ;:¡êy÷¬_­ƒLp˂ǒKSNsw&˜eÚ˜¥ÎCã#:õ£ì³a5úåüb8ŒŒ‡Oô”W>u^;Ó÷¹ë AÎôOý rCq°pƒLàÀp;êz—ƒáÕàâü1ÃGWs ¦Ðt–¸!/º¼*;–ðà,csKM2Ôx”b—/¶Kø â›j©šwãmÐÄ,š%0²i4mæÄÐËF&‹bj 4¦‡r Å =¬kÉîBúÄ0;c™MM2Œ—ZìzA€x ‡•÷fÍK_0Ã#®c¸õ0ž~ƒâ&n‰V¼;Õd<>ºÿo„± üÉrˈ֛i¦ÿËáªÕü£ q,eÊèZoᶉOÿC÷lxÚíòéªxoîFC^I8oдвp Šm©7ÃhиJ;ší€+ªúÿ³€ø›1µ‘rÏŸævuÛiô.Î?ü©±ÖÄ1¥0¾¿þñ‰uæË‡ äÞ8›)¬1Bƒ$B,ê¬Ñ~!˜gkå4Dô.@ø”-•Ù¡8ÌÙ½¾:¹¸|"©t7^‰Te’Nø?I´‹h–ù‘Ó07p\{Ôýïõe׎„ý1>|ìÁ¬è_"¯œ¹H17Kç2±?¹!¶ óY¶ídè±1à&`O¤Ý=ü4•£¼M™:áwì^ŒöÀÜI£~Ÿº§£‹'½euZî-t=“ÝȤÚn]ÚHÚŸ§i¬öšy…˜@ÀXT!סωšF\ùÁޱytomoyo-tools/usr_share_man/man8/tomoyo-domainmatch.8.gz0000644000000000000000000000426614116520000022246 0ustar rootroot‹Î]Y\­XmSÛHþî_Ñå\"°l²EX–Šcàð]6[«ÝB–F¶‚Þ¢‘p|—ã·ßÓ3’-6ɇ¥¥žéyú½§'¦Ý¤A‘'‘“®†Kš‰XdN.<š.iœxçNL}³ß§–"'A”†‚vMk·Ý0í&ÿÒ$wbÏÉû©Hr±£Äƒùj“9 fó|ƒS+4Ü~ªÅ “†ÛÛà:i#"0wæP 9 å ¾Ò[(ÓR>™ð“LÐ"áØMûà!ó–rï*E‡‰Ï)€½3a‹³Ä÷wRœäs¸WsµJ€U¹°ò9 —<#»µõk¡”Pœ†öƒÑ1­Þ+5lÙéo—ßÛ¶ìa»¶‹H!û¿vºa*Ë„# 4…|kÙqË<ûe¯h¿jYÑ/}|¹>¦pû…QÿîpœØŸÕVîÎÿ®÷m¸}¶‚ë×áp iÒl®¨« n¯SSÆ4³ÿ×0EøÔ_ì–ˆì/u'@‡´.èî®.È0*°²wœH×IID°J;É¡ T º® ò³$¢Ç×tƒLP“ª¨íÈTb'Í”_Ÿ ‡ó‰U&ýS®+Á#ŸsN)C\¢Èݼȸ “x]ÅàÚ("TòÄg1Ô‚I Å<‘eœk:Ÿƒ<ÄF˼>kïÐ\8Øer¤,¦R¸ºö±4ÁÌ‹˜¥m]J€‚ª„DNveT8èÏ<¢ëwÀÑÎm;~içMý¦j{Ìgô’zúŽ9…*g¾4õN‡ëÂ`e,©†”~zÛzÑŽ¤é¸.YækÚßïöúÝÞ>MÎG?ët¸¾£=³ßÖ}÷T8Ä\1þNØ!•Ù /îY¿h^ª#¥N–ËRyœ÷ o&à7O9î!ªÉ¨à¨ÆR^$õxqVYÌÄ-™ûÑš<%s·FþN¶o­É?@޵'Ômõ¶Õ²ŠNËÆ’Ú/^ö‹v{ ·nÔÞ÷Ó†¼Þ†´WÂ^iY¥±RßãŽrþZkÔ1ïêÄŸub§N<Òãê»ûæ×¾?àÆÕRæ.Š­ý®Õë õ¿8k6Úpó vŠf]ü7ÏÜaKŸ1êZ>=cõº–Uúó™3;ÏËi;Ï0?~Ŭ¡qÓDmãñ™Ýï˜ñ»%Ãú¹Z óUÀZžÈeÓÛU uhÕ…wð-)ÛPn›¿N!·"ÞB² ~³_næZ 0i…½OØQ@|ýLYˆ¦Ônò•äm{FO¥oÚݾ‚옻±ÂùößÀ¼²‹½?¨×©ËL=%sa<%PÿudØ~ßnÍ—¨1R+›šwè¸Ò ‡«Ã–)·ã,ÏVdÔÝïqo%­&+ˆ]¹m0>XÚ-§Ä‘Qlõ»»mž-¬„ÙÖ\Wr»Ï‡û¥|f·ûÚ¨°’JÈ꤃¢­$8àÞSÉ(ÔÞ@РÚ¬öNV ã&YV]C\ñ«:P÷ã×é»õ¦žŒ¬gñh{¬uÝ)+€ï˜2k/B¡Á¬½MÏV]z…É·²L‚uÅ­À-7‚›å*ÝÃ4Ó3¾šŠŽú»T·GÖ²õær@bõ½OR®ˆ„œÕ72Je‘…|š­—UŽ”Ëóåj™3 1¦éºŒ¯ÕÊûñz…ã´Ì‘9ÑÅ›EܜёÑäÑÁѵ‡ók‰/ÖѺ扄š×—ç—¿]vŽ/Ï£‹óÁõðŒöÁp}FÏíP³ß³ÞtzýNï5ÆB¼ “eÒÉ“$”xþdö°8Yb\ŠhàE¸¬1Ÿ©G Ýäf6Ì-úZ?­n”5T·¤…Ìï7>× Â…³”ëýù2Å{NíþŒ 3Á½ |o¬ÄKŠ ”wÔÄ/Üy¬^„^âw<³zð9È<ã0öNΨy18?i6´AvÇã7ˆñðu9_‘”&aà. #iÌãʦ¥JJí-zÄÅg8ùíâr<M´»ÏÔÈW_µ_Ùþ»Òƒ5y¶…«¤[ìød2¼¯G—›x×s ©òf™)5åÕçx˜¤{Q6Ĥ%™¶ Äc““×ÉÑ âœOñ,Sá"*«w¨IJ þaHÀƒ,ãÁ–ø³L¤Ð·eabŸ]‘æŒAJ-bÃ¥R1ˆÕÿ"è's(ÛÙ©J S[}òap>~òÄ…µUõî2GcjBƒ &<Ä‹mÑòJkøªÃÐ Žf³+r·ë#A¦M>}›´§ÆÐFi ß­QjÐpĉßüBM…†y;% ™€ú™Ì[s‚Qh—¼»ùçwè¼W0Ïd¸ ¦ÅLâe!I !ê°’Ãã®´;"~¢^¤™H/6#qTz~ps}vyõDRm¾º¹,:ãÿÜ¡ÃTij"ˆíŽN¬·#»&œ þ}s50ca~L6ýô,ÀêüsÇçœEN‘Ï“ÌÔZüˉ³b)b:üÈÔ[mcÈ¥hº‰YÜ?üƒgÇeÏÐŒ¯–…˜J<ÍJ'MNNhð~rù¤ÂW«õ çÀ@¸ª}\Oãqc"Îó<•Ý®fÓa€©*{".Ž æ¬î‰ÿEž+Ê“tomoyo-tools/usr_share_man/man8/tomoyo-patternize.8.gz0000644000000000000000000000524114116520000022141 0ustar rootroot‹Ï]Y\­XkwÛFýî_£´¡[”H;]×MÓÈŽ]k7‰u,;M[¶JJŒù*9´¢n׿}/fHŠ’'{Nóä’ˆa Lbæ‰,ã\ÓùÈ móê¼³Gsáb•‡cæÅ$S}ö15ÆÜ‹x0L;ú@* ªR¹Ù ŒQYüîÙsfetñd^ø4MŠ,ÇQ^&›9wq.q8Ò,™Š¥ÃÃ^ßîõiüzøN‡ë“c:0펮»gÂ͠沈ñï˜ ©ÜÆøMÂ!ëâï-Š—ªH©›É¼4ò~áÍè˜Ê‘㢊Œ Ž*,åEÒ<Î+yð–ÌÃh5<#s¿1ü•ßZ Ãp¤™P·Õl»mݶƒ_Hj¿øÚ.:'¸u£Îš¾oÖôõ×´=^SöXë*Íõ=î*ò¿Ô[£‰ù¾9ø½9Økîè®þî}Â]à:7G\¸ÚÊÝEñä°gõ»(ýÎ;†ƒ2Üú +GE«©þA™÷XÒ2FÓÊM«ß³¬Jè÷-2{ÛõtŒ½-›ïîmÖиi¢Žq·E¢÷7þtÚyØ”kÄPÖk{n/»è†DØ©©C«.¼£‡´ì¸]þ:ƒ>ÜŠüùšMì7írÉ0WF`“Þ£° + ˆ¯Ÿ +Ñ#µšÜÓ¼ë,°ÑFÉM§g+È®¹+œ_ÙiOð Ì3+µXûMzݦÎÔS:ÆQÙO#Ãñm§=_âŒ1šY·ìe—^V¶Aøe%lY‘¢²ÜX‘ѤßãÚJÚL6«ù®5Dû`i´ž6‡FñÄîíw¸·°Þ¶òàªÂÈwm¶K+Xf¿÷Ô¨°’JI-éâÐV\ì>PÉ(ÔÚ@РZÔk§uÂL“,«®!>ñ·õ9¿U÷ãýô}òm3ÙÎâÎñØê&)5Àç˜rë ÂAƒ[ëÌVUv…É·rž„ÛŠ[K6n„i&Uº‡i¦{|Õ=·÷©¼}n}KÎN³¸‘¨¿)ÏëABnýŒRYd!Ÿf«i•#åô|YOs$Æ$]ã+5ój´šá8­sdNõáÍ".ΨÈ(ò¨à¨Ú'ók‰/Öá;ºâŽ„ZW¯/~¾èŽWW§—o†¿œÒ!Ö¯Îi˵ì¾õm·owûOÑâe˜,“®L’0Ç#ð³ÉñÍRD/ÂUîL=ièZèØÐµèKý¬ºOöTKE\>¹ |¼ÞXÄ Â…»ÌWëóeŠ×œZýí:‚‘+4ìCs“`&^R¥¼¢ú}1Çê=è%Ó"âzgVÏ=y‚GšÞñ9µÞ ^Ÿ¶v´CN7u%¾8øO— e;«À ‰ñº“h äÞ‹£Ñ§WéWÇÂ?˜ùAÈ3¿i}/OÇ'—ÃÑÕðâͺʵ…«9:Yt‚³ÌpŽ\Îo¼ñWž¨æ)¯íAÌ-"Ÿ«L¤¡Ë'må¾j>+§MºÔ;˜iÊ nx]¼àªw,4ÀðžÓžv«§R§·rÎd¯à‰#øŒ¡ä·[EqN9CƒèÚ¼²…U-²z\rÓÊæI&ñúçÕÍþ²òÙ'>NEªRSc›ŠòQí˜z¥r±)Eô,úæã­ò}5Þ–ü2-‰ñLºˆÃ¥z7øA–sŽâYÅ0õã—a9U¹Á ¦Eèf5£&¿;ÜzíPÅZ’’R¼òñøgôtîÆ3è×ipúnðzôêt#í³êµjGÔB†•1ChŽ[-~ õ¸)îù^¯ßâ :ÈŽß>nnpÄWj BÖ¢ÕÍ߇mŸoÀ6A7Ù„ýC.'­ n©LÀµÉ¯àáÛ Y8@xiˆE†·”bôße¢Ñ=Ÿ§Å²÷{È;YäÛya¤æ†/ce…zŸ–âçIY­XéÕmá„_¡œ|¸nÛ‡{VçHäHº“»ƒ§ßÜgd˾/M—í*ÖéÙÿKÛQ5ä–µ­i$Ý ¬)[Óü9Úz2J{é<ýåÃu³¿I׋//®GÖp³Ž³ÎÉ}”{$¬‹kÙRì³Ç¦’uÄ*ˆHÀõ(fü3‰ ÂÜwºV¿Ïµÿ9]»ß¯<)ý°fãÿ¬©±¢æ>`oW"i¶ì5¶â"šˆ¬æ«4¨xhgmpµ÷Ó<†I’NÜé ¹ž‡ 2¯.a¦íÕÅÉàÕùÅøêaæ> ÁDm¹ÇÕ§ VÒÛø)7¯Ü¶ÿaöñÛjÈ=(Ð?jþ^SŒá  ¡.`ÝeÿÉ}’ºw#þ‘róõ þ¨Ë9_ÆÒýXÞ Ç×?nÜžz—ó2)€†¦‚ïâI1ÃUŒž„‡$Ð)¨ÞãYÕHòÏ”r§+â!ÚÒÜLr/6#ñ¼T3¸¾:¿¸ÜÐÔ˜DØ®„Ì‹„ιۡg©ˆgE;ݰ!ÂC§&·Âþu}90ca~HŸ¯Gl+@-¿M|ç57Û-ÙŸëN€I]Ðû÷uA†Q1+{DZrDB«´SÊ0@Õp¡µA~*#šr| E×H@‰šÔE…hG¦Ž;iªý2ø=œ¬2?%ÖX úœƒtB)ú€âEîfyÊ)ã-tƒk£lˆPÉŸHÄP ÊŠy"M9׊|²-óê´½E3áà”Á1ƒ*Ÿ(áµÔ(˜10LÚEAjšU%$rÒ(£³øíþ{etqg^øäÊ|E;f¿]ôÝá¤s™Çø;f‡Tf~-¹#¤üÞ¢y鎔8i¦JåAïçÞTÀn&9î!ºÉèàèÆR>$õxrZYÌÀ2w£xBæv ü•lßZ¿žÐ¯Õ=¶­–•wZ6~Ô~þ´Ÿ·Ûxu£öš¼ÖäõÖ¤=[ö¬U«ŠwÜÑÎÿVk:Ï÷uà÷:°UîènùÝý sÁ×¾ÙãÆÕÒæÎóÝ®Õë õ?9m6Úpó3Nöòf]üiÞ㨠1êZÞ§±z]˪ˆ~„fëq9mcë‘Ëw.¬ñÒDmãîŠîWÌøÓn©°NW‹a¶ XËsµè`a» dZýàí}IÊ&”Ûä¯Èëȟo ÙÄ}³_æJ \*îhÞ»„͈ŸŸ ) }*HÞ´ç¸è £ôM»Û×,;æv¬ùüÊF{‚_`ƬÄâì7ÍÔëÔe&ž–97QÿydØ~ßnͨ1f¤1ëšuè¨Ò ÄG±eEÚí åÀŠŒºû=î­T¨É âTmZCŒVaC!§ŽC#ßèw·Û<[X’¯­,¸ªx¨Í>÷K-˜f»ûܨxÉJÈ’ÒAÑVÜÞÑÉ(ôÙ@Р:,ÏŽ— ãÊ4­ž!®øÛeßê÷ñaún¼¨'#ë™ßÙk]wÊ’Á× L›µ¡Ð`Öκg«. ½B9Ç«¬d˜³®x¸eãEpÓL§{˜¤ÅŒ¯§¢ƒþ6UÀ›ëÙzsÙ#±üÞ%¥–€$gùŒÒYd!Ÿ¦+´Î‘=[,ќҘ$«2¾Ò˜³Ñ ÃqZ æÈÅ›FܜёÑäÑÁѵ¿ó¶Äëð-]ñDBÍ«‹ó‹w£áÉÉèâlxøŽvq~uJP³ß³^tzýNï9†Bl†r!;™”¡ÂøƒÙr¼À°ÑÀ‹ðTc:Ó+ ]g&6L-Å£~R½'[z¤"nHr•>¶7&€cáÜY¨Õùl‘`›Ó§?b<ÂDp#”æ†{n$0ñ‚"å=ï wë}Гnq¿3«uÏAž`‰ÃÐ;>¥æëÁùq³Qdw¼À÷î‚lD 0˜•WF £rmÔBçdá-: âüSÁrüîõÅh<Þ>Õ_k?³ýW¥ WmÿÿP†Þ…Ü?Öð±˜?À⎎LJ—ÃÑÕðâõºÄµƒ«¦XLÓÔ‰°Y¥7ŠŸz¬U)‡Pˆ5Ï–…±s‚Ñ×U"ÜÀ ƒ?1l²©3ÔXÈÕšcL$¹à²ÌÃL‚ïÝzsK’0(¶>ñ ñáó’ªPþøíà|tv|ÏWu숚ÐîçjÜv þÒ_Wš7iG3r6¿FÖØö-CìÃèwEæv |wÍí&ô…S„ ŒÅ7ë½…é“¢ªÿ”1ÏåìíÇ2áïÙøw˜Å¨†ÿ‹Ñ¥ùw3úóÿ) ìNa4§ñ«ëŸï¥pÁ.‰]ƒ°¼xÄ=j’O¶>¡A`rmìWÂyQvGÄ/CT‹2¥òb3eµ ®¯N/.ïIª!áþ+‘©\Ò)Oû‰ˆ§yÛý5|9´;¡¼æxð¯ëËg~HÖýû(ƒ%ýcäsö¾“g3™š…ÿt",¯§ùBÄ´ÿ¡—…!÷IÓ•f~sOð7Ò4ŽÊŽ^¼/üìÏÅDam.4>>¦ÁÙøâ^û]bëí7”Ž·Ì³Ö.Zßê,q2,ÿ1º_qÖ0G£ÆXÚŸeY¢öºejA‚#tŽEüŸ« æÿ=ïYã¿”ö;Mtomoyo-tools/sbin/0000755000000000000000000000000014116520000013160 5ustar rootroottomoyo-tools/sbin/tomoyo-init.c0000644000000000000000000002723014116520000015617 0ustar rootroot/* * tomoyo-init.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2012 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is executed automatically by kernel * when execution of /sbin/init is requested. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define _FILE_OFFSET_BITS 64 #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include static void panic(void) { printf("Fatal error while loading policy.\n"); fflush(stdout); while (1) sleep(100); } #define policy_dir "/etc/tomoyo/" #define proc_manager "/sys/kernel/security/tomoyo/manager" #define proc_exception_policy "/sys/kernel/security/tomoyo/exception_policy" #define proc_domain_policy "/sys/kernel/security/tomoyo/domain_policy" #define proc_profile "/sys/kernel/security/tomoyo/profile" #define proc_stat "/sys/kernel/security/tomoyo/stat" static const char *profile_name = "default"; static _Bool ccs_noload = 0; static _Bool proc_unmount = 0; static _Bool sys_unmount = 0; static _Bool security_unmount = 0; static _Bool chdir_ok = 0; static struct ns_profile { char *namespace; _Bool profile[256]; } *ns_profile_list; static int ns_profile_list_len = 0; static char buffer[8192]; static void check_arg(const char *arg) { if (!strcmp(arg, "TOMOYO=ask")) profile_name = "ask"; else if (!strcmp(arg, "TOMOYO=default")) profile_name = "default"; else if (!strcmp(arg, "TOMOYO=disabled")) profile_name = "disable"; else if (!strncmp(arg, "TOMOYO=", 7)) { char buffer[1024]; memset(buffer, 0, sizeof(buffer)); snprintf(buffer, sizeof(buffer) - 1, "profile-%s.conf", arg + 7); profile_name = strdup(buffer); if (!profile_name) panic(); } else if (!strcmp(arg, "TOMOYO_NOLOAD")) ccs_noload = 1; } static void ask_profile(void) { static char input[128]; while (1) { char *ret_ignored; printf("TOMOYO: Select a profile from " "the following list.\n"); if (chdir_ok) { /* Show profiles in policy directory. */ DIR *dir = opendir("."); if (!access("profile.conf", R_OK)) printf("default\n"); while (1) { struct dirent *entry = readdir(dir); int len; char *name; if (!entry) break; name = entry->d_name; if (strncmp(name, "profile-", 8)) continue; if (!strcmp(name, "profile-default.conf") || !strcmp(name, "profile-disable.conf")) continue; len = strlen(name); if (len > 13 && !strcmp(name + len - 5, ".conf")) { int i; for (i = 8; i < len - 5; i++) putchar(name[i]); putchar('\n'); } } closedir(dir); } printf("disable\n"); profile_name = ""; printf("> "); memset(input, 0, sizeof(input)); ret_ignored = fgets(input, sizeof(input) - 1, stdin); { char *cp = strchr(input, '\n'); if (cp) *cp = '\0'; } if (chdir_ok) { if (!strcmp(input, "default")) { if (!access("profile.conf", R_OK)) { profile_name = "default"; break; } } else if (strcmp(input, "disable")) { memset(buffer, 0, sizeof(buffer)); snprintf(buffer, sizeof(buffer) - 1, "profile-%s.conf", input); if (!access(buffer, R_OK)) { profile_name = strdup(buffer); if (!profile_name) panic(); break; } } } if (!strcmp(input, "disable")) { profile_name = "disable"; break; } if (!strcmp(input, "TOMOYO_NOLOAD")) ccs_noload = 1; } } static void copy_files(const char *src, const char *dest) { int sfd; int dfd = open(dest, O_WRONLY); if (dfd == EOF) { if (errno != ENOENT) panic(); return; } sfd = open(src, O_RDONLY); if (sfd != EOF) { while (1) { int ret_ignored; int len = read(sfd, buffer, sizeof(buffer)); if (len <= 0) break; ret_ignored = write(dfd, buffer, len); } close(sfd); } close(dfd); } static void scan_used_profile_index(void) { static _Bool checked = 0; unsigned int i; FILE *fp; struct ns_profile *ptr = NULL; if (checked) return; checked = 1; fp = fopen(proc_domain_policy, "r"); if (!fp) panic(); while (memset(buffer, 0, sizeof(buffer)) && fgets(buffer, sizeof(buffer) - 1, fp)) { if (buffer[0] == '<') { char *cp = strchr(buffer, ' '); if (!cp) cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; ptr = NULL; for (i = 0; i < ns_profile_list_len; i++) { if (strcmp(buffer, ns_profile_list[i].namespace)) continue; ptr = &ns_profile_list[i]; break; } if (ptr) continue; ns_profile_list = realloc(ns_profile_list, sizeof(*ptr) * (ns_profile_list_len + 1)); if (!ns_profile_list) panic(); ptr = &ns_profile_list[ns_profile_list_len++]; ptr->namespace = strdup(buffer); if (!ptr->namespace) panic(); memset(ptr->profile, 0, sizeof(ptr->profile)); } else if (ptr && sscanf(buffer, "use_profile %u", &i) == 1 && i < 256) ptr->profile[i] = 1; } fclose(fp); } static void disable_profile(void) { FILE *fp_out = fopen(proc_profile, "w"); FILE *fp_in; int i; if (!fp_out) panic(); scan_used_profile_index(); for (i = 0; i < ns_profile_list_len; i++) { struct ns_profile *ptr = &ns_profile_list[i]; int j; for (j = 0; j < 256; j++) { if (!ptr->profile[j]) continue; fprintf(fp_out, "%s %u-COMMENT=disabled\n", ptr->namespace, j); } } fclose(fp_out); fp_in = fopen(proc_profile, "r"); fp_out = fopen(proc_profile, "w"); if (!fp_in || !fp_out) panic(); while (memset(buffer, 0, sizeof(buffer)) && fgets(buffer, sizeof(buffer) - 1, fp_in)) { char *cp = strstr(buffer, "={ mode="); if (!cp) continue; *(cp + 8) = '\0'; fprintf(fp_out, "%sdisabled }\n", buffer); } fclose(fp_in); fclose(fp_out); } /* * Upgrade from TOMOYO 2.5's profile (PROFILE_VERSION=20110903) to TOMOYO 2.6's * profile (PROFILE_VERSION=20150505) as needed. */ static void upgrade_profile(void) { _Bool in_section = 0; FILE *fp_out = fopen(proc_profile, "w"); FILE *fp_in = fopen(proc_profile, "r"); if (!fp_in || !fp_out) panic(); while (memset(buffer, 0, sizeof(buffer)) && fgets(buffer, sizeof(buffer) - 1, fp_in)) { char *cp; const char *ns = ""; unsigned int version; if (buffer[0] == '<') { cp = strchr(buffer, ' '); if (!cp) continue; *cp++ = '\0'; ns = buffer; } else cp = buffer; if (sscanf(cp, "PROFILE_VERSION=%u", &version) == 1) in_section = version == 20110903; if (in_section && sscanf(cp, "%u", &version) != 1) fprintf(fp_out, "%s PROFILE_VERSION=20150505\n", ns); } fclose(fp_in); fclose(fp_out); } static void show_domain_usage(void) { unsigned int domain = 0; unsigned int acl = 0; FILE *fp = fopen(proc_domain_policy, "r"); if (!fp) return; while (memset(buffer, 0, sizeof(buffer)) && fgets(buffer, sizeof(buffer) - 1, fp)) { if (buffer[0] == '<') domain++; else if (buffer[0] > ' ' && strncmp(buffer, "use_", 4)) acl++; } fclose(fp); printf("%u domain%s. %u ACL entr%s.\n", domain, domain > 1 ? "s" : "", acl, acl > 1 ? "ies" : "y"); } static void show_memory_usage(void) { FILE *fp = fopen(proc_stat, "r"); if (!fp) return; while (memset(buffer, 0, sizeof(buffer)) && fgets(buffer, sizeof(buffer) - 1, fp)) { unsigned int size; if (sscanf(buffer, "Memory used by policy: %u", &size) != 1) continue; printf("%u KB used by policy.\n", (size + 1023) / 1024); break; } fclose(fp); } int main(int argc, char *argv[]) { struct stat buf; /* Mount /proc if not mounted. */ if (lstat("/proc/self/", &buf) || !S_ISDIR(buf.st_mode)) proc_unmount = !mount("/proc", "/proc/", "proc", 0, NULL); /* Mount /sys if not mounted. */ if (lstat("/sys/kernel/security/", &buf) || !S_ISDIR(buf.st_mode)) sys_unmount = !mount("/sys", "/sys", "sysfs", 0, NULL); /* Mount /sys/kernel/security if not mounted. */ if (lstat("/sys/kernel/security/tomoyo/", &buf) || !S_ISDIR(buf.st_mode)) security_unmount = !mount("none", "/sys/kernel/security", "securityfs", 0, NULL); /* Unmount and exit if policy interface doesn't exist. */ if (lstat("/sys/kernel/security/tomoyo", &buf) || !S_ISDIR(buf.st_mode)) { if (security_unmount) umount("/sys/kernel/security/"); if (sys_unmount) umount("/sys/"); if (proc_unmount) umount("/proc/"); return 1; } /* * Open /dev/console if stdio are not connected. * * WARNING: Don't let this program be invoked implicitly * if you are not operating from console. * Otherwise, you will get unable to respond to prompt * if something went wrong. */ if (access("/proc/self/fd/0", R_OK)) { close(0); close(1); close(2); open("/dev/console", O_RDONLY); open("/dev/console", O_WRONLY); open("/dev/console", O_WRONLY); } /* Check /proc/cmdline and /proc/self/cmdline */ { char *cp; int i; int ret_ignored; int fd = open("/proc/cmdline", O_RDONLY); memset(buffer, 0, sizeof(buffer)); ret_ignored = read(fd, buffer, sizeof(buffer) - 1); close(fd); cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; while (1) { char *cp = strchr(buffer, ' '); if (cp) *cp = '\0'; check_arg(buffer); if (!cp) break; cp++; memmove(buffer, cp, strlen(cp) + 1); } for (i = 1; i < argc; i++) check_arg(argv[i]); } /* Does policy directory exist? */ if (!chdir(policy_dir)) chdir_ok = 1; else profile_name = "disable"; /* Does selected profile exist? */ if (chdir_ok) { if (!strcmp(profile_name, "default")) { if (access("profile.conf", R_OK)) { printf("TOMOYO: Default profile " "doesn't exist.\n"); profile_name = "ask"; } } else if (strcmp(profile_name, "ask") && strcmp(profile_name, "disable")) { if (access(profile_name, R_OK)) { printf("TOMOYO: Specified profile " "doesn't exist.\n"); profile_name = "ask"; } } } /* Show prompt if something went wrong or explicitly asked. */ if (!strcmp(profile_name, "ask")) ask_profile(); /* Load policy. */ if (chdir_ok) { copy_files("manager.conf", proc_manager); copy_files("exception_policy.conf", proc_exception_policy); if (!ccs_noload) copy_files("domain_policy.conf", proc_domain_policy); if (!strcmp(profile_name, "default")) copy_files("profile.conf", proc_profile); else if (strcmp(profile_name, "disable")) copy_files(profile_name, proc_profile); copy_files("stat.conf", proc_stat); upgrade_profile(); } /* Use disabled mode? */ if (!strcmp(profile_name, "disable")) disable_profile(); /* Do additional initialization. */ if (!access("/etc/tomoyo/tomoyo-post-init", X_OK)) { const pid_t pid = fork(); switch (pid) { case 0: execl("/etc/tomoyo/tomoyo-post-init", "/etc/tomoyo/tomoyo-post-init", NULL); _exit(0); case -1: panic(); } while (waitpid(pid, NULL, __WALL) == EOF && errno == EINTR); } show_domain_usage(); /* Show memory usage. */ show_memory_usage(); if (security_unmount) umount("/sys/kernel/security/"); if (sys_unmount) umount("/sys/"); if (proc_unmount) umount("/proc"); return 0; } tomoyo-tools/sbin/Makefile0000644000000000000000000000045114116520000014620 0ustar rootrootinclude ../Include.make BUILD_FILES = tomoyo-init all: $(BUILD_FILES) install: all mkdir -p -m 0755 $(INSTALLDIR)$(SBINDIR) $(INSTALL) -m 0700 $(BUILD_FILES) $(INSTALLDIR)$(SBINDIR) .c: $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< clean: rm -f -- $(BUILD_FILES) .PHONY: clean install tomoyo-tools/COPYING.tomoyo0000644000000000000000000004310314116520000014606 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. tomoyo-tools/Makefile0000644000000000000000000000070614116520000013670 0ustar rootrootall: $(MAKE) -C sbin all $(MAKE) -C usr_sbin all $(MAKE) -C usr_lib_tomoyo all install: all $(MAKE) -C sbin install $(MAKE) -C usr_sbin install $(MAKE) -C usr_lib_tomoyo install $(MAKE) -C usr_share_man install clean: ## ## I don't enable "find" line because older versions does not support -delete ## action. ## # find -name '*~' -delete $(MAKE) -C sbin clean $(MAKE) -C usr_sbin clean $(MAKE) -C usr_lib_tomoyo clean .PHONY: clean install tomoyo-tools/tomoyo-tools.spec0000644000000000000000000000533214116520000015570 0ustar rootrootSummary: Userspace tools for TOMOYO Linux 2.6.x Name: tomoyo-tools Version: 2.6.1 Release: 1 License: GPL Group: System Environment/Kernel ExclusiveOS: Linux Autoreqprov: no Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot ## ## This spec file is intended to be distribution independent. ## I don't enable "BuildRequires:" line because rpmbuild will fail on ## environments where packages are managed by (e.g.) apt. ## # BuildRequires: ncurses-devel Requires: ncurses Conflicts: tomoyo-tools < 2.6.1-1 Source0: https://osdn.dl.osdn.jp/tomoyo/70710/tomoyo-tools-2.6.1-20210910.tar.gz %description This package contains userspace tools for administrating TOMOYO Linux 2.6.x. Please see https://tomoyo.osdn.jp/2.6/ for documentation. %prep %setup -q -n tomoyo-tools %build make USRLIBDIR=%_libdir CFLAGS="-Wall $RPM_OPT_FLAGS" %install rm -rf $RPM_BUILD_ROOT make INSTALLDIR=$RPM_BUILD_ROOT USRLIBDIR=%_libdir install %clean rm -rf $RPM_BUILD_ROOT %post ldconfig || true %files %defattr(-,root,root) /sbin/* %_libdir/tomoyo/* %_libdir/libtomoyo* /usr/sbin/* /usr/share/man/man8/* %changelog * Fri Sep 10 2021 2.6.1-1 - Add -DNCURSES_WIDECHAR=0 to programs using ncurses library. ( https://lists.gnu.org/archive/html/bug-ncurses/2021-07/msg00021.html ) * Wed Nov 01 2020 2.6.0-3 - Loosen pathname/domainname validation. - Limit wildcard recursion depth. * Wed Jan 01 2020 2.6.0-2 - Remove "socket:[family=\\$:type=\\$:protocol=\\$]" from ANY_PATHNAME group. * Tue Mar 05 2019 2.6.0-1 - Adjust to TOMOYO 2.6. * Mon Jan 02 2017 2.5.0-9 - Rebase to ccs-tools 1.8.5-2. * Sun Jun 01 2014 2.5.0-8 - Let tomoyo-editpolicy print "acl_group $N" correctly when using offline mode. * Sun Jan 05 2014 2.5.0-7 - Let init_policy add path to systemd , as suggested by Shawn Landden. - Let tomoyo-queryd use poll() rather than select(). * Sat Apr 06 2013 2.5.0-6 - Fix compile warning from clang. * Thu Feb 14 2013 2.5.0-5 - Change Makefile's build flags, as suggested by Simon Ruderich and Hideki Yamane. (Debian bug 674723) - Change / to /* in rpm's %files section because Fedora 18 complains conflicts. * Sun Aug 05 2012 2.5.0-4 - Let tomoyo-checkpolicy handle namespace prefix in exception policy. - Rename manpage for init_policy to tomoyo_init_policy (to allow parallel installation of ccs-tools package). * Sat Apr 14 2012 2.5.0-3 - Let tomoyo-init parse statistics lines correctly. - Let tomoyo-editpolicy print number of selected entries if any. - Fix IP address parsing in ccs_parse_ip(). * Tue Oct 25 2011 2.5.0-2 - Let tomoyo-queryd use query id rather than global PID when reaching target process's domain policy. - Add "socket:[family=\\$:type=\\$:protocol=\\$]" to ANY_PATHNAME group. * Thu Sep 29 2011 2.5.0-1 - Major update release. tomoyo-tools/examples/0000755000000000000000000000000014116520000014043 5ustar rootroottomoyo-tools/examples/env_chk.c0000644000000000000000000000541414116520000015630 0ustar rootroot/* * env_chk.c * * An example program for execute_handler . * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include static void unescape(unsigned char *dest) { unsigned char *src = dest; unsigned char c; unsigned char d; unsigned char e; while (1) { c = *src++; if (!c) break; if (c != '\\') { *dest++ = c; continue; } c = *src++; if (c == '\\') { *dest++ = c; continue; } if (c < '0' || c > '3') break; d = *src++; if (d < '0' || d > '7') break; e = *src++; if (e < '0' || e > '7') break; *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); } *dest = '\0'; } int main(int raw_argc, char *raw_argv[]) { int i; int argc; int envc; char *filename; char **argv; char **envp; { /* Check that I'm an execute handler process. */ int fd = open("/sys/kernel/security/tomoyo/.execute_handler", 0); close(fd); if (fd == EOF) { fprintf(stderr, "FATAL: I'm not execute_handler.\n"); return 1; } } if (raw_argc < 7) return 1; filename = raw_argv[4]; argc = atoi(raw_argv[5]); envc = atoi(raw_argv[6]); if (raw_argc != argc + envc + 7) return 1; for (i = 5; i < argc + 5; i++) raw_argv[i] = raw_argv[i + 2]; raw_argv[argc + 5] = NULL; for (i = argc + 6; i < argc + envc + 6; i++) raw_argv[i] = raw_argv[i + 1]; raw_argv[argc + envc + 6] = NULL; argv = raw_argv + 5; envp = raw_argv + argc + 6; /* * "/usr/sbin/sshd" executes "/usr/sbin/sshd -R". * So, don't check environment variables. */ unescape(raw_argv[2]); if (argc == 2 && !strcmp(argv[1], "-R") && !strcmp(raw_argv[2], filename)) { execve(filename, argv, envp); return 1; } /* * Check environment variables passed to execve() request * and execute the program if it has "CERBERUS=sftp" environment. */ for (i = 0; i < envc; i++) { if (strcmp(envp[i], "CERBERUS=sftp")) continue; while (i < envc) { envp[i] = envp[i + 1]; i++; } execve(filename, argv, envp); break; } return 1; } tomoyo-tools/examples/falsh.c0000644000000000000000000001036114116520000015305 0ustar rootroot/* * falsh.c * * A tiny shell without built-in commands. * ( https://osdn.jp/projects/tomoyo/document/winf2005-en.pdf ) * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is intended to provide a login shell * to allow users do extra authentications (CERBERUS) safely. * Most shells contain built-in commands that allow attackers * do bad things (for example, terminate processes using "kill", * dull the response by an infinite loop using "for"). * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include static int do_shell(const char *commandline) { int status; int err; pid_t pid; struct sigaction sa; struct sigaction intr; struct sigaction quit; sigset_t omask; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); if (sigaction(SIGINT, &sa, &intr) < 0) goto out; if (sigaction(SIGQUIT, &sa, &quit) < 0) goto out_restore_sigint; sigaddset(&sa.sa_mask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask) == EOF) { sigaction(SIGQUIT, &quit, (struct sigaction *) NULL); out_restore_sigint: sigaction(SIGINT, &intr, (struct sigaction *) NULL); out: return -1; } pid = fork(); switch (pid) { wordexp_t p; case 0: sigaction(SIGINT, &intr, (struct sigaction *) NULL); sigaction(SIGQUIT, &quit, (struct sigaction *) NULL); sigprocmask(SIG_SETMASK, &omask, (sigset_t *) NULL); if (wordexp(commandline, &p, WRDE_NOCMD) == 0) { char **args = (char **) calloc(p.we_wordc + 1, sizeof(char *)); int i; for (i = 0; i < p.we_wordc; i++) args[i] = p.we_wordv[i]; execvp(args[0], args); err = errno; free(args); wordfree(&p); fprintf(stderr, "ERROR: Can't execute. %s : %s\n", commandline, strerror(err)); } else { fprintf(stderr, "ERROR: Can't parse. %s\n", commandline); err = EINVAL; } _exit(err); break; case -1: err = errno; fprintf(stderr, "ERROR: Can't fork. : %s\n", strerror(err)); status = -1; break; default: while (1) { err = waitpid(pid, &status, 0); if (err != EOF) break; if (errno != EINTR) break; } if (err != pid) status = -1; } sigaction(SIGINT, &intr, (struct sigaction *) NULL); sigaction(SIGQUIT, &quit, (struct sigaction *) NULL); sigprocmask(SIG_SETMASK, &omask, (sigset_t *) NULL); return status; } int main(int argc, char *argv[]) { static char buffer[1024]; static char hostname[1024]; static char cwd[1024]; struct passwd *pw = getpwuid(getuid()); char *line; int shelllevel = 0; char *ret_ignored; if (argc == 3 && !strcmp(argv[1], "-c")) return do_shell(argv[2]); else if (argc != 1) return 1; { const char *shlvl = getenv("SHLVL"); if (shlvl) shelllevel = atoi(shlvl); shelllevel++; memset(buffer, 0, sizeof(buffer)); snprintf(buffer, sizeof(buffer) - 1, "%d", shelllevel); setenv("SHLVL", buffer, 1); } setenv("SHELL", "/bin/sh", 1); memset(buffer, 0, sizeof(buffer)); memset(hostname, 0, sizeof(hostname)); memset(cwd, 0, sizeof(cwd)); ret_ignored = getcwd(cwd, sizeof(cwd) - 1); gethostname(hostname, sizeof(hostname) - 1); snprintf(buffer, sizeof(buffer) - 1, "[%s@%s %s (SHLVL=%d)]# ", pw ? pw->pw_name : "I have no name!", hostname, cwd, shelllevel); stifle_history(20); while (1) { line = readline(buffer); if (!line) break; if (*line) { add_history(line); do_shell(line); } free(line); line = NULL; } if (line) free(line); printf("\n"); return 0; } tomoyo-tools/examples/candy.c0000644000000000000000000000460014116520000015305 0ustar rootroot/* * candy.c * * An example program for CERBERUS. * ( https://osdn.jp/projects/tomoyo/document/winf2005-en.pdf ) * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include static const char *get_shell(void) { static char *shell = NULL; if (!shell) { struct passwd *pw = getpwuid(getuid()); shell = pw ? pw->pw_shell : "/bin/sh"; } return shell; } static int get_start_time(pid_t pid, unsigned long long *t) { FILE *fp; int i; char *cp; char buffer[1024]; char *ret_ignored; memset(buffer, 0, sizeof(buffer)); snprintf(buffer, sizeof(buffer) - 1, "/proc/%d/stat", pid); fp = fopen(buffer, "r"); if (!fp) return EOF; ret_ignored = fgets(buffer, sizeof(buffer) - 1, fp); fclose(fp); for (i = 0; i < 21; i++) { cp = strchr(buffer, ' '); if (!cp) return EOF; memmove(buffer, cp + 1, strlen(cp + 1) + 1); } cp = strchr(buffer, ' '); if (!cp) return EOF; *cp = '\0'; if (sscanf(buffer, "%llu", t) != 1) return EOF; return 0; } int main(int argc, char *argv[]) { static char buffer[1024]; static const char *passwd = "CERBERUS\n"; int trial; const char *shell = get_shell(); for (trial = 0; trial < 3; trial++) { char *ret_ignored; memset(buffer, 0, sizeof(buffer)); printf("Password: "); ret_ignored = fgets(buffer, sizeof(buffer) - 1, stdin); if (shell && !strcmp(buffer, passwd)) { unsigned long long t0; unsigned long long t1; if (get_start_time(getppid(), &t0) == 0 && get_start_time(getpid(), &t1) == 0) { /* 10 sec */ if ((t1 - t0) < 1000) execlp(shell, shell, NULL); } } sleep(3); } printf("Authentication Failure\n"); return 0; } tomoyo-tools/examples/checktoken.c0000644000000000000000000000343314116520000016330 0ustar rootroot/* * checktoken.c * * An example program for CERBERUS. * ( https://osdn.jp/projects/tomoyo/document/winf2005-en.pdf ) * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include static const char *get_shell(void) { static char *shell = NULL; if (!shell) { struct passwd *pw = getpwuid(getuid()); shell = pw ? pw->pw_shell : "/bin/sh"; } return shell; } int main(int argc, char *argv[]) { static char buffer[1024]; static char seed[40]; int i; int trial; const char *shell = get_shell(); srand(time(NULL) / 30); memset(seed, 0, sizeof(seed)); for (i = 0; i < sizeof(seed) - 1; i++) seed[i] = (rand() % 64) + 33; for (trial = 0; trial < 3; trial++) { char *dp; char *ret_ignored; memset(buffer, 0, sizeof(buffer)); printf("Password: "); ret_ignored = fgets(buffer, sizeof(buffer) - 1, stdin); dp = strchr(buffer, '\n'); if (dp) *dp = '\0'; if (!strcmp(buffer, seed)) { if (shell) execlp(shell, shell, NULL); } sleep(3); } printf("Authentication Failure\n"); return 0; } tomoyo-tools/examples/chaplet.c0000644000000000000000000000403014116520000015624 0ustar rootroot/* * chaplet.c * * An example program for CERBERUS. * ( https://osdn.jp/projects/tomoyo/document/winf2005-en.pdf ) * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include static const char *get_shell(void) { static char *shell = NULL; if (!shell) { struct passwd *pw = getpwuid(getuid()); shell = pw ? pw->pw_shell : "/bin/sh"; } return shell; } int main(int argc, char *argv[]) { static char buffer[1024]; static char seed[40]; int i; int trial; const char *shell = get_shell(); srand(time(NULL)); for (trial = 0; trial < 3; trial++) { char *sp; char *dp; char *ret_ignored; memset(seed, 0, sizeof(seed)); for (i = 0; i < sizeof(seed) - 1; i++) seed[i] = (rand() % 64) + 33; printf("Challenge: %s\n", seed); dp = seed; sp = dp; while (1) { char c = *sp; *dp = c; if (!c) break; if (*sp < '0' || *sp > '9') { sp++; continue; } sp++; dp++; } /* printf("Answer: %s\n", seed); */ memset(buffer, 0, sizeof(buffer)); printf("Response: "); ret_ignored = fgets(buffer, sizeof(buffer) - 1, stdin); dp = strchr(buffer, '\n'); if (dp) *dp = '\0'; if (!strcmp(buffer, seed)) { if (shell) execlp(shell, shell, NULL); } sleep(3); } printf("Authentication Failure\n"); return 0; } tomoyo-tools/examples/gettoken.c0000644000000000000000000000224414116520000016031 0ustar rootroot/* * gettoken.c * * An example program for CERBERUS. * ( https://osdn.jp/projects/tomoyo/document/winf2005-en.pdf ) * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include int main(int argc, char *argv[]) { static char seed[40]; int i; srand(time(NULL) / 30); memset(seed, 0, sizeof(seed)); for (i = 0; i < sizeof(seed) - 1; i++) seed[i] = (rand() % 64) + 33; printf("%s\n", seed); return 0; } tomoyo-tools/examples/timeauth.c0000644000000000000000000003425514116520000016040 0ustar rootroot/* * timeauth.c * * An example program for CERBERUS. * ( https://osdn.jp/projects/tomoyo/document/winf2005-en.pdf ) * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include static int str_starts(char *src, const char *find) { const int len = strlen(find); if (strncmp(src, find, len)) return 0; memmove(src, src + len, strlen(src + len) + 1); return 1; } static char *str_dup(const char *str) { char *cp = strdup(str); if (!cp) { fprintf(stderr, "Out of memory\n"); exit(1); } return cp; } static void unescape_line(unsigned char *buffer) { unsigned char *cp = buffer; unsigned char c; unsigned char d; unsigned char e; if (!cp) return; while (1) { c = *buffer++; if (!c) break; if (c != '\\') { *cp++ = c; continue; } c = *buffer++; if (c == '\\') { *cp++ = c; continue; } if (c < '0' || c > '3') break; d = *buffer++; if (d < '0' || d > '7') break; e = *buffer++; if (e < '0' || e > '7') break; *cp++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); } *cp = '\0'; } static void normalize_line(unsigned char *buffer) { unsigned char *sp = buffer; unsigned char *dp = buffer; int first = 1; while (*sp && (*sp <= 32 || 127 <= *sp)) sp++; while (*sp) { if (!first) *dp++ = ' '; first = 0; while (32 < *sp && *sp < 127) *dp++ = *sp++; while (*sp && (*sp <= 32 || 127 <= *sp)) sp++; } *dp = '\0'; } struct element { unsigned int key; unsigned int interval; unsigned int min_interval; unsigned int max_interval; }; #define PASSWORD_PROMPT "PASSWORD_PROMPT " #define MAX_TRIALS "MAX_TRIALS " #define SLEEP_ON_FAILURE "SLEEP_ON_FAILURE " #define MAX_TIMING_ERRORS "MAX_TIMING_ERRORS " #define EXEC_ON_SUCCESS "EXEC_ON_SUCCESS " #define AUTHENTICATION_DATA "AUTHENTICATION_DATA " #define SINGLE_FAILURE_MESSAGE "SINGLE_FAILURE_MESSAGE " #define COMPLETE_FAILURE_MESSAGE "COMPLETE_FAILURE_MESSAGE " static char *password_prompt = "Enter\\040password"; static unsigned int max_trials = 3; static unsigned int sleep_on_failure = 3; static unsigned int max_timing_errors = 0; static char *exec_on_success = "/bin/sh"; static char *authentication_data = NULL; static char *single_failure_message = "Incorrect\\040password."; static char *complete_failure_message = "Authentication\\040failed."; static struct element *authdata_list = NULL; static int authdata_list_len = 0; static void show_help(const char *argv0) { char *self = canonicalize_file_name(argv0); fprintf(stderr, "This is an interpreter for " "password-with-timing-information authentication " "script. To perform password-with-timing-information " "authentication, create a program like the following " "example.\n\n"); printf("#! %s\n" PASSWORD_PROMPT "%s\n" MAX_TRIALS "%d\n" SLEEP_ON_FAILURE "%d\n" MAX_TIMING_ERRORS "%d\n" EXEC_ON_SUCCESS "%s\n" AUTHENTICATION_DATA "1000-5000 p 1000-5000 a " "1000-5000 s 1000-5000 s 1000-5000 w 1000-5000 o " "1000-5000 r 1000-5000 d 1000-5000 \\012\n" SINGLE_FAILURE_MESSAGE "%s\n" COMPLETE_FAILURE_MESSAGE "%s\n", self, password_prompt, max_trials, sleep_on_failure, max_timing_errors, exec_on_success, single_failure_message, complete_failure_message); fprintf(stderr, "\n" PASSWORD_PROMPT "is a string to display before user's input.\n" MAX_TRIALS "is the maximal count the user can try.\n" SLEEP_ON_FAILURE "is the delay in second for each failure.\n" MAX_TIMING_ERRORS "is the acceptable limit for timing errors.\n" EXEC_ON_SUCCESS "is a program to execute when the " "authentication succeeded.\n" AUTHENTICATION_DATA "is an authentication data that are the " "repetition of minimal-interval and maximal-interval in " "milliseconds and character.\n" SINGLE_FAILURE_MESSAGE "is a string to display when the " "authentication failed for once.\n" COMPLETE_FAILURE_MESSAGE "is a string to display when the " "authentication failed for all trials.\n" "The above example requires the user to press 'password' + " "Enter with 1 second to 5 seconds interval for each key input." "\n\n" "Any character with value <= 0x20 or 0x7F " "<= values are regarded as a delimiter.\n" "You have to follow the character representation rule " "shown below.\n" " ASCII character (0x21 <= value <= 0x5B or 0x5D <= " "value <= 7E).\n" " \\\\ for \\ character (value = 0x5C).\n" " \\ooo style octal representation (value = any).\n" "You can obtain " AUTHENTICATION_DATA "interactively by " "executing '%s --make'\n", self); } static void make_mode(const char *argv0) { int trial; int pos; struct termios tp; struct termios tp0; tcgetattr(0, &tp0); tp = tp0; tp.c_lflag &= ~(ICANON | ECHO); tp.c_cc[VTIME] = 0; tp.c_cc[VMIN] = 1; fprintf(stderr, "You have chosen make mode, so I will " "generate script template for you.\n\n" "Before you start, you need to decide the password and " "acceptable interval.\n\n" "You need to type same password for three times. " "For the first time, type the password with acceptable " "slowest interval. " "For the second time, type the same password with " "acceptable fastest interval. " "The third time is for your practice to confirm you " "can type with interval between the slowest and the " "fastest.\n" "If you typed the same password for three times with " "appropriate interval, the script will be printed to " "stdout.\n\n"); retry3: fprintf(stderr, "Press Enter to start."); getchar(); trial = 0; retry: switch (trial) { case 0: fprintf(stderr, "RECORD %d: Enter password with acceptable " "slowest interval.\n", trial); break; case 1: fprintf(stderr, "RECORD %d: Enter the same password with " "acceptable fastest interval.\n", trial); break; default: fprintf(stderr, "VERIFY: Enter the same password with between " "slowest and fastest.\n"); break; } pos = 0; tcsetattr(0, TCSANOW, &tp); while (1) { int key; int interval; struct timeval tv0; struct timeval tv1; struct timezone tz; gettimeofday(&tv0, &tz); key = getc(stdin); gettimeofday(&tv1, &tz); #if 1 fputc(key, stderr); #else if (key != '\n') fputc('*', stderr); else fputc('\n', stderr); #endif interval = (tv1.tv_sec - tv0.tv_sec) * 1000 + (tv1.tv_usec - tv0.tv_usec) / 1000; if (trial == 0) { authdata_list_len = pos + 1; authdata_list = (struct element *) realloc(authdata_list, sizeof(struct element) * authdata_list_len); if (!authdata_list) { tcsetattr(0, TCSANOW, &tp0); fprintf(stderr, "Out of memory.\n"); exit(1); } authdata_list[pos].key = key; } else { if (pos >= authdata_list_len || authdata_list[pos].key != key) goto out; } if (trial == 0) authdata_list[pos++].max_interval = interval; else if (trial == 1) authdata_list[pos++].min_interval = interval; else authdata_list[pos++].interval = interval; if (key != '\n') continue; if (pos != authdata_list_len) goto out; if (trial < 2) break; for (pos = 0; pos < authdata_list_len; pos++) { if (authdata_list[pos].interval >= authdata_list[pos].min_interval || authdata_list[pos].interval <= authdata_list[pos].max_interval) continue; tcsetattr(0, TCSANOW, &tp0); if (0) { fprintf(stderr, "Oops! You didn't hit within " "acceptable speed. (%d<=%d<=%d) " "Restarting from the begininng.\n", authdata_list[pos].min_interval, authdata_list[pos].interval, authdata_list[pos].max_interval); goto retry3; } else { fprintf(stderr, "Oops! You didn't hit within " "acceptable speed. (%d<=%d<=%d) " "Incremented " MAX_TIMING_ERRORS ".\n", authdata_list[pos].min_interval, authdata_list[pos].interval, authdata_list[pos].max_interval); max_timing_errors++; } } break; } tcsetattr(0, TCSANOW, &tp0); if (trial < 2) { trial++; goto retry; } { int i; char *self = canonicalize_file_name(argv0); exec_on_success = getenv("SHELL"); if (!exec_on_success) exec_on_success = "/bin/sh"; fprintf(stderr, "Printing the content of " "authentication script.\n"); printf("#! %s\n", self); printf(PASSWORD_PROMPT "%s\n", password_prompt); printf(MAX_TRIALS "%d\n", max_trials); printf(SLEEP_ON_FAILURE "%d\n", sleep_on_failure); printf(MAX_TIMING_ERRORS "%d\n", max_timing_errors); printf(EXEC_ON_SUCCESS "%s\n", exec_on_success); printf(AUTHENTICATION_DATA); for (i = 0; i < authdata_list_len; i++) { unsigned char c = authdata_list[i].key; printf("%u-%u ", authdata_list[i].min_interval, authdata_list[i].max_interval); if (c == '\\') printf("\\\\ "); else if (c > 32 && c < 127) printf("%c ", c); else printf("\\%03o ", c); } printf("\n"); printf(SINGLE_FAILURE_MESSAGE "%s\n", single_failure_message); printf(COMPLETE_FAILURE_MESSAGE "%s\n", complete_failure_message); } return; out: tcsetattr(0, TCSANOW, &tp0); fprintf(stderr, "Oops! You didn't hit the same key. Restarting this " "trial.\n"); goto retry; } static int parse_script(const char *argv1) { char buffer[8192]; FILE *fp = fopen(argv1, "r"); if (!fp) { fprintf(stderr, "Can't open %s\n", argv1); return 1; } while (memset(buffer, 0, sizeof(buffer)), fgets(buffer, sizeof(buffer) - 1, fp)) { char *cp = strchr(buffer, '\n'); unsigned int v; if (*cp) { *cp = '\0'; } else if (!feof(fp)) { fprintf(stderr, "Line too long.\n"); fclose(fp); return 1; } normalize_line(buffer); if (str_starts(buffer, PASSWORD_PROMPT)) password_prompt = str_dup(buffer); else if (sscanf(buffer, MAX_TRIALS "%u", &v) == 1) max_trials = v; else if (sscanf(buffer, SLEEP_ON_FAILURE "%u", &v) == 1) sleep_on_failure = v; else if (sscanf(buffer, MAX_TIMING_ERRORS "%u", &v) == 1) max_timing_errors = v; else if (str_starts(buffer, EXEC_ON_SUCCESS)) exec_on_success = str_dup(buffer); else if (str_starts(buffer, AUTHENTICATION_DATA)) authentication_data = str_dup(buffer); else if (str_starts(buffer, SINGLE_FAILURE_MESSAGE)) single_failure_message = str_dup(buffer); else if (str_starts(buffer, COMPLETE_FAILURE_MESSAGE)) complete_failure_message = str_dup(buffer); } fclose(fp); if (!authentication_data) { fprintf(stderr, "No authentication data found.\n"); return 1; } password_prompt = str_dup(password_prompt); unescape_line(password_prompt); exec_on_success = str_dup(exec_on_success); unescape_line(exec_on_success); single_failure_message = str_dup(single_failure_message); unescape_line(single_failure_message); complete_failure_message = str_dup(complete_failure_message); unescape_line(complete_failure_message); { char *cp = strtok(authentication_data, " "); while (cp) { unsigned int a; unsigned int b; unsigned int c; unsigned int v; if (sscanf(cp, "%u-%u", &a, &b) != 2) break; cp = strtok(NULL, " "); if (!cp) break; if (!strcmp(cp, "\\\\")) { c = '\\'; } else if (sscanf(cp, "\\%o", &v) == 1) { c = v; } else { v = *(unsigned char *) cp; if (v >= 0x21 && v <= 0x7E && v != 0x5c) c = v; else break; } authdata_list = (struct element *) realloc(authdata_list, sizeof(struct element) * (authdata_list_len + 1)); if (!authdata_list) break; authdata_list[authdata_list_len].min_interval = a; authdata_list[authdata_list_len].max_interval = b; authdata_list[authdata_list_len++].key = c; cp = strtok(NULL, " "); } if (!authdata_list) { fprintf(stderr, "No authentication data found.\n"); return 1; } free(authentication_data); authentication_data = NULL; } return 0; } static int do_auth(void) { int trial; struct termios tp; struct termios tp0; tcgetattr(0, &tp0); tp = tp0; tp.c_lflag &= ~(ICANON | ECHO); tp.c_cc[VTIME] = 0; tp.c_cc[VMIN] = 1; for (trial = 0; trial < max_trials; trial++) { int errors = 0; int pos = 0; int failed = 0; printf("%s: ", password_prompt); tcsetattr(0, TCSANOW, &tp); while (1) { int key; int interval; struct timeval tv0; struct timeval tv1; struct timezone tz; gettimeofday(&tv0, &tz); key = getc(stdin); gettimeofday(&tv1, &tz); #if 1 fputc(key, stderr); #else if (key != '\n') putchar('*'); else putchar('\n'); #endif interval = (tv1.tv_sec - tv0.tv_sec) * 1000 + (tv1.tv_usec - tv0.tv_usec) / 1000; if (!failed && pos < authdata_list_len) { if (authdata_list[pos].key != key) { failed = 1; } else if (interval < authdata_list[pos].min_interval || interval > authdata_list[pos].max_interval) { if (++errors > max_timing_errors) failed = 1; } } else { failed = 1; } pos++; if (key == '\n') break; } tcsetattr(0, TCSANOW, &tp0); if (pos == authdata_list_len && !failed) { execlp(exec_on_success, exec_on_success, NULL); fprintf(stderr, "Can't execute %s\n", exec_on_success); exit(1); } printf("%s\n", single_failure_message); sleep(sleep_on_failure); } printf("%s\n", complete_failure_message); return 1; } int main(int argc, char *argv[]) { if (argc != 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { show_help(argv[0]); return 1; } if (!strcmp(argv[1], "--make")) { make_mode(argv[0]); return 0; } if (parse_script(argv[1])) return 1; return do_auth(); } tomoyo-tools/examples/proxy.c0000644000000000000000000001317214116520000015374 0ustar rootroot/* * proxy.c * * Binds to local port explicitly before forwarding TCP connections. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This tool is intended to limit local port numbers that clients * will use when connecting to servers, so that servers can enforce * client's port number based access control. * * To compile this program run the following command * * gcc -Wall -O3 -o path_to_output proxy.c * chown 0:0 path_to_output * * where the path_to_output is the location you want to place the binary. * * To use this program for ssh, create a file named "useproxy" * containing a line * * ProxyCommand path_to_output %h %p min_port max_port * * where the min_port and max_port are the local port range * the sshd will accept. * * Use -F option like * * ssh -F useproxy example.com * * when you run ssh. * * You may append to ~/.ssh/config or /etc/ssh/ssh_config * if you want to use this tool by default. * * You need to turn SUID bit on (or give CAP_NET_BIND_SERVICE capability) like * * chmod 4755 path_to_output * * if you want to allow non root user to use local port less than 1024. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static in_addr_t get_host_by_name_alias(const char *strHostName) { in_addr_t IP; IP = inet_addr(strHostName); if (IP == INADDR_NONE) { struct hostent *hp = gethostbyname(strHostName); if (hp) IP = *(in_addr_t *) hp->h_addr_list[0]; } return IP; } int main(int argc, char *argv[]) { const int remote = socket(PF_INET, SOCK_STREAM, 0); unsigned int port; struct sockaddr_in addr; in_addr_t forward_connect_ip = INADDR_NONE; unsigned short int forward_connect_port = 0; unsigned short int forward_bind_port_min = 0; unsigned short int forward_bind_port_max = 0; if (argc != 5) { fprintf(stderr, "Usage: %s forward_connect_host " "forward_connect_port forward_bind_port_min " "forward_bind_port_max\n", argv[0]); return 1; } forward_bind_port_min = atoi(argv[3]); forward_bind_port_max = atoi(argv[4]); for (port = forward_bind_port_min; port <= forward_bind_port_max; port++) { addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(port); if (!bind(remote, (struct sockaddr *) &addr, sizeof(addr))) break; } if (port > forward_bind_port_max) { fprintf(stderr, "ERROR: No local ports available.\n"); return 1; } { /* Drop root privileges. */ const gid_t gid = -1; setgroups(1, &gid); setgid(-1); setuid(-1); } forward_connect_ip = ntohl(get_host_by_name_alias(argv[1])); forward_connect_port = atoi(argv[2]); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(forward_connect_ip); addr.sin_port = htons(forward_connect_port); if (connect(remote, (struct sockaddr *) &addr, sizeof(addr)) || fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK) || fcntl(1, F_SETFL, fcntl(1, F_GETFL) | O_NONBLOCK) || fcntl(remote, F_SETFL, fcntl(remote, F_GETFL) | O_NONBLOCK)) { fprintf(stderr, "ERROR: Connecting to %u.%u.%u.%u : %s\n", (unsigned char) (forward_connect_ip >> 24), (unsigned char) (forward_connect_ip >> 16), (unsigned char) (forward_connect_ip >> 8), (unsigned char) forward_connect_ip, strerror(errno)); return 1; } while (1) { fd_set rfds; fd_set wfds; static char local_buf[4096]; static char remote_buf[4096]; static int local_len = 0; static int remote_len = 0; static int local_eof = 0; static int remote_eof = 0; int len; FD_ZERO(&rfds); FD_ZERO(&wfds); if (local_eof == 0 && local_len < sizeof(local_buf)) FD_SET(0, &rfds); if (remote_eof == 0 && remote_len < sizeof(remote_buf)) FD_SET(remote, &rfds); if (local_len) FD_SET(remote, &wfds); if (remote_len) FD_SET(1, &wfds); select(remote + 1, &rfds, &wfds, NULL, NULL); if (FD_ISSET(0, &rfds)) { len = read(0, local_buf + local_len, sizeof(local_buf) - local_len); if (len > 0) local_len += len; else if (len == 0) local_eof = 1; } if (FD_ISSET(remote, &rfds)) { len = read(remote, remote_buf + remote_len, sizeof(remote_buf) - remote_len); if (len > 0) remote_len += len; else if (len == 0) remote_eof = 1; } if (FD_ISSET(remote, &wfds)) { len = write(remote, local_buf, local_len); if (len > 0) { local_len -= len; memmove(local_buf, local_buf + len, local_len); } } if (FD_ISSET(1, &wfds)) { len = write(1, remote_buf, remote_len); if (len > 0) { remote_len -= len; memmove(remote_buf, remote_buf + len, remote_len); } } if (local_len == 0 && local_eof == 1) { shutdown(remote, SHUT_WR); local_eof = 2; } if (remote_len == 0 && remote_eof == 1) { shutdown(1, SHUT_WR); remote_eof = 2; } if (local_eof == 2 && remote_eof == 2) break; } return 0; } tomoyo-tools/examples/honey.c0000644000000000000000000000552314116520000015336 0ustar rootroot/* * honey.c * * An example program for CERBERUS. * ( https://osdn.jp/projects/tomoyo/document/winf2005-en.pdf ) * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include static const char *get_shell(void) { static char *shell = NULL; if (!shell) { struct passwd *pw = getpwuid(getuid()); shell = pw ? pw->pw_shell : "/bin/sh"; } return shell; } #define MAX_PASSWORD_LEN 10 #define PASSWORD_LEN 7 int main(int argc, char *argv[]) { static struct timeval tv[MAX_PASSWORD_LEN+1]; static int buffer[MAX_PASSWORD_LEN]; static const long min_interval[PASSWORD_LEN] = { 1000, 1000, 100, 100, 100, 100, 2000 }; static const long max_interval[PASSWORD_LEN] = { 20000, 20000, 10000, 10000, 5000, 5000, 3000 }; static const int password[PASSWORD_LEN] = { 'l', 'c', '2', '0', '0', '5', '\n' }; struct termios tp; struct termios tp0; int i = 0; struct timezone tz; int trial; const char *shell = get_shell(); tcgetattr(0, &tp0); tp = tp0; tp.c_lflag &= ~ICANON; tp.c_cc[VTIME] = 0; tp.c_cc[VMIN] = 1; for (trial = 0; trial < 3; trial++) { memset(tv, 0, sizeof(tv)); memset(buffer, 0, sizeof(buffer)); printf("Password: "); gettimeofday(&tv[0], &tz); tcsetattr(0, TCSANOW, &tp); for (i = 0; i < MAX_PASSWORD_LEN; i++) { buffer[i] = getc(stdin); gettimeofday(&tv[i+1], &tz); if (buffer[i] == '\n') break; } tcsetattr(0, TCSANOW, &tp0); if (i == PASSWORD_LEN - 1) { for (i = 0; i < PASSWORD_LEN; i++) { long diff = (tv[i+1].tv_sec - tv[i].tv_sec) * 1000 + (tv[i+1].tv_usec - tv[i].tv_usec) / 1000; if (diff < min_interval[i] || diff > max_interval[i]) { /* printf("Wrong interval %lu <= %lu " "<= %lu for %d\n", min_interval[i], diff, max_interval[i], i); */ break; } else if (password[i] != buffer[i]) { /* printf("Wrong password\n"); */ break; } } if (i == PASSWORD_LEN && shell) execlp(shell, shell, NULL); } sleep(3); } printf("Authentication Failure\n"); return 0; } tomoyo-tools/examples/mailauth.c0000644000000000000000000001760114116520000016020 0ustar rootroot/* * mailauth.c * * An example program for CERBERUS. * ( https://osdn.jp/projects/tomoyo/document/winf2005-en.pdf ) * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include static int str_starts(char *src, const char *find) { const int len = strlen(find); if (strncmp(src, find, len)) return 0; memmove(src, src + len, strlen(src + len) + 1); return 1; } static char *str_dup(const char *str) { char *cp = strdup(str); if (!cp) { fprintf(stderr, "Out of memory\n"); exit(1); } return cp; } static void unescape_line(unsigned char *buffer) { unsigned char *cp = buffer; unsigned char c; unsigned char d; unsigned char e; if (!cp) return; while (1) { c = *buffer++; if (!c) break; if (c != '\\') { *cp++ = c; continue; } c = *buffer++; if (c == '\\') { *cp++ = c; continue; } if (c < '0' || c > '3') break; d = *buffer++; if (d < '0' || d > '7') break; e = *buffer++; if (e < '0' || e > '7') break; *cp++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); } *cp = '\0'; } static void normalize_line(unsigned char *buffer) { unsigned char *sp = buffer; unsigned char *dp = buffer; int first = 1; while (*sp && (*sp <= 32 || 127 <= *sp)) sp++; while (*sp) { if (!first) *dp++ = ' '; first = 0; while (32 < *sp && *sp < 127) *dp++ = *sp++; while (*sp && (*sp <= 32 || 127 <= *sp)) sp++; } *dp = '\0'; } #define PASSWORD_PROMPT "PASSWORD_PROMPT " #define MAX_TRIALS "MAX_TRIALS " #define SLEEP_ON_FAILURE "SLEEP_ON_FAILURE " #define EXEC_ON_SUCCESS "EXEC_ON_SUCCESS " #define MAIL_COMMAND "MAIL_COMMAND " #define SINGLE_FAILURE_MESSAGE "SINGLE_FAILURE_MESSAGE " #define COMPLETE_FAILURE_MESSAGE "COMPLETE_FAILURE_MESSAGE " static char *password_prompt = "Enter\\040password"; static unsigned int max_trials = 3; static unsigned int sleep_on_failure = 3; static char *exec_on_success = "/bin/sh"; static char *mail_command = NULL; static char *single_failure_message = "Incorrect\\040password."; static char *complete_failure_message = "Authentication\\040failed."; static void show_help(const char *argv0) { char *self = canonicalize_file_name(argv0); fprintf(stderr, "This is an interpreter for one-time-password-using-mail " "authentication script. To perform " "one-time-password-using-mail authentication, create a program " "like the following example.\n\n"); printf("#! %s\n" PASSWORD_PROMPT "%s\n" MAX_TRIALS "%d\n" SLEEP_ON_FAILURE "%d\n" EXEC_ON_SUCCESS "%s\n" MAIL_COMMAND "%s\n" SINGLE_FAILURE_MESSAGE "%s\n" COMPLETE_FAILURE_MESSAGE "%s\n", self, password_prompt, max_trials, sleep_on_failure, exec_on_success, "curl --data-binary @- https://your.server/path_to_cgi", single_failure_message, complete_failure_message); fprintf(stderr, "\n" PASSWORD_PROMPT "is a string to display before user's input.\n" MAX_TRIALS "is the maximal count the user can try.\n" SLEEP_ON_FAILURE "is the delay in second for each failure.\n" EXEC_ON_SUCCESS "is a program to execute when the " "authentication succeeded.\n" MAIL_COMMAND "is the command line to notify password.\n" SINGLE_FAILURE_MESSAGE "is a string to display when " "the authentication failed for once.\n" COMPLETE_FAILURE_MESSAGE "is a string to display when " "the authentication failed for all trials.\n" "The above example sends password to " "https://your.server/path_to_cgi using curl.\n" "Any character with value <= 0x20 or 0x7F <= values " "are regarded as a delimiter.\n" "You have to follow the character representation rule shown " "below.\n" " ASCII character (0x21 <= value <= 0x5B or 0x5D <= value " "<= 7E).\n" " \\\\ for \\ character (value = 0x5C).\n" " \\ooo style octal representation (value = any).\n" "The line "MAIL_COMMAND "is passed to system(), so " "escape appropriately as needed.\n"); } static int parse_script(const char *argv1) { char buffer[8192]; FILE *fp = fopen(argv1, "r"); if (!fp) { fprintf(stderr, "Can't open %s\n", argv1); return 1; } while (memset(buffer, 0, sizeof(buffer)), fgets(buffer, sizeof(buffer) - 1, fp)) { char *cp = strchr(buffer, '\n'); unsigned int v; if (*cp) { *cp = '\0'; } else if (!feof(fp)) { fprintf(stderr, "Line too long.\n"); fclose(fp); return 1; } normalize_line(buffer); if (str_starts(buffer, PASSWORD_PROMPT)) password_prompt = str_dup(buffer); else if (sscanf(buffer, MAX_TRIALS "%u", &v) == 1) max_trials = v; else if (sscanf(buffer, SLEEP_ON_FAILURE "%u", &v) == 1) sleep_on_failure = v; else if (str_starts(buffer, EXEC_ON_SUCCESS)) exec_on_success = str_dup(buffer); else if (str_starts(buffer, MAIL_COMMAND)) mail_command = str_dup(buffer); else if (str_starts(buffer, SINGLE_FAILURE_MESSAGE)) single_failure_message = str_dup(buffer); else if (str_starts(buffer, COMPLETE_FAILURE_MESSAGE)) complete_failure_message = str_dup(buffer); } fclose(fp); if (!mail_command) { fprintf(stderr, "No mail command found.\n"); return 1; } password_prompt = str_dup(password_prompt); unescape_line(password_prompt); exec_on_success = str_dup(exec_on_success); unescape_line(exec_on_success); single_failure_message = str_dup(single_failure_message); unescape_line(single_failure_message); complete_failure_message = str_dup(complete_failure_message); unescape_line(complete_failure_message); return 0; } static int do_auth(void) { char password[17]; char buffer[8192]; { /* Create password. */ int i = 0; FILE *fp = fopen("/dev/urandom", "r"); if (!fp) { fprintf(stderr, "Can't open /dev/urandom .\n"); return 1; } memset(password, 0, sizeof(password)); while (i < sizeof(password) - 1) { const unsigned int c = fgetc(fp); if (c < 10) password[i++] = c + '0'; } fclose(fp); } { /* Send password. */ FILE *fp = popen(mail_command, "w"); if (!fp) { fprintf(stderr, "Can't send mail\n"); return 1; } fprintf(fp, "%s\n", password); pclose(fp); } /* fprintf(stderr, "%s\n", password); */ { /* Check password. */ int trial; for (trial = 0; trial < max_trials; trial++) { char *cp; char *ret_ignored; printf("%s\n", password_prompt); memset(buffer, 0, sizeof(buffer)); ret_ignored = fgets(buffer, sizeof(buffer) - 1, stdin); cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; if (!strcmp(buffer, password)) { execlp(exec_on_success, exec_on_success, NULL); fprintf(stderr, "Can't execute %s\n", exec_on_success); exit(1); } printf("%s\n", single_failure_message); sleep(sleep_on_failure); } } printf("%s\n", complete_failure_message); return 1; } int main(int argc, char *argv[]) { unsetenv("SHELLOPTS"); /* Make sure popen() executes MAIL_COMMAND */ if (argc != 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { show_help(argv[0]); return 1; } if (parse_script(argv[1])) return 1; return do_auth(); } tomoyo-tools/examples/groovy.c0000644000000000000000000000334214116520000015536 0ustar rootroot/* * groovy.c * * An example program for CERBERUS. * ( https://osdn.jp/projects/tomoyo/document/winf2005-en.pdf ) * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include static const char *get_shell(void) { static char *shell = NULL; if (!shell) { struct passwd *pw = getpwuid(getuid()); shell = pw ? pw->pw_shell : "/bin/sh"; } return shell; } int main(int argc, char *argv[]) { static char buffer[1024]; static const char *lockfile = "/tmp/.lockme"; int trial; const char *shell = get_shell(); for (trial = 0; trial < 3; trial++) { char *ret_ignored; memset(buffer, 0, sizeof(buffer)); printf("Password: "); ret_ignored = fgets(buffer, sizeof(buffer) - 1, stdin); if (shell) { int fd; fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd != EOF) { close(fd); execlp(shell, shell, NULL); } } sleep(3); } printf("Authentication Failure\n"); return 0; } tomoyo-tools/examples/statvfs.c0000644000000000000000000000177414116520000015712 0ustar rootroot/* * statvfs.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define _FILE_OFFSET_BITS 64 #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #include #include int main(int argc, char *argv[]) { struct statfs buf; if (statfs(argv[1], &buf)) return 1; printf("0x%08X\n", buf.f_type); return 0; } tomoyo-tools/examples/from-where.c0000644000000000000000000000754014116520000016270 0ustar rootroot/* * from-where.c * * TOMOYO Linux's utilities. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include int main(int argc, char *argv[]) { static char buffer[8192]; static chat buffer2[8192]; pid_t pid; int i; int j; int k; unsigned int inode; const int debug = 0; struct address_entry { unsigned int inode; char *address; } *address_list = NULL; int address_list_len = 0; FILE *fp; memset(buffer, 0, sizeof(buffer)); memset(buffer2, 0, sizeof(buffer2)); if (argc == 2 && !strcmp(argv[1], "--help")) { printf("Usage: %s [exclude1 [exclude2 [...]]]\n\n", argv[0]); printf("This utility reports remote IP address and remote port " "number pair of TCP sockets that are connected with" " this process.\n"); printf("You can use exclude list for excluding actively opened " "TCP connections because servers likely use constant " "address while clients unlikely do so.\n"); printf("An exclude entry is in ip_address:port form " "(e.g. 127.0.0.1:22 ).\n"); printf("You need to set SUID bit if you want to allow non-root " "users to use this utility.\n"); return 0; } /* Get "inode" and "remotehost:remoteport" pairs. */ fp = popen("netstat -nte", "r"); if (!fp) { printf("Can't execute netstat\n"); goto out; } while (fgets(buffer, sizeof(buffer) - 1, fp)) { struct address_entry *tmp; if (sscanf(buffer, "tcp %*u %*u %*s %128s %*s %*u %u", buffer2, &inode) != 2) continue; tmp = (struct address_entry *) realloc(address_list, sizeof(struct address_entry) * (address_list_len + 1)); if (!tmp) break; address_list = tmp; tmp[address_list_len].inode = inode; tmp[address_list_len].address = strdup(buffer2); if (!tmp[address_list_len].address) break; address_list_len++; } pclose(fp); /* Run process traversal starting from parent. */ pid = getppid(); repeat: for (i = 0; i < 1024; i++) { snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/fd/%d", pid, i); if (readlink(buffer, buffer2, sizeof(buffer2) - 1) <= 0) continue; if (debug) printf("Reading %s\n", buffer); if (sscanf(buffer2, "socket:[%u]", &inode) != 1) continue; if (debug) printf("inode=%u\n", inode); for (j = 0; j < address_list_len; j++) { if (address_list[j].inode != inode) continue; /* * Ignore some specific connections which are * identified by constant "remotehost:remoteport" pair * (e.g. "127.0.0.1:5432" for connection to DMBS * server). */ for (k = 1; k < argc; k++) { if (!strcmp(address_list[j].address, argv[k])) break; } if (k == argc) printf("%s\n", address_list[j].address); } } snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/status", pid); /* Get parent process. */ fp = fopen(buffer, "r"); if (!fp) goto out; if (debug) printf("Reading %s\n", buffer); while (fgets(buffer, sizeof(buffer) - 1, fp)) { if (sscanf(buffer, "PPid: %u", &pid) != 1) continue; fclose(fp); if (debug) printf("PPID=%u\n", pid); goto repeat; } fclose(fp); out: /* No more parents exist or an error (e.g. ENOENT or EPERM) occurred. */ if (debug) printf("Done\n"); return 0; } tomoyo-tools/Include.make0000644000000000000000000000030514116520000014445 0ustar rootrootINSTALL := install SBINDIR := /sbin USRSBINDIR := /usr/sbin USRLIBDIR := /usr/lib MAN8 := /usr/share/man/man8 ifndef CFLAGS CFLAGS := -Wall -O2 endif tomoyo-tools/kernel_test/0000755000000000000000000000000014116520000014544 5ustar rootroottomoyo-tools/kernel_test/tomoyo_file_test.c0000644000000000000000000002157614116520000020307 0ustar rootroot/* * tomoyo_file_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" #include static void make_elf_lib(void) { static const struct elf32_phdr eph = { .p_type = PT_LOAD, .p_offset = 4096, .p_filesz = 1, }; static const struct elf32_hdr eh = { .e_ident = ELFMAG, .e_type = ET_EXEC, .e_machine = EM_386, .e_phoff = sizeof(eh), .e_phentsize = sizeof(eph), .e_phnum = 1, }; const int fd = open("/tmp/uselib", O_WRONLY | O_CREAT | O_TRUNC, 0755); if (fd != EOF) { write(fd, &eh, sizeof(eh)); write(fd, &eph, sizeof(eph)); lseek(fd, 4096, SEEK_SET); write(fd, "", 1); close(fd); } } static int should_fail = 0; static void show_prompt(const char *str) { printf("Testing %35s: (%s) ", str, should_fail ? "must fail" : "should success"); errno = 0; } static void show_result(int result) { if (should_fail) { if (result == EOF) { if (errno == EPERM) printf("OK: Permission denied.\n"); else printf("FAILED: %s\n", strerror(errno)); } else { printf("BUG!\n"); } } else { if (result != EOF) printf("OK\n"); else printf("%s\n", strerror(errno)); } } static const char *dev_null_path = "/dev/null"; static const char *truncate_path = "/tmp/truncate_test"; static const char *ftruncate_path = "/tmp/ftruncate_test"; static const char *open_creat_path = "/tmp/open_test"; static const char *mknod_reg_path = "/tmp/mknod_reg_test"; static const char *mknod_chr_path = "/tmp/mknod_chr_test"; static const char *mknod_blk_path = "/tmp/mknod_blk_test"; static const char *mknod_fifo_path = "/tmp/mknod_fifo_test"; static const char *mknod_sock_path = "/tmp/mknod_sock_test"; static const char *unlink_path = "/tmp/unlink_test"; static const char *mkdir_path = "/tmp/mkdir_test"; static const char *rmdir_path = "/tmp/rmdir_test"; static const char *link_source_path = "/tmp/link_source_test"; static const char *link_dest_path = "/tmp/link_dest_test"; static const char *symlink_source_path = "/tmp/symlink_source_test"; static const char *symlink_dest_path = "/tmp/symlink_dest_test"; static const char *rename_source_path = "/tmp/rename_source_test"; static const char *rename_dest_path = "/tmp/rename_dest_test"; static const char *socket_path = "/tmp/socket_test"; static int ftruncate_fd = EOF; static void stage_file_test(void) { int fd; static int version_name[] = { CTL_KERN, KERN_VERSION }; if (sysctl(version_name, 2, NULL, NULL, 0, 0) != EOF || errno != ENOSYS) { static int name[] = { CTL_NET, NET_IPV4, NET_IPV4_LOCAL_PORT_RANGE }; int buffer[2] = { 32768, 61000 }; size_t size = sizeof(buffer); show_prompt("sysctl(READ)"); show_result(sysctl(name, 3, buffer, &size, 0, 0)); show_prompt("sysctl(WRITE)"); show_result(sysctl(name, 3, 0, 0, buffer, size)); } if (uselib("/") != EOF || errno != ENOSYS) { show_prompt("uselib()"); show_result(uselib("/tmp/uselib")); } { int pipe_fd[2] = { EOF, EOF }; int err = 0; int ret_ignored; fflush(stdout); fflush(stderr); ret_ignored = pipe(pipe_fd); if (fork() == 0) { execl(BINDIR "/true", BINDIR "/true", NULL); err = errno; ret_ignored = write(pipe_fd[1], &err, sizeof(err)); _exit(0); } close(pipe_fd[1]); ret_ignored = read(pipe_fd[0], &err, sizeof(err)); show_prompt("execve()"); errno = err; show_result(err ? EOF : 0); } show_prompt("open(O_RDONLY)"); fd = open(dev_null_path, O_RDONLY); show_result(fd); if (fd != EOF) close(fd); show_prompt("open(O_WRONLY)"); fd = open(dev_null_path, O_WRONLY); show_result(fd); if (fd != EOF) close(fd); show_prompt("open(O_RDWR)"); fd = open(dev_null_path, O_RDWR); show_result(fd); if (fd != EOF) close(fd); show_prompt("open(O_CREAT | O_EXCL)"); fd = open(open_creat_path, O_CREAT | O_EXCL, 0666); show_result(fd); if (fd != EOF) close(fd); show_prompt("open(O_TRUNC)"); fd = open(truncate_path, O_TRUNC); show_result(fd); if (fd != EOF) close(fd); show_prompt("truncate()"); show_result(truncate(truncate_path, 0)); show_prompt("ftruncate()"); show_result(ftruncate(ftruncate_fd, 0)); show_prompt("mknod(S_IFREG)"); show_result(mknod(mknod_reg_path, S_IFREG, 0)); show_prompt("mknod(S_IFCHR)"); show_result(mknod(mknod_chr_path, S_IFCHR, MKDEV(1, 3))); show_prompt("mknod(S_IFBLK)"); show_result(mknod(mknod_blk_path, S_IFBLK, MKDEV(1, 0))); show_prompt("mknod(S_IFIFO)"); show_result(mknod(mknod_fifo_path, S_IFIFO, 0)); show_prompt("mknod(S_IFSOCK)"); show_result(mknod(mknod_sock_path, S_IFSOCK, 0)); show_prompt("mkdir()"); show_result(mkdir(mkdir_path, 0600)); show_prompt("rmdir()"); show_result(rmdir(rmdir_path)); show_prompt("unlink()"); show_result(unlink(unlink_path)); show_prompt("symlink()"); show_result(symlink(symlink_dest_path, symlink_source_path)); show_prompt("link()"); show_result(link(link_source_path, link_dest_path)); show_prompt("rename()"); show_result(rename(rename_source_path, rename_dest_path)); { struct sockaddr_un addr; int fd; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); fd = socket(AF_UNIX, SOCK_STREAM, 0); show_prompt("unix_bind()"); show_result(bind(fd, (struct sockaddr *) &addr, sizeof(addr))); if (fd != EOF) close(fd); } printf("\n\n"); } static void create_files(void) { mkdir(rmdir_path, 0700); close(creat(link_source_path, 0600)); close(creat(rename_source_path, 0600)); close(creat(truncate_path, 0600)); close(creat(unlink_path, 0600)); ftruncate_fd = open(ftruncate_path, O_WRONLY | O_CREAT, 0600); } static void creanup_files(void) { if (ftruncate_fd != EOF) close(ftruncate_fd); ftruncate_fd = EOF; unlink(open_creat_path); unlink(mknod_reg_path); unlink(mknod_chr_path); unlink(mknod_blk_path); unlink(mknod_fifo_path); unlink(mknod_sock_path); rmdir(mkdir_path); unlink(symlink_source_path); unlink(symlink_dest_path); unlink(link_source_path); unlink(link_dest_path); unlink(rename_source_path); unlink(rename_dest_path); unlink(truncate_path); unlink(ftruncate_path); unlink(socket_path); } static void set_file_enforce(int enforce) { if (enforce) { set_profile(3, "file::execute"); set_profile(3, "file::open"); set_profile(3, "file::create"); set_profile(3, "file::unlink"); set_profile(3, "file::mkdir"); set_profile(3, "file::rmdir"); set_profile(3, "file::mkfifo"); set_profile(3, "file::mksock"); set_profile(3, "file::truncate"); set_profile(3, "file::symlink"); set_profile(3, "file::mkblock"); set_profile(3, "file::mkchar"); set_profile(3, "file::link"); set_profile(3, "file::rename"); set_profile(3, "file::chmod"); set_profile(3, "file::chown"); set_profile(3, "file::chgrp"); set_profile(3, "file::ioctl"); set_profile(3, "file::chroot"); set_profile(3, "file::mount"); set_profile(3, "file::unmount"); set_profile(3, "file::pivot_root"); } else { set_profile(0, "file::execute"); set_profile(0, "file::open"); set_profile(0, "file::create"); set_profile(0, "file::unlink"); set_profile(0, "file::mkdir"); set_profile(0, "file::rmdir"); set_profile(0, "file::mkfifo"); set_profile(0, "file::mksock"); set_profile(0, "file::truncate"); set_profile(0, "file::symlink"); set_profile(0, "file::mkblock"); set_profile(0, "file::mkchar"); set_profile(0, "file::link"); set_profile(0, "file::rename"); set_profile(0, "file::chmod"); set_profile(0, "file::chown"); set_profile(0, "file::chgrp"); set_profile(0, "file::ioctl"); set_profile(0, "file::chroot"); set_profile(0, "file::mount"); set_profile(0, "file::unmount"); set_profile(0, "file::pivot_root"); } } int main(int argc, char *argv[]) { ccs_test_init(); make_elf_lib(); printf("***** Testing file hooks in enforce mode. *****\n"); create_files(); should_fail = 1; set_file_enforce(1); stage_file_test(); set_file_enforce(0); clear_status(); creanup_files(); printf("***** Testing file hooks in permissive mode. *****\n"); should_fail = 0; create_files(); set_file_enforce(0); stage_file_test(); creanup_files(); clear_status(); if (0) { /* To suppress "defined but not used" warnings. */ write_domain_policy("", 0); write_exception_policy("", 0); } return 0; } tomoyo-tools/kernel_test/tomoyo_argv0_test.c0000644000000000000000000000531614116520000020401 0ustar rootroot/* * tomoyo_argv0_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" static int is_enforce = 0; static void show_prompt(const char *str) { printf("Testing %40s: (%s) ", str, is_enforce ? "must fail" : "should success"); errno = 0; } static void show_result(int result) { if (is_enforce) { if (result == EOF) { if (errno == EPERM) printf("OK: Permission denied.\n"); else printf("FAILED: %s\n", strerror(errno)); } else { printf("BUG!\n"); } } else { if (result != EOF) printf("OK\n"); else printf("%s\n", strerror(errno)); } } static void stage_argv0_test(void) { static char buffer[1024]; char *argv[2] = { "false", NULL }; int status = 0; memset(buffer, 0, sizeof(buffer)); { is_enforce = 0; set_profile(2, "file::execute"); fflush(stdout); if (fork() == 0) { execv(BINDIR "/true", argv); _exit(errno); } snprintf(buffer, sizeof(buffer) - 1, "Executing " BINDIR "/true in permissive mode"); show_prompt(buffer); wait(&status); errno = WEXITSTATUS(status); show_result(errno ? EOF : 0); is_enforce = 1; set_profile(3, "file::execute"); fflush(stdout); if (fork() == 0) { execv(BINDIR "/true", argv); _exit(errno); } snprintf(buffer, sizeof(buffer) - 1, "Executing " BINDIR "/true in enforce mode"); show_prompt(buffer); wait(&status); errno = WEXITSTATUS(status); show_result(errno ? EOF : 0); write_domain_policy("file execute " BINDIR "/true", 0); is_enforce = 0; fflush(stdout); if (fork() == 0) { argv[0] = ""; execv(BINDIR "/true", argv); _exit(errno); } snprintf(buffer, sizeof(buffer) - 1, "Executing " BINDIR "/true in enforce mode"); show_prompt(buffer); wait(&status); errno = WEXITSTATUS(status); show_result(errno ? EOF : 0); write_domain_policy("file execute " BINDIR "/true", 1); } } int main(int argc, char *argv[]) { ccs_test_init(); stage_argv0_test(); clear_status(); if (0) /* To suppress "defined but not used" warnings. */ write_exception_policy("", 0); return 0; } tomoyo-tools/kernel_test/dummyfs.c0000644000000000000000000000240714116520000016377 0ustar rootroot/* * dummyfs.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This dummy filesystem is for 2.6.29. Build with "obj-y += dummyfs.o". * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include static int dummy_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { return -ENOMEM; } static struct file_system_type dummy_fs_type = { .name = "fs\tname", .get_sb = dummy_get_sb, .kill_sb = kill_litter_super, }; static int __init dummyfs_init(void) { return register_filesystem(&dummy_fs_type); } __initcall(dummyfs_init); tomoyo-tools/kernel_test/tomoyo_rewrite_test.c0000644000000000000000000000620614116520000021042 0ustar rootroot/* * tomoyo_rewrite_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" static int should_fail = 0; static void show_prompt(const char *str) { printf("Testing %35s: (%s) ", str, should_fail ? "must fail" : "must success"); errno = 0; } static void show_result(int result) { if (should_fail) { if (result == EOF) { if (errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG!\n"); } else { printf("BUG!\n"); } } else { if (result != EOF) printf("OK\n"); else printf("BUG!\n"); } } #define REWRITE_PATH "/tmp/rewrite_test" static void stage_rewrite_test(void) { int fd; /* Start up */ write_domain_policy("file read " REWRITE_PATH, 0); write_domain_policy("file append " REWRITE_PATH, 0); write_domain_policy("file truncate " REWRITE_PATH, 0); write_domain_policy("file create " REWRITE_PATH " 0600", 0); write_domain_policy("file unlink " REWRITE_PATH, 0); set_profile(3, "file::open"); set_profile(3, "file::create"); set_profile(3, "file::truncate"); set_profile(3, "file::unlink"); close(open(REWRITE_PATH, O_WRONLY | O_APPEND | O_CREAT, 0600)); /* Enforce mode */ should_fail = 0; show_prompt("open(O_RDONLY)"); fd = open(REWRITE_PATH, O_RDONLY); show_result(fd); close(fd); show_prompt("open(O_WRONLY | O_APPEND)"); fd = open(REWRITE_PATH, O_WRONLY | O_APPEND); show_result(fd); close(fd); should_fail = 1; show_prompt("open(O_WRONLY)"); fd = open(REWRITE_PATH, O_WRONLY); show_result(fd); close(fd); show_prompt("open(O_WRONLY | O_TRUNC)"); fd = open(REWRITE_PATH, O_WRONLY | O_TRUNC); show_result(fd); close(fd); /* Permissive mode */ set_profile(2, "file::open"); set_profile(2, "file::create"); set_profile(2, "file::truncate"); set_profile(2, "file::unlink"); should_fail = 0; show_prompt("open(O_RDONLY)"); fd = open(REWRITE_PATH, O_RDONLY); show_result(fd); close(fd); show_prompt("open(O_WRONLY | O_APPEND)"); fd = open(REWRITE_PATH, O_WRONLY | O_APPEND); show_result(fd); close(fd); show_prompt("open(O_WRONLY)"); fd = open(REWRITE_PATH, O_WRONLY); show_result(fd); close(fd); show_prompt("open(O_WRONLY | O_TRUNC)"); fd = open(REWRITE_PATH, O_WRONLY | O_TRUNC); show_result(fd); close(fd); /* Clean up */ unlink(REWRITE_PATH); printf("\n\n"); } int main(int argc, char *argv[]) { ccs_test_init(); stage_rewrite_test(); clear_status(); if (0) /* To suppress "defined but not used" warnings. */ write_exception_policy("", 0); return 0; } tomoyo-tools/kernel_test/tomoyo_cond_test.c0000644000000000000000000003467414116520000020316 0ustar rootroot/* * tomoyo_cond_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" static void try_open(const char *policy, const char *file, const int mode, const char should_success) { FILE *fp; char buffer[8192]; int domain_found = 0; int policy_found = 0; int err = 0; memset(buffer, 0, sizeof(buffer)); set_profile(0, "file::open"); fp = fopen(proc_policy_domain_policy, "r+"); set_profile(3, "file::open"); printf("%s: ", policy); fflush(stdout); fprintf(domain_fp, "%s\n", policy); if (!fp) { printf("BUG: policy read failed\n"); return; } fprintf(fp, "select pid=%d\n", pid); while (fgets(buffer, sizeof(buffer) - 1, fp)) { char *cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; if (!strncmp(buffer, "", 8)) domain_found = !strcmp(self_domain, buffer); if (!domain_found) continue; /* printf("<%s>\n", buffer); */ if (!strcmp(buffer, policy)) { policy_found = 1; break; } } fclose(fp); if (!policy_found) { if (!strstr(policy, "read/write")) { printf("BUG: policy write failed\n"); return; } } { int fd; errno = 0; fd = open(file, mode, 0); err = errno; if (fd != EOF) close(fd); } fprintf(domain_fp, "delete %s\n", policy); if (should_success) { if (!err) printf("OK\n"); else printf("BUG: failed (%d)\n", err); } else { if (err == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: failed (%d)\n", err); } } static void stage_open_test(void) { const pid_t pid = getppid(); int i; char buffer[128]; for (i = 0; i < 5; i++) { memset(buffer, 0, sizeof(buffer)); snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/mounts", pid); try_open("file read /etc/fstab", "/etc/fstab", O_RDONLY, 1); try_open("file write /etc/fstab", "/etc/fstab", O_WRONLY, 1); try_open("file write /etc/fstab", "/etc/fstab", O_RDONLY, 0); try_open("file read /etc/fstab", "/etc/fstab", O_WRONLY, 0); try_open("file read/write /etc/fstab", "/etc/fstab", O_RDWR, 1); try_open("file read/write /etc/fstab", "/etc/fstab", O_RDONLY, 1); try_open("file read/write /etc/fstab", "/etc/fstab", O_WRONLY, 1); try_open("file read /etc/fstab task.uid=0 task.euid=0", "/etc/fstab", O_RDONLY, 1); try_open("file read /etc/fstab " "task.uid=0 task.euid=0-4294967295", "/etc/fstab", O_RDONLY, 1); try_open("file read /etc/fstab " "task.uid=0 task.euid!=0-4294967295", "/etc/fstab", O_RDONLY, 0); try_open("file read /etc/fstab task.uid=0 task.euid!=0", "/etc/fstab", O_RDONLY, 0); try_open("file read /etc/fstab exec.argc=0", "/etc/fstab", O_RDONLY, 0); try_open("file read /etc/fstab exec.envc=0", "/etc/fstab", O_RDONLY, 0); try_open("file read /etc/fstab exec.argv[0]=\"\"", "/etc/fstab", O_RDONLY, 0); try_open("file read /etc/fstab exec.argv[0]!=\"\"", "/etc/fstab", O_RDONLY, 0); try_open("file read /etc/fstab exec.envp[\"HOME\"]=\"\"", "/etc/fstab", O_RDONLY, 0); try_open("file read /etc/fstab exec.envp[\"HOME\"]!=\"\"", "/etc/fstab", O_RDONLY, 0); try_open("file read /etc/fstab exec.envp[\"HOME\"]=NULL", "/etc/fstab", O_RDONLY, 0); try_open("file read /etc/fstab exec.envp[\"HOME\"]!=NULL", "/etc/fstab", O_RDONLY, 0); try_open("file read proc:/\\*/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\@/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\$/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\X/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\+/mounts", buffer, O_RDONLY, pid >= 0 && pid < 10); try_open("file read proc:/\\+\\+/mounts", buffer, O_RDONLY, pid >= 10 && pid < 100); try_open("file read proc:/\\+\\+\\+/mounts", buffer, O_RDONLY, pid >= 100 && pid < 1000); try_open("file read proc:/\\+\\+\\+\\+/mounts", buffer, O_RDONLY, pid >= 1000 && pid < 10000); try_open("file read proc:/\\+\\+\\+\\+\\+/mounts", buffer, O_RDONLY, pid >= 10000 && pid < 100000); try_open("file read proc:/\\+\\+\\+\\+\\+\\+/mounts", buffer, O_RDONLY, pid >= 100000 && pid < 1000000); try_open("file read proc:/\\x/mounts", buffer, O_RDONLY, pid < 10); try_open("file read proc:/\\x\\x/mounts", buffer, O_RDONLY, pid >= 10 && pid < 100); try_open("file read proc:/\\x\\x\\x/mounts", buffer, O_RDONLY, pid >= 100 && pid < 1000); try_open("file read proc:/\\x\\x\\x\\x/mounts", buffer, O_RDONLY, pid >= 1000 && pid < 10000); try_open("file read proc:/\\x\\x\\x\\x\\x/mounts", buffer, O_RDONLY, pid >= 10000 && pid < 100000); try_open("file read proc:/\\x\\x\\x\\x\\x\\x/mounts", buffer, O_RDONLY, pid >= 100000 && pid < 1000000); try_open("file read proc:/\\$\\*/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\$\\@/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\$\\*\\*/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\$\\@\\@/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\$\\*\\@/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\$\\@\\*/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\$\\*/mounts\\*", buffer, O_RDONLY, 1); try_open("file read proc:/\\$\\@/mounts\\@", buffer, O_RDONLY, 1); try_open("file read proc:/\\$\\*\\*/mounts\\*\\*", buffer, O_RDONLY, 1); try_open("file read proc:/\\$\\@\\@/mounts\\@\\@", buffer, O_RDONLY, 1); try_open("file read proc:/\\$\\*\\@/mounts\\*\\@", buffer, O_RDONLY, 1); try_open("file read proc:/\\$\\@\\*/mounts\\@\\*", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\$/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\$/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\*\\$/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\@\\$/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\@\\$/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\*\\$/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\$/\\*mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\$/\\@mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\*\\$/\\*\\*mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\@\\$/\\@\\@mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\@\\$/\\*\\@mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\*\\$/\\@\\*mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\$\\*/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\$\\@/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\*\\$\\*\\*/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\@\\$\\@\\@/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\@\\$\\*\\@/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\*\\$\\@\\*/mounts", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\$\\*/\\*mounts\\*", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\$\\@/\\@mounts\\@", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\*\\$\\*\\*/\\*\\*mounts\\*\\*", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\@\\$\\@\\@/\\@\\@mounts\\@\\@", buffer, O_RDONLY, 1); try_open("file read proc:/\\*\\@\\$\\*\\@/\\*\\@mounts\\*\\@", buffer, O_RDONLY, 1); try_open("file read proc:/\\@\\*\\$\\@\\*/\\@\\*mounts\\@\\*", buffer, O_RDONLY, 1); } } static int try_exec(void) { int status = 0; int pipe_fd[2] = { EOF, EOF }; int ret_ignored = pipe(pipe_fd); switch (fork()) { case 0: errno = 0; execl(BINDIR "/true", "true", NULL); /* Unreachable if execl() succeeded. */ status = errno; ret_ignored = write(pipe_fd[1], &status, sizeof(status)); _exit(0); case -1: fprintf(stderr, "fork() failed.\n"); break; default: close(pipe_fd[1]); ret_ignored = read(pipe_fd[0], &status, sizeof(status)); wait(NULL); close(pipe_fd[0]); } return status ? EOF : 0; } static void stage_cond_test(void) { int fd; const char *policy; /* open read */ policy = "file read /etc/fstab task.uid=path1.uid"; write_domain_policy(policy, 0); fd = open("/etc/fstab", O_RDONLY); if (fd != EOF) close(fd); printf("%s : %s\n", policy, fd != EOF ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* open read */ policy = "file read /etc/fstab task.uid!=path1.uid"; write_domain_policy(policy, 0); fd = open("/etc/fstab", O_RDONLY); if (fd != EOF) close(fd); printf("%s : %s\n", policy, fd == EOF ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* open write */ policy = "file write /etc/fstab task.uid=path1.uid"; write_domain_policy(policy, 0); fd = open("/etc/fstab", O_WRONLY); if (fd != EOF) close(fd); printf("%s : %s\n", policy, fd != EOF ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* open write */ policy = "file write /etc/fstab task.uid!=path1.uid"; write_domain_policy(policy, 0); fd = open("/etc/fstab", O_WRONLY); if (fd != EOF) close(fd); printf("%s : %s\n", policy, fd == EOF ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* single path and single number */ policy = "file mkdir /tmp/testdir/ 0755 task.uid!=path1.parent.uid"; write_domain_policy(policy, 0); printf("%s : %s\n", policy, mkdir("/tmp/testdir", 0755) == EOF ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* single path and single number */ policy = "file mkdir /tmp/testdir/ 0755 task.uid=path1.parent.uid"; write_domain_policy(policy, 0); printf("%s : %s\n", policy, mkdir("/tmp/testdir", 0755) == 0 ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* single path */ policy = "file rmdir /tmp/testdir/ task.uid!=path1.uid"; write_domain_policy(policy, 0); printf("%s : %s\n", policy, rmdir("/tmp/testdir") == EOF ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* single path */ policy = "file rmdir /tmp/testdir/ task.uid=path1.uid"; write_domain_policy(policy, 0); printf("%s : %s\n", policy, rmdir("/tmp/testdir") == 0 ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* single path and three numbers */ policy = "file mkchar /tmp/char-1-3 0600 1 3 " "task.uid!=path1.parent.uid"; write_domain_policy(policy, 0); printf("%s : %s\n", policy, mknod("/tmp/char-1-3", S_IFCHR | 0600, MKDEV(1, 3)) == EOF ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* single path and three numbers */ policy = "file mkchar /tmp/char-1-3 0600 1 3 " "task.uid=path1.parent.uid"; write_domain_policy(policy, 0); printf("%s : %s\n", policy, mknod("/tmp/char-1-3", S_IFCHR | 0600, MKDEV(1, 3)) == 0 ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* two paths */ policy = "file rename /tmp/char-1-3 /tmp/char-1-3.new " "path1.parent.ino!=path2.parent.ino"; write_domain_policy(policy, 0); printf("%s : %s\n", policy, rename("/tmp/char-1-3", "/tmp/char-1-3.new") == EOF ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* two paths */ policy = "file rename /tmp/char-1-3 /tmp/char-1-3.new " "path1.parent.ino=path2.parent.ino"; write_domain_policy(policy, 0); printf("%s : %s\n", policy, rename("/tmp/char-1-3", "/tmp/char-1-3.new") == 0 ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* open execute */ policy = "file execute " BINDIR "/true task.uid!=path1.uid"; write_domain_policy(policy, 0); printf("%s : %s\n", policy, try_exec() == EOF ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* open execute */ policy = "file execute " BINDIR "/true task.uid=path1.uid"; write_domain_policy(policy, 0); printf("%s : %s\n", policy, try_exec() == 0 ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* open execute */ policy = "file execute " BINDIR "/true exec.realpath!=\"" BINDIR "/true\""; write_domain_policy(policy, 0); printf("%s : %s\n", policy, try_exec() == EOF ? "OK" : "FAILED"); write_domain_policy(policy, 1); /* open execute */ policy = "file execute " BINDIR "/true exec.realpath=\"" BINDIR "/true\""; write_domain_policy(policy, 0); printf("%s : %s\n", policy, try_exec() == 0 ? "OK" : "FAILED"); write_domain_policy(policy, 1); } int main(int argc, char *argv[]) { ccs_test_init(); fprintf(domain_fp, "%s " BINDIR "/true\n", self_domain); fprintf(domain_fp, "use_profile 255\n"); fprintf(domain_fp, "use_group 0\n"); fprintf(domain_fp, "%s\n", self_domain); fprintf(domain_fp, "file read/write %s\n", proc_policy_domain_policy); set_profile(3, "file::execute"); set_profile(3, "file::open"); set_profile(3, "file::create"); set_profile(3, "file::unlink"); set_profile(3, "file::mkdir"); set_profile(3, "file::rmdir"); set_profile(3, "file::mkfifo"); set_profile(3, "file::mksock"); set_profile(3, "file::truncate"); set_profile(3, "file::symlink"); set_profile(3, "file::mkblock"); set_profile(3, "file::mkchar"); set_profile(3, "file::link"); set_profile(3, "file::rename"); set_profile(3, "file::chmod"); set_profile(3, "file::chown"); set_profile(3, "file::chgrp"); set_profile(3, "file::ioctl"); set_profile(3, "file::chroot"); set_profile(3, "file::mount"); set_profile(3, "file::unmount"); set_profile(3, "file::pivot_root"); stage_open_test(); stage_cond_test(); set_profile(0, "file::execute"); set_profile(0, "file::open"); set_profile(0, "file::create"); set_profile(0, "file::unlink"); set_profile(0, "file::mkdir"); set_profile(0, "file::rmdir"); set_profile(0, "file::mkfifo"); set_profile(0, "file::mksock"); set_profile(0, "file::truncate"); set_profile(0, "file::symlink"); set_profile(0, "file::mkblock"); set_profile(0, "file::mkchar"); set_profile(0, "file::link"); set_profile(0, "file::rename"); set_profile(0, "file::chmod"); set_profile(0, "file::chown"); set_profile(0, "file::chgrp"); set_profile(0, "file::ioctl"); set_profile(0, "file::chroot"); set_profile(0, "file::mount"); set_profile(0, "file::unmount"); set_profile(0, "file::pivot_root"); clear_status(); if (0) /* To suppress "defined but not used" warnings. */ write_exception_policy("", 0); return 0; } tomoyo-tools/kernel_test/include.h0000644000000000000000000003264614116520000016353 0ustar rootroot/* * include.h * * Common functions for testing TOMOYO Linux's kernel. * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define _GNU_SOURCE #include #include #include struct module; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __NR_sys_kexec_load #ifdef __NR_kexec_load #define __NR_sys_kexec_load __NR_kexec_load #endif #endif /* #define __NR_sys_kexec_load 283 */ static inline pid_t gettid(void) { return syscall(__NR_gettid); } static inline int uselib(const char *library) { return syscall(__NR_uselib, library); } static inline caddr_t create_module(const char *name, size_t size) { return (caddr_t) syscall(__NR_create_module, name, size); } static inline int pivot_root(const char *new_root, const char *put_old) { return syscall(__NR_pivot_root, new_root, put_old); } static inline int tkill(int tid, int sig) { return syscall(__NR_tkill, tid, sig); } #ifdef __NR_tgkill static inline int tgkill(int tgid, int tid, int sig) { return syscall(__NR_tgkill, tgid, tid, sig); } #endif #ifdef __NR_sys_kexec_load struct kexec_segment; static inline long sys_kexec_load(unsigned long entry, unsigned long nr_segments, struct kexec_segment *segments, unsigned long flags) { return (long) syscall(__NR_sys_kexec_load, entry, nr_segments, segments, flags); } #endif /* reboot() in glibc takes just one argument. */ int reboot(int cmd); int init_module(const char *name, struct module *image); int delete_module(const char *name); #define proc_policy_dir "/sys/kernel/security/tomoyo/" #define proc_policy_domain_policy "/sys/kernel/security/tomoyo/domain_policy" #define proc_policy_exception_policy "/sys/kernel/security/tomoyo/exception_policy" #define proc_policy_profile "/sys/kernel/security/tomoyo/profile" #define proc_policy_manager "/sys/kernel/security/tomoyo/manager" #define proc_policy_query "/sys/kernel/security/tomoyo/query" #define proc_policy_audit "/sys/kernel/security/tomoyo/audit" #define proc_policy_process_status "/sys/kernel/security/tomoyo/.process_status" #define proc_policy_self_domain "/sys/kernel/security/tomoyo/self_domain" static FILE *profile_fp = NULL; static FILE *domain_fp = NULL; static FILE *exception_fp = NULL; static char self_domain[4096] = ""; static pid_t pid = 0; static void clear_status(void) { static const char * const keywords[] = { "file::execute", "file::open", "file::create", "file::unlink", "file::getattr", "file::mkdir", "file::rmdir", "file::mkfifo", "file::mksock", "file::truncate", "file::symlink", "file::mkblock", "file::mkchar", "file::link", "file::rename", "file::chmod", "file::chown", "file::chgrp", "file::ioctl", "file::chroot", "file::mount", "file::unmount", "file::pivot_root", "misc::env", "network::inet_stream_bind", "network::inet_stream_listen", "network::inet_stream_connect", "network::inet_dgram_bind", "network::inet_dgram_send", "network::inet_raw_bind", "network::inet_raw_send", "network::unix_stream_bind", "network::unix_stream_listen", "network::unix_stream_connect", "network::unix_dgram_bind", "network::unix_dgram_send", "network::unix_seqpacket_bind", "network::unix_seqpacket_listen", "network::unix_seqpacket_connect", "ipc::signal", "capability::use_route", "capability::use_packet", "capability::SYS_REBOOT", "capability::SYS_VHANGUP", "capability::SYS_TIME", "capability::SYS_NICE", "capability::SYS_SETHOSTNAME", "capability::use_kernel_module", "capability::SYS_KEXEC_LOAD", "capability::SYS_PTRACE", NULL }; int i; FILE *fp = fopen(proc_policy_profile, "r"); static char buffer[4096]; if (!fp) { fprintf(stderr, "Can't open %s\n", proc_policy_profile); exit(1); } for (i = 0; keywords[i]; i++) fprintf(profile_fp, "255-CONFIG::%s={ mode=disabled " "grant_log=no reject_log=no }\n", keywords[i]); while (memset(buffer, 0, sizeof(buffer)), fgets(buffer, sizeof(buffer) - 10, fp)) { const char *mode; char *cp = strchr(buffer, '='); if (!cp) continue; *cp = '\0'; mode = cp + 1; cp = strchr(buffer, '-'); if (!cp) continue; *cp++ = '\0'; if (strcmp(buffer, "0")) continue; fprintf(profile_fp, "255-%s", cp); if (!strcmp(cp, "COMMENT")) mode = "Profile for kernel test\n"; else mode = "{ mode=disabled grant_log=no reject_log=no }" "\n"; fprintf(profile_fp, "255-%s=%s", cp, mode); } /* fprintf(profile_fp, "255-PREFERENCE={ enforcing_penalty=1 }\n"); */ fprintf(profile_fp, "255-PREFERENCE={ max_learning_entry=2048 }\n"); fflush(profile_fp); fclose(fp); } static void ccs_test_init(void) { pid = getpid(); if (access(proc_policy_dir, F_OK)) { fprintf(stderr, "You can't use this program for this kernel." "\n"); exit(1); } profile_fp = fopen(proc_policy_profile, "w"); if (!profile_fp) { fprintf(stderr, "Can't open %s .\n", proc_policy_profile); exit(1); } setlinebuf(profile_fp); domain_fp = fopen(proc_policy_domain_policy, "w"); if (!domain_fp) { fprintf(stderr, "Can't open %s .\n", proc_policy_domain_policy); exit(1); } setlinebuf(domain_fp); exception_fp = fopen(proc_policy_exception_policy, "w"); if (!exception_fp) { fprintf(stderr, "Can't open %s .\n", proc_policy_exception_policy); exit(1); } setlinebuf(exception_fp); if (fputc('\n', profile_fp) != '\n' || fflush(profile_fp)) { fprintf(stderr, "You need to register this program to %s to " "run this program.\n", proc_policy_manager); exit(1); } clear_status(); { FILE *fp = fopen(proc_policy_self_domain, "r"); memset(self_domain, 0, sizeof(self_domain)); if (!fp || !fgets(self_domain, sizeof(self_domain) - 1, fp) || fclose(fp)) { fprintf(stderr, "Can't open %s .\n", proc_policy_self_domain); exit(1); } } fprintf(domain_fp, "select pid=%u\n", pid); fprintf(domain_fp, "use_profile 255\n"); fprintf(domain_fp, "file read/write/truncate/getattr " "securityfs:/tomoyo/domain_policy\n"); fprintf(domain_fp, "file read/write/truncate/getattr " "securityfs:/tomoyo/exception_policy\n"); fprintf(domain_fp, "file read/write/truncate/getattr " "securityfs:/tomoyo/profile\n"); } static void BUG(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); static void BUG(const char *fmt, ...) { va_list args; printf("BUG: "); va_start(args, fmt); vprintf(fmt, args); va_end(args); putchar('\n'); fflush(stdout); while (1) sleep(100); } static char *ccs_freadline(FILE *fp) { static char *policy = NULL; int pos = 0; while (1) { static int max_policy_len = 0; const int c = fgetc(fp); if (c == EOF) return NULL; if (pos == max_policy_len) { char *cp; max_policy_len += 4096; cp = realloc(policy, max_policy_len); if (!cp) { BUG("Out of memory"); exit(1); } policy = cp; } policy[pos++] = (char) c; if (c == '\n') { policy[--pos] = '\0'; break; } } return policy; } static char *ccs_freadline_unpack(FILE *fp) { static char *previous_line = NULL; static char *cached_line = NULL; static int pack_start = 0; static int pack_len = 0; if (cached_line) goto unpack; if (!fp) return NULL; { char *pos; unsigned int offset; unsigned int len; char *line = ccs_freadline(fp); if (!line) return NULL; if (sscanf(line, "acl_group %u", &offset) == 1 && offset < 256) pos = strchr(line + 11, ' '); else pos = NULL; if (pos++) offset = pos - line; else offset = 0; if (!strncmp(line + offset, "file ", 5)) { char *cp = line + offset + 5; char *cp2 = strchr(cp + 1, ' '); len = cp2 - cp; if (cp2 && memchr(cp, '/', len)) { pack_start = cp - line; goto prepare; } } else if (!strncmp(line + offset, "network ", 8)) { char *cp = strchr(line + offset + 8, ' '); char *cp2 = NULL; if (cp) cp = strchr(cp + 1, ' '); if (cp) cp2 = strchr(cp + 1, ' '); cp++; len = cp2 - cp; if (cp2 && memchr(cp, '/', len)) { pack_start = cp - line; goto prepare; } } return line; prepare: pack_len = len; cached_line = strdup(line); if (!cached_line) { BUG("Out of memory"); exit(1); } } unpack: { char *line = NULL; char *pos = cached_line + pack_start; char *cp = memchr(pos, '/', pack_len); unsigned int len = cp - pos; free(previous_line); previous_line = NULL; if (!cp) { previous_line = cached_line; cached_line = NULL; line = previous_line; } else if (pack_len == 1) { /* Ignore trailing empty word. */ free(cached_line); cached_line = NULL; } else { /* Current string is "abc d/e/f ghi". */ line = strdup(cached_line); if (!line) { BUG("Out of memory"); exit(1); } previous_line = line; /* Overwrite "abc d/e/f ghi" with "abc d ghi". */ memmove(line + pack_start + len, pos + pack_len, strlen(pos + pack_len) + 1); /* Overwrite "abc d/e/f ghi" with "abc e/f ghi". */ cp++; memmove(pos, cp, strlen(cp) + 1); /* Forget "d/" component. */ pack_len -= len + 1; /* Ignore leading and middle empty word. */ if (!len) goto unpack; } return line; } } static int write_domain_policy(const char *policy, int is_delete) { char *tmp_policy = strdup(policy); if (!tmp_policy) { BUG("Out of memory"); exit(1); } while (1) { FILE *fp = fopen(proc_policy_domain_policy, "r"); _Bool domain_found = 0; _Bool policy_found = 0; if (!fp) { BUG("Can't read %s", proc_policy_domain_policy); return 0; } { char *cp = strrchr(tmp_policy, '\t'); if (cp) *cp++ = '\0'; else cp = tmp_policy; policy = cp; } if (is_delete) fprintf(domain_fp, "delete "); fprintf(domain_fp, "%s\n", policy); while (1) { char *line = ccs_freadline_unpack(fp); if (!line) break; if (!strncmp(line, "", 8)) domain_found = !strcmp(self_domain, line); if (!domain_found) continue; /* printf("<%s>\n", buffer); */ if (strcmp(line, policy)) continue; policy_found = 1; while (ccs_freadline_unpack(NULL)); break; } fclose(fp); if (policy_found == is_delete) { BUG("Can't %s %s", is_delete ? "delete" : "append", policy); return 0; } if (policy == tmp_policy) break; } free(tmp_policy); errno = 0; return 1; } static int write_exception_policy(const char *policy, int is_delete) { char *tmp_policy = strdup(policy); if (!tmp_policy) { BUG("Out of memory"); exit(1); } while (1) { FILE *fp = fopen(proc_policy_exception_policy, "r"); _Bool policy_found = 0; if (!fp) { BUG("Can't read %s", proc_policy_exception_policy); return 0; } { char *cp = strrchr(tmp_policy, '\t'); if (cp) *cp++ = '\0'; else cp = tmp_policy; policy = cp; } if (is_delete) fprintf(exception_fp, "delete "); fprintf(exception_fp, "%s\n", policy); while (1) { char *line = ccs_freadline_unpack(fp); if (!line) break; if (!strncmp(line, " ", 9)) line += 9; /* printf("<%s>\n", buffer); */ if (strcmp(line, policy)) continue; policy_found = 1; while (ccs_freadline_unpack(NULL)); break; } fclose(fp); if (policy_found == is_delete) { BUG("Can't %s %s", is_delete ? "delete" : "append", policy); return 0; } if (policy == tmp_policy) break; } free(tmp_policy); errno = 0; return 1; } static int set_profile(const int mode, const char *name) { static const char *modes[4] = { "disabled", "learning", "permissive", "enforcing" }; FILE *fp = fopen(proc_policy_profile, "r"); char buffer[8192]; int policy_found = 0; const int len = strlen(name); if (!fp) { BUG("Can't read %s", proc_policy_profile); return 0; } fprintf(profile_fp, "255-CONFIG::%s={ mode=%s }\n", name, modes[mode]); while (memset(buffer, 0, sizeof(buffer)), fgets(buffer, sizeof(buffer) - 1, fp)) { char *cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; if (!strncmp(buffer, " ", 9)) memmove(buffer, buffer + 9, strlen(buffer + 9) + 1); if (strncmp(buffer, "255-CONFIG::", 12) || strncmp(buffer + 12, name, len) || buffer[12 + len] != '=') continue; if (strstr(buffer + 13 + len, modes[mode])) policy_found = 1; break; } fclose(fp); if (!policy_found) { BUG("Can't change profile to 255-CONFIG::%s={ mode=%s }", name, modes[mode]); return 0; } errno = 0; return 1; } tomoyo-tools/kernel_test/tomoyo_filesystem_test.c0000644000000000000000000006502614116520000021552 0ustar rootroot/* * tomoyo_filesystem_test.c * * Copyright (C) 2005-2012 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define _GNU_SOURCE #include "include.h" static void show_prompt(const char *str, const int should_fail) { printf("Testing %60s: (%s) ", str, should_fail ? "must fail" : "should success"); errno = 0; } #ifndef MS_MOVE #define MS_MOVE 8192 #endif #ifndef MS_UNBINDABLE #define MS_UNBINDABLE (1<<17) #endif #ifndef MS_PRIVATE #define MS_PRIVATE (1<<18) #endif #ifndef MS_SLAVE #define MS_SLAVE (1<<19) #endif #ifndef MS_SHARED #define MS_SHARED (1<<20) #endif static int child(void *arg) { errno = 0; pivot_root("/sys/kernel/security/", "/sys/kernel/security/tomoyo/"); return errno; } static void mount2(const char *source, const char *target, const char *filesystemtype) { if (mount(source, target, filesystemtype, 0, NULL)) { printf("BUG: mount() failed\n"); fflush(stdout); } } static const unsigned char compressed_ext2_image_sample[1350] = { 0x1F, 0x8B, 0x08, 0x00, 0xA8, 0xF2, 0x96, 0x4B, 0x02, 0x03, 0xED, 0xDC, 0x3D, 0x4B, 0x5B, 0x51, 0x18, 0x07, 0xF0, 0xE7, 0xDE, 0xAB, 0x14, 0x8C, 0xAB, 0xD5, 0x9A, 0xF8, 0x36, 0x0B, 0xA1, 0xE0, 0xE0, 0xDC, 0xD0, 0xAD, 0xD0, 0xC5, 0xAF, 0x50, 0x9C, 0x42, 0x1D, 0x6A, 0xE6, 0xA6, 0x9B, 0x9B, 0x8B, 0xD8, 0xA5, 0x5B, 0x97, 0x2E, 0xF9, 0x0E, 0x85, 0x4C, 0xF6, 0x23, 0x74, 0x70, 0x55, 0x28, 0x52, 0xA8, 0xDD, 0xED, 0xB9, 0xB9, 0xB1, 0xA6, 0xEA, 0x24, 0xA5, 0x81, 0xDE, 0xDF, 0x0F, 0x9E, 0xDC, 0xB7, 0x13, 0x2E, 0xF7, 0xC0, 0xFF, 0x70, 0xCE, 0x85, 0x24, 0x02, 0xA8, 0xAB, 0x7E, 0xF9, 0x31, 0x13, 0xB1, 0x95, 0x36, 0xA7, 0x45, 0x44, 0x2F, 0x6D, 0xB3, 0xC9, 0x06, 0xEB, 0x55, 0xF5, 0xC7, 0x87, 0x9F, 0x7E, 0x1C, 0xBF, 0x88, 0x68, 0xC5, 0xCE, 0xF7, 0x6C, 0xD4, 0x6E, 0x74, 0xFC, 0xF2, 0x62, 0x74, 0xED, 0xFA, 0x7B, 0x8D, 0xB8, 0x69, 0x9F, 0x8F, 0xCF, 0x9F, 0x1D, 0x7E, 0x78, 0xF7, 0x6D, 0xD8, 0x79, 0xFF, 0x71, 0xD0, 0xED, 0xBC, 0xCD, 0x9A, 0xBD, 0x69, 0x3C, 0xEB, 0xE0, 0xCB, 0xF0, 0xA4, 0xF9, 0xF5, 0xF9, 0xCA, 0xE0, 0xE0, 0x72, 0xBB, 0x7B, 0xD4, 0x1A, 0xE6, 0x13, 0xD7, 0xAA, 0xE7, 0x82, 0x7A, 0x29, 0xAA, 0xF8, 0xC7, 0xEC, 0x28, 0xFF, 0xBD, 0xC8, 0x75, 0x09, 0xD4, 0xC6, 0x55, 0x92, 0x4D, 0x71, 0xFA, 0x71, 0x05, 0x4C, 0xCF, 0xA3, 0xBB, 0xE3, 0x01, 0x50, 0x0F, 0x93, 0xEB, 0xDF, 0xEB, 0xFA, 0x97, 0x13, 0x80, 0x8B, 0x67, 0xD5, 0x02, 0xE4, 0xEE, 0xFD, 0x8B, 0x3F, 0xD6, 0x22, 0x0B, 0xA6, 0x6A, 0xC0, 0x5F, 0xF6, 0xB9, 0x1C, 0x7F, 0x9E, 0xDE, 0x37, 0xFE, 0xE4, 0xB1, 0x34, 0xD1, 0xEE, 0x71, 0xAA, 0xC5, 0x54, 0xE5, 0xB9, 0x27, 0xA9, 0x96, 0x53, 0x35, 0xA3, 0x7C, 0x13, 0x1A, 0xB1, 0x92, 0x6A, 0x35, 0xD5, 0xDA, 0xF8, 0x75, 0xE9, 0x86, 0x6E, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x81, 0xCA, 0xDF, 0xD8, 0xCF, 0x47, 0x96, 0xB7, 0x7F, 0xEF, 0xE7, 0x79, 0xBB, 0x5D, 0xFD, 0x87, 0xDF, 0x79, 0x31, 0x97, 0x77, 0xF7, 0xDE, 0xEC, 0x6F, 0xEE, 0xEE, 0xF5, 0x5E, 0xBF, 0xD2, 0x57, 0xF0, 0xBF, 0x69, 0xDC, 0xCA, 0xFF, 0xCF, 0xA2, 0xCA, 0x3F, 0x50, 0x13, 0x33, 0xBA, 0x00, 0xE4, 0x1F, 0x90, 0x7F, 0x40, 0xFE, 0x01, 0xF9, 0x07, 0xE4, 0x1F, 0x90, 0x7F, 0x40, 0xFE, 0x01, 0xF9, 0x07, 0xE4, 0x1F, 0x90, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFA, 0xF9, 0x05, 0x34, 0xF2, 0x14, 0x08, 0x00, 0x00, 0x10, 0x00 }; int main(int argc, char *argv[]) { char c = 0; ccs_test_init(); /* Test mount(). */ { set_profile(3, "file::mount"); show_prompt("mount('dev\\011name', '/', 'fs\\011name') ", 1); if (mount("dev\tname", "/", "fs\tname", 0, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else if (errno == ENODEV) printf("OK: No such device.\n"); else printf("BUG: %s\n", strerror(errno)); set_profile(1, "file::mount"); show_prompt("mount('dev\\011name', '/', 'fs\\011name') ", 0); if (mount("dev\tname", "/", "fs\tname", 0, NULL) == EOF && errno == ENOMEM) printf("OK: Out of memory.\n"); else if (errno == ENODEV) printf("OK: No such device.\n"); else printf("BUG: %s\n", strerror(errno)); set_profile(3, "file::mount"); show_prompt("mount('dev\\011name', '/', 'fs\\011name') ", 0); if (mount("dev\tname", "/", "fs\tname", 0, NULL) == EOF && errno == ENOMEM) printf("OK: Out of memory.\n"); else if (errno == ENODEV) printf("OK: No such device.\n"); else printf("BUG: %s\n", strerror(errno)); fprintf(domain_fp, "delete file mount dev\\011name / " "fs\\011name 0\n"); show_prompt("mount('dev\\011name', '/', 'fs\\011name') ", 1); if (mount("dev\tname", "/", "fs\tname", 0, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else if (errno == ENODEV) printf("OK: No such device.\n"); else printf("BUG: %s\n", strerror(errno)); set_profile(1, "file::mount"); show_prompt("mount(NULL, '/', 'tmpfs') ", 0); if (mount(NULL, "/", "tmpfs", 0, NULL)) printf("BUG: %s\n", strerror(errno)); else printf("OK: Success\n"); set_profile(3, "file::mount"); show_prompt("mount(NULL, '/', 'tmpfs') ", 0); if (mount(NULL, "/", "tmpfs", 0, NULL)) printf("BUG: %s\n", strerror(errno)); else printf("OK: Success\n"); show_prompt("mount('anydev', '/', 'tmpfs') ", 0); if (mount("anydev", "/", "tmpfs", 0, NULL)) printf("BUG: %s\n", strerror(errno)); else printf("OK: Success\n"); fprintf(domain_fp, "delete file mount / tmpfs 0\n"); fprintf(domain_fp, "file mount anydev / tmpfs 0\n"); show_prompt("mount(NULL, '/', 'tmpfs') ", 0); if (mount(NULL, "/", "tmpfs", 0, NULL)) printf("BUG: %s\n", strerror(errno)); else printf("OK: Success\n"); fprintf(domain_fp, "delete file mount anydev / tmpfs 0\n"); set_profile(2, "file::mount"); show_prompt("mount(NULL, NULL, 'tmpfs') ", 1); if (mount(NULL, NULL, "tmpfs", 0, NULL)) printf("OK: %s\n", strerror(errno)); else printf("BUG: Did not fail.\n"); show_prompt("mount(NULL, NULL, NULL) ", 1); if (mount(NULL, NULL, NULL, 0, NULL)) printf("OK: %s\n", strerror(errno)); else printf("BUG: Did not fail.\n"); show_prompt("mount('/', NULL, NULL) ", 1); if (mount("/", NULL, NULL, 0, NULL)) printf("OK: %s\n", strerror(errno)); else printf("BUG: Did not fail.\n"); show_prompt("mount('/', NULL, 'tmpfs') ", 1); if (mount("/", NULL, "tmpfs", 0, NULL)) printf("OK: %s\n", strerror(errno)); else printf("BUG: Did not fail.\n"); show_prompt("mount('/', '/', 'nonexistentfs') ", 1); if (mount("/", "/", "nonexistentfs", 0, NULL)) printf("OK: %s\n", strerror(errno)); else printf("BUG: Did not fail.\n"); set_profile(0, "file::mount"); } mkdir("/tmp/mount/", 0755); mkdir("/tmp/mount_bind/", 0755); mkdir("/tmp/mount_move/", 0755); /* Test mount(). */ { static char buf[4096]; char *dev_ram_path = canonicalize_file_name("/dev/ram0"); if (!dev_ram_path) dev_ram_path = canonicalize_file_name("/dev/ram"); if (!dev_ram_path) { dev_ram_path = "/dev/ram0"; mknod(dev_ram_path, S_IFBLK, MKDEV(1, 0)); } memset(buf, 0, sizeof(buf)); { struct stat sbuf; FILE *fp = NULL; int ret_ignored; snprintf(buf, sizeof(buf) - 1, "zcat - > %s", dev_ram_path); if (lstat(dev_ram_path, &sbuf) == 0 && S_ISBLK(sbuf.st_mode) && MAJOR(sbuf.st_rdev) == 1) fp = popen(buf, "w"); if (fp) { ret_ignored = fwrite(compressed_ext2_image_sample, 1, sizeof(compressed_ext2_image_sample), fp); pclose(fp); } else fprintf(stderr, "Can't write to %s .\n", dev_ram_path); } set_profile(3, "file::mount"); /* Test standard case */ show_prompt("mount('none', '/tmp/mount/', 'tmpfs') for " "'/tmp/mount/'", 1); if (mount("none", "/tmp/mount/", "tmpfs", 0, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); /* Test device_name with pattern */ snprintf(buf, sizeof(buf) - 1, "mount('%s', '/tmp/mount/', " "'ext2') for '%s\\*'", dev_ram_path, dev_ram_path); show_prompt(buf, 1); if (mount(dev_ram_path, "/tmp/mount/", "ext2", MS_RDONLY, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); /* Test dir_name with pattern */ show_prompt("mount('none', '/tmp/mount/', 'tmpfs') for " "'/tmp/\\?\\?\\?\\?\\?/'", 1); if (mount("none", "/tmp/mount/", "tmpfs", 0, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); /* Test standard case */ fprintf(domain_fp, "file mount none /tmp/mount/ tmpfs 0\n"); show_prompt("mount('none', '/tmp/mount/', 'tmpfs') for " "'/tmp/mount/'", 0); if (mount("none", "/tmp/mount/", "tmpfs", 0, NULL) == 0) printf("OK\n"); else printf("FAILED: %s\n", strerror(errno)); fprintf(domain_fp, "delete file mount none /tmp/mount/ tmpfs 0\n"); /* Test device_name with pattern */ fprintf(domain_fp, "file mount %s\\* /tmp/mount/ ext2 1\n", dev_ram_path); snprintf(buf, sizeof(buf) - 1, "mount('%s', '/tmp/mount/', " "'ext2') for '%s\\*'", dev_ram_path, dev_ram_path); show_prompt(buf, 0); if (mount(dev_ram_path, "/tmp/mount/", "ext2", MS_RDONLY, NULL) == 0) printf("OK\n"); else printf("FAILED: %s\n", strerror(errno)); fprintf(domain_fp, "delete file mount %s\\* " "/tmp/mount/ ext2 1\n", dev_ram_path); /* Test dir_name with pattern */ fprintf(domain_fp, "file mount none /tmp/\\?\\?\\?\\?\\?/ tmpfs 0\n"); show_prompt("mount('none', '/tmp/mount/', 'tmpfs') for " "'/tmp/\\?\\?\\?\\?\\?/'", 0); if (mount("none", "/tmp/mount/", "tmpfs", 0, NULL) == 0) printf("OK\n"); else printf("FAILED: %s\n", strerror(errno)); fprintf(domain_fp, "delete file mount none " "/tmp/\\?\\?\\?\\?\\?/ tmpfs 0\n"); set_profile(0, "file::mount"); while (umount("/tmp/mount/") == 0) c++; /* Dummy. */ } /* Test mount(). */ { mount2("none", "/tmp/mount/", "tmpfs"); set_profile(3, "file::mount"); /* Test remount case */ show_prompt("mount('/tmp/mount/', MS_REMOUNT)", 1); if (mount("none", "/tmp/mount/", "tmpfs", MS_REMOUNT, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); show_prompt("mount('/tmp/mount/', MS_REMOUNT)", 1); if (mount(NULL, "/tmp/mount/", NULL, MS_REMOUNT, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); fprintf(domain_fp, "file mount something /tmp/mount/ " "--remount 0\n"); show_prompt("mount('/tmp/mount/', MS_REMOUNT)", 0); if (mount(NULL, "/tmp/mount/", NULL, MS_REMOUNT, NULL)) printf("BUG: %s\n", strerror(errno)); else printf("OK: Success.\n"); fprintf(domain_fp, "delete file mount something /tmp/mount/ " "--remount 0\n"); /* Test bind case */ show_prompt("mount('/tmp/mount/', '/tmp/mount_bind/', " "MS_BIND)", 1); if (mount("/tmp/mount/", "/tmp/mount_bind/", NULL, MS_BIND, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); /* Test move case */ show_prompt("mount('/tmp/mount/', '/tmp/mount_move/', " "MS_MOVE)", 1); if (mount("/tmp/mount/", "/tmp/mount_move/", NULL, MS_MOVE, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); /* Test remount case */ fprintf(domain_fp, "file mount any /tmp/mount/ --remount 0\n"); show_prompt("mount('/tmp/mount/', MS_REMOUNT)", 0); if (mount("none", "/tmp/mount/", "tmpfs", MS_REMOUNT, NULL) == 0) printf("OK\n"); else printf("FAILED: %s\n", strerror(errno)); fprintf(domain_fp, "delete file mount any /tmp/mount/ " "--remount 0\n"); /* Test bind case */ fprintf(domain_fp, "file mount /tmp/mount/ /tmp/mount_bind/ --bind 0\n"); show_prompt("mount('/tmp/mount/', '/tmp/mount_bind', MS_BIND)", 0); if (mount("/tmp/mount/", "/tmp/mount_bind/", NULL, MS_BIND, NULL) == 0) printf("OK\n"); else printf("FAILED: %s\n", strerror(errno)); set_profile(0, "file::mount"); umount("/tmp/mount_bind/"); fprintf(domain_fp, "delete file mount /tmp/mount/ " "/tmp/mount_bind/ --bind 0\n"); /* Test move case */ set_profile(3, "file::mount"); fprintf(domain_fp, "file unmount /tmp/mount/\n"); fprintf(domain_fp, "file mount /tmp/mount/ /tmp/mount_move/ " "--move 0\n"); show_prompt("mount('/tmp/mount/', '/tmp/mount_move/', " "MS_MOVE)", 0); if (mount("/tmp/mount/", "/tmp/mount_move/", NULL, MS_MOVE, NULL) == 0) printf("OK\n"); else printf("FAILED: %s\n", strerror(errno)); set_profile(0, "file::mount"); umount("/tmp/mount_move/"); fprintf(domain_fp, "delete file unmount /tmp/mount/\n"); fprintf(domain_fp, "delete file mount /tmp/mount/ " "/tmp/mount_move/ --move 0\n"); fprintf(domain_fp, "file mount something /tmp/mount/ tmpfs" " 0\n"); mount2("none", "/tmp/mount/", "tmpfs"); fprintf(domain_fp, "delete file mount something /tmp/mount/" " tmpfs 0\n"); /* Tests for shared subtree case. */ set_profile(0, "file::mount"); mount2("none", "/tmp/mount/", "tmpfs"); set_profile(3, "file::mount"); /* Test private case */ fprintf(domain_fp, "file mount something /tmp/mount/" " --make-private 0\n"); show_prompt("mount('/tmp/mount/', MS_PRIVATE)", 0); if (mount(NULL, "/tmp/mount/", NULL, MS_PRIVATE, NULL)) printf("BUG: %s\n", strerror(errno)); else printf("OK: Success.\n"); fprintf(domain_fp, "delete file mount something" " /tmp/mount/ --make-private 0\n"); show_prompt("mount('/tmp/mount/', MS_PRIVATE)", 1); if (mount(NULL, "/tmp/mount/", NULL, MS_PRIVATE, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); /* Test shared case */ fprintf(domain_fp, "file mount something /tmp/mount/" " --make-shared 0\n"); show_prompt("mount('/tmp/mount/', MS_SHARED)", 0); if (mount(NULL, "/tmp/mount/", NULL, MS_SHARED, NULL)) printf("BUG: %s\n", strerror(errno)); else printf("OK: Success.\n"); fprintf(domain_fp, "delete file mount something" " /tmp/mount/ --make-shared 0\n"); show_prompt("mount('/tmp/mount/', MS_SHARED)", 1); if (mount(NULL, "/tmp/mount/", NULL, MS_SHARED, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); /* Test slave case */ fprintf(domain_fp, "file mount something /tmp/mount/" " --make-slave 0\n"); show_prompt("mount('/tmp/mount/', MS_SLAVE)", 0); if (mount(NULL, "/tmp/mount/", NULL, MS_SLAVE, NULL)) printf("BUG: %s\n", strerror(errno)); else printf("OK: Success.\n"); fprintf(domain_fp, "delete file mount something" " /tmp/mount/ --make-slave 0\n"); show_prompt("mount('/tmp/mount/', MS_SLAVE)", 1); if (mount(NULL, "/tmp/mount/", NULL, MS_SLAVE, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); /* Test unbindable case */ fprintf(domain_fp, "file mount something /tmp/mount/" " --make-unbindable 0\n"); show_prompt("mount('/tmp/mount/', MS_UNBINDABLE)", 0); if (mount(NULL, "/tmp/mount/", NULL, MS_UNBINDABLE, NULL)) printf("BUG: %s\n", strerror(errno)); else printf("OK: Success.\n"); fprintf(domain_fp, "delete file mount something" " /tmp/mount/ --make-unbindable 0\n"); show_prompt("mount('/tmp/mount/', MS_UNBINDABLE)", 1); if (mount(NULL, "/tmp/mount/", NULL, MS_UNBINDABLE, NULL) == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); /* Test invalid case */ show_prompt("mount('/tmp/mount/', MS_PRIVATE | MS_SHARED)", 1); if (mount(NULL, "/tmp/mount/", NULL, MS_PRIVATE | MS_SHARED, NULL) == EOF && errno == EINVAL) printf("OK: Invalid argument.\n"); else printf("BUG: %s\n", strerror(errno)); set_profile(0, "file::mount"); while (umount("/tmp/mount/") == 0) c++; /* Dummy. */ } /* Test umount(). */ { /* Test standard case */ fprintf(domain_fp, "file unmount /tmp/mount/\n"); set_profile(0, "file::unmount"); mount2("none", "/tmp/mount/", "tmpfs"); set_profile(3, "file::unmount"); show_prompt("umount('/tmp/mount/') for '/tmp/mount/'", 0); if (umount("/tmp/mount/") == 0) printf("OK\n"); else printf("BUG: %s\n", strerror(errno)); fprintf(domain_fp, "delete file unmount /tmp/mount/\n"); set_profile(0, "file::unmount"); mount2("none", "/tmp/mount/", "tmpfs"); set_profile(3, "file::unmount"); show_prompt("umount('/tmp/mount/') for '/tmp/mount/'", 1); if (umount("/tmp/mount/") == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("FAILED: %s\n", strerror(errno)); /* Test pattern */ fprintf(domain_fp, "file unmount /tmp/\\?\\?\\?\\?\\?/\n"); set_profile(0, "file::unmount"); mount2("none", "/tmp/mount/", "tmpfs"); set_profile(3, "file::unmount"); show_prompt("umount('/tmp/mount/') for " "'/tmp/\\?\\?\\?\\?\\?/'", 1); if (umount("/tmp/mount/") == 0) printf("OK\n"); else printf("BUG: %s\n", strerror(errno)); fprintf(domain_fp, "delete file unmount /tmp/\\?\\?\\?\\?\\?/\n"); set_profile(0, "file::unmount"); while (umount("/tmp/mount/") == 0) c++; /* Dummy. */ } /* Test chroot(). */ { set_profile(3, "file::chroot"); /* Test standard case */ fprintf(domain_fp, "file chroot /tmp/mount/\n"); show_prompt("chroot('/tmp/mount/') for '/tmp/mount/'", 0); fflush(stdout); if (fork() == 0) { if (chroot("/tmp/mount/") == 0) printf("OK\n"); else printf("FAILED: %s\n", strerror(errno)); fflush(stdout); _exit(0); } wait(NULL); fprintf(domain_fp, "delete file chroot /tmp/mount/\n"); show_prompt("chroot('/tmp/mount/') for '/tmp/mount/'", 1); fflush(stdout); if (fork() == 0) { if (chroot("/tmp/mount/") == EOF && errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); fflush(stdout); _exit(0); } wait(NULL); /* Test pattern */ fprintf(domain_fp, "file chroot /tmp/\\?\\?\\?\\?\\?/\n"); show_prompt("chroot('/tmp/mount/') for " "'/tmp/\\?\\?\\?\\?\\?/'", 0); fflush(stdout); if (fork() == 0) { if (chroot("/tmp/mount/") == 0) printf("OK\n"); else printf("FAILED: %s\n", strerror(errno)); fflush(stdout); _exit(0); } wait(NULL); fprintf(domain_fp, "delete file chroot /tmp/\\?\\?\\?\\?\\?/\n"); set_profile(0, "file::chroot"); } /* Test pivot_root(). */ { int error; char *stack = malloc(8192); set_profile(3, "file::pivot_root"); fprintf(domain_fp, "file pivot_root securityfs:/ securityfs:/tomoyo/\n"); snprintf(stack, 8191, "pivot_root('securityfs:/', 'securityfs:/tomoyo/')"); show_prompt(stack, 0); { const pid_t pid = clone(child, stack + (8192 / 2), CLONE_NEWNS, NULL); while (waitpid(pid, &error, __WALL) == EOF && errno == EINTR) c++; /* Dummy. */ } errno = WIFEXITED(error) ? WEXITSTATUS(error) : -1; if (errno == 0) printf("OK\n"); else printf("FAILED: %s\n", strerror(errno)); fprintf(domain_fp, "delete file pivot_root securityfs:/ securityfs:/tomoyo/\n"); snprintf(stack, 8191, "pivot_root('securityfs:/', 'securityfs:/tomoyo/')"); show_prompt(stack, 1); { const pid_t pid = clone(child, stack + (8192 / 2), CLONE_NEWNS, NULL); while (waitpid(pid, &error, __WALL) == EOF && errno == EINTR) c++; /* Dummy. */ } errno = WIFEXITED(error) ? WEXITSTATUS(error) : -1; if (errno == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: %s\n", strerror(errno)); set_profile(2, "file::pivot_root"); snprintf(stack, 8191, "pivot_root('securityfs:/', 'securityfs:/tomoyo/')"); show_prompt(stack, 0); { const pid_t pid = clone(child, stack + (8192 / 2), CLONE_NEWNS, NULL); while (waitpid(pid, &error, __WALL) == EOF && errno == EINTR) c++; /* Dummy. */ } errno = WIFEXITED(error) ? WEXITSTATUS(error) : -1; if (errno == 0) printf("OK\n"); else printf("FAILED: %s\n", strerror(errno)); set_profile(0, "file::pivot_root"); free(stack); } rmdir("/tmp/mount_move/"); rmdir("/tmp/mount_bind/"); rmdir("/tmp/mount/"); clear_status(); if (0) { /* To suppress "defined but not used" warnings. */ write_domain_policy("", 0); write_exception_policy("", 0); } return 0; } tomoyo-tools/kernel_test/newns.c0000644000000000000000000000303214116520000016040 0ustar rootroot/* * newns.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #ifndef MS_REC #define MS_REC 16384 #endif #ifndef MS_PRIVATE #define MS_PRIVATE (1<<18) #endif static int child(void *arg) { char **argv = (char **) arg; argv++; mount("/tmp/", "/tmp/", "tmpfs", 0, NULL); execvp(argv[0], argv); _exit(1); } int main(int argc, char *argv[]) { char c = 0; char *stack = malloc(8192); pid_t pid; /* Undo mount("/", MS_REC|MS_SHARED) made by systemd. */ mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL); pid = clone(child, stack + (8192 / 2), CLONE_NEWNS, (void *) argv); while (waitpid(pid, NULL, __WALL) == EOF && errno == EINTR) c++; /* Dummy. */ return 0; } tomoyo-tools/kernel_test/generate_execve_log.c0000644000000000000000000000627214116520000020711 0ustar rootroot/* * generate_execve_log.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include int main(int argc0, char *argv0[]) { char *argv[128]; char *envp[128]; char buffer[16384]; memset(argv, 0, sizeof(argv)); memset(envp, 0, sizeof(envp)); if (fork() == 0) { execve(BINDIR "/true", NULL, NULL); _exit(0); } wait(NULL); if (fork() == 0) { execve(BINDIR "/true", argv, NULL); _exit(0); } wait(NULL); if (fork() == 0) { execve(BINDIR "/true", NULL, envp); _exit(0); } wait(NULL); if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); argv[0] = ""; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); argv[0] = NULL; envp[0] = ""; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); argv[0] = "\xFF\xFE\x01\x02"; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); memset(buffer, ' ', 1000); argv[1] = buffer; envp[0] = "envp[0]"; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); argv[2] = buffer; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); argv[3] = buffer; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); envp[1] = "envp[1]"; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); envp[2] = buffer; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); return 0; /* memset(argv, 0, sizeof(argv)); memset(envp, 0, sizeof(envp)); */ memset(buffer, 'a', sizeof(buffer) - 1); argv[0] = "true"; argv[1] = buffer; argv[2] = NULL; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); /* memset(argv, 0, sizeof(argv)); memset(envp, 0, sizeof(envp)); */ buffer[4000] = '\0'; argv[0] = buffer; argv[1] = buffer; argv[2] = buffer; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); argv[2] = NULL; envp[0] = buffer; envp[1] = buffer; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); argv[1] = NULL; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); argv[0] = NULL; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); envp[1] = NULL; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } wait(NULL); return 0; } tomoyo-tools/kernel_test/tomoyo_policy_io_test.c0000644000000000000000000001714714116520000021355 0ustar rootroot/* * tomoyo_policy_io_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" static FILE *policy_fp = NULL; static const char *policy_file = ""; static void try_io(const char *policy, const char should_success) { FILE *fp = fopen(policy_file, "r"); char buffer[8192]; int policy_found = 0; memset(buffer, 0, sizeof(buffer)); printf("%s: ", policy); fprintf(policy_fp, "%s\n", policy); if (!fp) { printf("BUG: policy read failed\n"); return; } while (fgets(buffer, sizeof(buffer) - 1, fp)) { char *cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; if (!strcmp(policy_file, proc_policy_exception_policy) && !strncmp(buffer, " ", 9)) memmove(buffer, buffer + 9, strlen(buffer + 9) + 1); if (!strcmp(buffer, policy)) { policy_found = 1; break; } } fclose(fp); if (should_success) { if (policy_found) printf("OK\n"); else printf("BUG: policy write failed\n"); } else { if (!policy_found) printf("OK : write rejected.\n"); else printf("BUG: policy write not rejected.\n"); } fprintf(policy_fp, "delete %s\n", policy); } static void stage_policy_io_test(void) { int i; policy_file = proc_policy_domain_policy; policy_fp = domain_fp; for (i = 0; i < 3; i++) { try_io("file chroot /", 1); try_io("file chroot ", 0); try_io("file chroot /mnt0/", 1); try_io("file chroot /var1/chroot2/", 1); try_io("file chroot /mnt0/", 1); try_io("file chroot /mnt0/", 1); try_io("file chroot /mnt0/", 1); try_io("file chroot /mnt\\?\\*/", 1); try_io("file chroot /mnt\\?\\*/", 1); try_io("file unmount /", 1); try_io("file unmount /sys1/", 1); try_io("file unmount /initrd2/", 1); try_io("file unmount /initrd/dev3/", 1); try_io("file unmount /initrd/\\*\\+/", 1); try_io("file unmount /initrd/\\@\\*/", 1); try_io("file unmount /initrd2/", 1); try_io("file pivot_root / /proc3/", 1); try_io("file pivot_root /sys5/ /proc3/", 1); try_io("file pivot_root /sys/", 0); try_io("file pivot_root *", 0); try_io("file pivot_root /sys5/ /proc3/", 1); try_io("file mount / / --bind 0xD", 1); try_io("file mount / / --move 0xF", 1); try_io("file mount / --remount", 0); try_io("file mount /", 0); try_io("file mount none /tmp/ tmpfs 0x1", 1); try_io("file mount none /tmp/ tmpfs", 0); try_io("file mount none /tmp/ nonexistent 0x0", 1); try_io("file mount none /proc/ proc 0x0", 1); try_io("file mount none /selinux/ selinuxfs 0x0", 1); try_io("file mount /proc/bus/usb proc:/bus/usb/ usbfs 0x0", 1); try_io("file mount none /dev/pts/ devpts 0x0", 1); try_io("file mount any / --remount 0xC00", 1); try_io("file mount /dev/sda1 /boot/ ext3 0xC00", 1); try_io("file mount none /dev/shm/ tmpfs 0x0", 1); try_io("file mount none proc:/sys/fs/binfmt_misc/ binfmt_misc " "0x0", 1); try_io("file mount none proc:/sys/fs/binfmt_misc/ binfmt_misc " "0x0 0x1", 0); try_io("file mount none proc:/sys/fs/binfmt_misc/ tmpfs " "binfmt_misc 0x0", 0); try_io("file mount /proc/bus/usb proc:/bus/usb/ usbfs 0x0", 1); } policy_file = proc_policy_exception_policy; policy_fp = exception_fp; for (i = 0; i < 3; i++) { try_io("acl_group 0 file read /tmp/abc", 1); try_io("acl_group 0 file read /tmp/abc\\*", 1); try_io("acl_group 0 file read abc", 1); try_io("acl_group 0 file read /tmp/abc/", 1); try_io("acl_group 0 file read", 0); try_io("acl_group 0 file read *", 1); try_io("acl_group 0 misc env FOO", 1); try_io("acl_group 0 misc env FOO=", 0); try_io("acl_group 0 misc env FOO=BAR", 0); try_io("acl_group 0 misc env FOO BAR", 0); try_io("acl_group 0 misc env FOO\\040BAR", 1); try_io("acl_group 0 misc env FOO;BAR;BUZ", 1); try_io("path_group TEST /", 1); try_io("path_group TEST /boo", 1); try_io("path_group TEST /bar", 1); try_io("path_group TEST /\\*", 1); try_io("path_group TEST / /", 0); try_io("path_group TEST /boo", 1); try_io("path_group TEST /bar", 1); try_io("path_group TEST boo", 1); try_io("path_group TEST boo/", 1); try_io("path_group TEST /bar", 1); try_io("path_group TEST3 /\\*", 1); try_io("path_group TEST3 / /", 0); try_io("path_group TEST3 /boo", 1); try_io("path_group TEST3 /bar", 1); try_io("path_group TEST3 boo", 1); try_io("path_group TEST3 boo/", 1); try_io("address_group TEST 0.0.0.0", 1); try_io("address_group TEST 0.0.0.0-1.2.3.4", 1); try_io("address_group TEST ::ff", 1); try_io("address_group TEST ::-ff:ff:ff:ff:ff:ff:ff:ff", 1); try_io("address_group TEST " "fff0:fff1:fff2:fff3:fff4:fff5:fff6:fff7-" "fff8:fff9:fffa:fffb:fffc:fffd:fffe:ffff", 1); try_io("address_group TEST2 ::ff", 1); try_io("address_group TEST2 ::-ff:ff:ff:ff:ff:ff:ff:ff", 1); try_io("address_group TEST2 " "fff0:fff1:fff2:fff3:fff4:fff5:fff6:fff7-" "fff8:fff9:fffa:fffb:fffc:fffd:fffe:ffff", 1); try_io("aggregator /boo/\\* /BOO", 1); try_io("aggregator /boo/\\* /BOO\\*", 0); try_io("aggregator /boo/\\*/ /BOO", 1); try_io("aggregator /boo/\\* /BOO/", 1); try_io("keep_domain any from ", 1); try_io("keep_domain any from /sbin/init", 1); try_io("keep_domain any from foo", 0); try_io("keep_domain any from \\*", 0); try_io("keep_domain any from /ssh", 1); try_io("keep_domain any from /ssh /foo", 0); try_io("keep_domain /foo from ", 1); try_io("keep_domain /foo from /sbin/init", 1); try_io("keep_domain from /sbin/init", 0); try_io("keep_domain \\* from /sbin/init", 0); try_io("no_keep_domain any from ", 1); try_io("no_keep_domain any from /sbin/init", 1); try_io("no_keep_domain any from foo", 0); try_io("no_keep_domain any from \\*", 0); try_io("no_keep_domain any from /ssh", 1); try_io("no_keep_domain any from /ssh /foo", 0); try_io("no_keep_domain /foo from ", 1); try_io("no_keep_domain /foo from /sbin/init", 1); try_io("no_keep_domain from /sbin/init", 0); try_io("no_keep_domain \\* from /sbin/init", 0); try_io("initialize_domain /foo from any", 1); try_io("initialize_domain /\\* from any", 1); try_io("initialize_domain /foo /bar from any", 0); try_io("initialize_domain /foo from /bar", 1); try_io("initialize_domain /foo from /bar", 1); try_io("initialize_domain /\\* from ", 1); try_io("initialize_domain /foo from \\*", 0); try_io("no_initialize_domain /foo from any", 1); try_io("no_initialize_domain /\\* from any", 1); try_io("no_initialize_domain /foo /bar from any", 0); try_io("no_initialize_domain /foo from /bar", 1); try_io("no_initialize_domain /foo from /bar", 1); try_io("no_initialize_domain /\\* from ", 1); try_io("no_initialize_domain /foo from \\*", 0); } } int main(int argc, char *argv[]) { ccs_test_init(); stage_policy_io_test(); if (0) { /* To suppress "defined but not used" warnings. */ write_domain_policy("", 0); write_exception_policy("", 0); set_profile(0, ""); } return 0; } tomoyo-tools/kernel_test/tomoyo_policy_memory_test.c0000644000000000000000000002731514116520000022254 0ustar rootroot/* * tomoyo_policy_memory_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * Usage: Run this program using init= boot option. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include static void BUG(const char *msg) { printf("%s", msg); fflush(stdout); while (1) sleep(100); } static const char *policy_file = NULL; static const char *policy = NULL; static _Bool ignore_ns = 0; static void get_meminfo(unsigned int *policy_memory) { static char buf[1024]; FILE *fp = fopen("/sys/kernel/security/tomoyo/stat", "r+"); while (memset(buf, 0, sizeof(buf)), fp && fgets(buf, sizeof(buf) - 1, fp)) { if (sscanf(buf, "Memory used by policy: %u", policy_memory) != 1) continue; fclose(fp); return; } BUG("BUG: Policy read error\n"); } static void check_policy_common(const int found_expected, const int id) { FILE *fp = fopen(policy_file, "r"); char buffer[8192]; int policy_found = 0; memset(buffer, 0, sizeof(buffer)); if (!fp) BUG("BUG: Policy read error\n"); while (fgets(buffer, sizeof(buffer) - 1, fp)) { char *cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; if (ignore_ns && !strncmp(buffer, " ", 9)) memmove(buffer, buffer + 9, strlen(buffer + 9) + 1); if (strcmp(buffer, policy)) continue; policy_found = 1; break; } fclose(fp); if (policy_found != found_expected) { printf("BUG: Policy write error: %s %s at %d\n", policy, found_expected ? "not added" : "not deleted", id); BUG(""); } } static inline void check_policy_written(FILE *fp, const int id) { fflush(fp); check_policy_common(1, id); } static inline void check_policy_deleted(FILE *fp, const int id) { fflush(fp); check_policy_common(0, id); } static const char * const domain_testcases[] = { "file create /tmp/mknod_reg_test 0600", "file create /tmp/open_test 0600 path1.parent.uid=task.uid", "file create /tmp/open_test 0600 0=0", "file create /tmp/open_test 0600", "file execute /bin/true task.uid!=10 path1.parent.uid=0", "file execute /bin/true", "file execute /bin/true0 task.uid=0", "file execute /bin/true1 task.uid=task.gid", "file execute /bin/true2 0=0", "file execute /bin/true3 0!=0", "file execute /bin/true4 123-456=789", "file execute /bin/true5 exec.realpath=\"/bin/true5\"", "file execute /bin/true6 exec.argv[0]=\"true6\"", "file execute /bin/true7 1-2=@bar", "file execute /bin/true7 exec.realpath!=@foo", "file execute /bin/true7 exec.realpath=@foo", "file execute /bin/true8 " "exec.argv[0]=\"test8\" exec.realpath=\"/bin/true8\"", "file ioctl socket:[\\$] 0-35122", "file ioctl socket:[\\$] 35122-35124 " "task.uid=0", "file link /tmp/link_source_test /tmp/link_dest_test", "file mkblock /tmp/mknod_blk_test 0600 1 0", "file mkchar /tmp/mknod_chr_test 0600 1 3", "file mkdir /tmp/mkdir_test/ 0755", "file mkfifo /tmp/mknod_fifo_test 0600 path1.parent.perm=01777 " "path1.parent.perm=sticky path1.parent.uid=0 path1.parent.gid=0", "file mkfifo /tmp/mknod_fifo_test 0600", "file mksock /tmp/mknod_sock_test 0600", "file mksock /tmp/socket_test 0600", "file read /bin/true path1.uid=0 path1.parent.uid=0 10=10-100", "file read /bin/true", "file read /dev/null path1.parent.ino=path1.parent.ino", "file read /dev/null path1.perm!=0777", "file read /dev/null path1.perm=0666", "file read /dev/null path1.perm=owner_read path1.perm=owner_write " "path1.perm!=owner_execute path1.perm=group_read " "path1.perm=group_write path1.perm!=group_execute " "path1.perm=others_read path1.perm=others_write " "path1.perm!=others_execute path1.perm!=setuid path1.perm!=setgid " "path1.perm!=sticky", "file read /dev/null " "path1.type=char path1.dev_major=1 path1.dev_minor=3", "file read /dev/null", "file read /foo", "file read proc:/sys/net/ipv4/ip_local_port_range " "task.uid=0 task.gid=0", "file read proc:/sys/net/ipv4/ip_local_port_range", "file append /bar", "file append /dev/null task.uid=path1.parent.uid", "file append /dev/null", "file read proc:/sys/net/ipv4/ip_local_port_range 1!=10-100", "file read proc:/sys/net/ipv4/ip_local_port_range", "file append /tmp/fifo path1.type=fifo", "file append /tmp/fifo", "file append /tmp/rewrite_test", "file rename /tmp/rename_source_test /tmp/rename_dest_test", "file rmdir /tmp/rmdir_test/", "file symlink /symlink symlink.target!=@target", "file symlink /symlink symlink.target!=\"target\"", "file symlink /symlink symlink.target=@symlink_target", "file symlink /symlink symlink.target=\"target\"", "file symlink /tmp/symlink_source_test " "symlink.target!=\"/tmp/symlink_\\*_test\"", "file symlink /tmp/symlink_source_test symlink.target!=\"\\*\"", "file symlink /tmp/symlink_source_test " "symlink.target=\"/tmp/symlink_\\*_test\"", "file symlink /tmp/symlink_source_test " "task.uid=0 symlink.target=\"/tmp/symlink_\\*_test\"", "file symlink /tmp/symlink_source_test", "file truncate /tmp/rewrite_test", "file truncate /tmp/truncate_test task.uid=path1.uid", "file truncate /tmp/truncate_test", "file unlink /tmp/unlink_test", "file write /123", "file write /dev/null path1.uid=path1.gid", "file write /dev/null", "file write /devfile path1.major=1024 path1.minor=1048576", "file write /devfile", "file write proc:/sys/net/ipv4/ip_local_port_range " "task.euid=0 0=0 1-100=10-1000", "file write proc:/sys/net/ipv4/ip_local_port_range", "file write /tmp/open_test path1.parent.uid=0", "file write /tmp/open_test task.uid=0 path1.ino!=0", "file write /tmp/open_test", "file write /tmp/truncate_test 1!=100-1000000", "file write /tmp/truncate_test", "file mount /dev/sda1 /mnt/sda1/ ext3 0x123", "file mount /dev/sda1 /mnt/sda1/ ext3 123", "file mount /dev/sda1 /mnt/sda1/ ext3 0123", "file mount /dev/sda1 /mnt/sda1/ ext3 0x123 path1.uid=path2.uid", "file mount /dev/sda1 /mnt/sda1/ ext3 123 path1.uid=task.uid", "file mount /dev/sda1 /mnt/sda1/ ext3 0123 path1.uid=@uid", "file chroot /", "file chroot / task.uid=123-456", "file chroot /mnt/ task.uid=123-456 path1.gid=0", "file pivot_root / /proc/ path1.uid!=0", "file pivot_root /mnt/ /proc/mnt/ path1.uid!=0 path2.gid=150", "file unmount / path1.uid!=0", "file unmount /proc/ path1.uid!=0", NULL }; static void domain_policy_test(const unsigned int before) { unsigned int after; int j; policy_file = "/sys/kernel/security/tomoyo/domain_policy"; for (j = 0; domain_testcases[j]; j++) { int i; FILE *fp = fopen(policy_file, "w"); if (!fp) BUG("BUG: Policy write error\n"); fprintf(fp, "\n"); policy = domain_testcases[j]; printf("Processing: %s\n", policy); for (i = 0; i < 100; i++) { fprintf(fp, "%s\n", policy); if (!i) check_policy_written(fp, 1); fprintf(fp, "delete %s\n", policy); } check_policy_deleted(fp, 1); for (i = 0; i < 100; i++) fprintf(fp, "%s\n", policy); check_policy_written(fp, 2); fprintf(fp, "delete %s\n", policy); check_policy_deleted(fp, 2); fclose(fp); for (i = 0; i < 300; i++) { usleep(100000); get_meminfo(&after); if (before == after) break; } if (before != after) { printf("Policy: %d\n", after - before); BUG("Policy read/write test: Fail\n"); } } printf("Processing all.\n"); for (j = 0; j < 10; j++) { int i; FILE *fp = fopen(policy_file, "w"); if (!fp) BUG("BUG: Policy write error\n"); fprintf(fp, " /sbin/init\n"); for (i = 0; domain_testcases[i]; i++) fprintf(fp, "%s\n", domain_testcases[i]); fprintf(fp, "delete /sbin/init\n"); fclose(fp); for (i = 0; i < 500; i++) { usleep(100000); get_meminfo(&after); if (before == after) break; } if (before != after) { printf("Policy: %d\n", after - before); BUG("Policy read/write test: Fail\n"); } } } static const char * const exception_testcases[] = { "acl_group 0 file read /tmp/mknod_reg_test", "acl_group 0 misc env HOME", "path_group PG1 /", "path_group PG2 /", "address_group AG3 0.0.0.0", "address_group AG3 1.2.3.4-5.6.7.8", "address_group AG3 f:ee:ddd:cccc:b:aa:999:8888", "address_group AG4 0:1:2:3:4:5:6:7-8:90:a00:b000:c00:d0:e:f000", "number_group NG1 1000", "number_group NG2 10-0x100000", "number_group NG3 01234567-0xABCDEF89", "initialize_domain /usr/sbin/sshd from any", "no_initialize_domain /usr/sbin/sshd from any", "initialize_domain /usr/sbin/sshd from /bin/bash", "no_initialize_domain /usr/sbin/sshd from /bin/bash", "initialize_domain /usr/sbin/sshd from " " /bin/mingetty /bin/bash", "no_initialize_domain /usr/sbin/sshd from " " /bin/mingetty /bin/bash", "keep_domain any from /usr/sbin/sshd /bin/bash", "no_keep_domain any from /usr/sbin/sshd /bin/bash", "keep_domain /bin/pwd from /usr/sbin/sshd /bin/bash", "no_keep_domain /bin/pwd from /usr/sbin/sshd /bin/bash", "keep_domain /bin/pwd from /bin/bash", "no_keep_domain /bin/pwd from /bin/bash", "acl_group 0 file read /etc/ld.so.cache", "acl_group 0 file read proc:/meminfo", "acl_group 0 file read proc:/sys/kernel/version", "acl_group 0 file read /etc/localtime", "acl_group 0 file read proc:/self/task/\\$/attr/current", "acl_group 0 file read proc:/self/task/\\$/oom_score", "acl_group 0 file read proc:/self/wchan", "acl_group 0 file read /lib/ld-2.5.so", "aggregator /etc/rc.d/rc\\?.d/\\?\\+\\+smb /etc/rc.d/init.d/smb", "aggregator /etc/rc.d/rc\\?.d/\\?\\+\\+crond /etc/rc.d/init.d/crond", NULL }; static void exception_policy_test(const unsigned int before) { unsigned int after; int j; ignore_ns = 1; policy_file = "/sys/kernel/security/tomoyo/exception_policy"; for (j = 0; exception_testcases[j]; j++) { int i; FILE *fp = fopen(policy_file, "w"); if (!fp) BUG("BUG: Policy write error\n"); policy = exception_testcases[j]; printf("Processing: %s\n", policy); for (i = 0; i < 100; i++) { fprintf(fp, "%s\n", policy); if (!i) check_policy_written(fp, 1); fprintf(fp, "delete %s\n", policy); } check_policy_deleted(fp, 1); for (i = 0; i < 100; i++) fprintf(fp, "%s\n", policy); check_policy_written(fp, 2); fprintf(fp, "delete %s\n", policy); check_policy_deleted(fp, 2); fclose(fp); for (i = 0; i < 300; i++) { usleep(100000); get_meminfo(&after); if (before == after) break; } if (before != after) { printf("Policy: %d\n", after - before); BUG("Policy read/write test: Fail\n"); } } printf("Processing all.\n"); for (j = 0; j < 10; j++) { int i; FILE *fp = fopen(policy_file, "w"); if (!fp) BUG("BUG: Policy write error\n"); for (i = 0; exception_testcases[i]; i++) fprintf(fp, "%s\n", exception_testcases[i]); for (i = 0; exception_testcases[i]; i++) fprintf(fp, "delete %s\n", exception_testcases[i]); fclose(fp); for (i = 0; i < 500; i++) { usleep(100000); get_meminfo(&after); if (before == after) break; } if (before != after) { printf("Policy: %d\n", after - before); BUG("Policy read/write test: Fail\n"); } } } int main(int argc, char *argv[]) { unsigned int before; mount("/proc", "/proc/", "proc", 0, NULL); printf("Waiting for stabilized.\n"); get_meminfo(&before); sleep(3); domain_policy_test(before); exception_policy_test(before); BUG("Policy read/write test: Success\n"); return 0; } tomoyo-tools/kernel_test/Makefile0000644000000000000000000000117214116520000016205 0ustar rootrootinclude ../Include.make ALL_FILES = tomoyo_accept_test tomoyo_argv0_test tomoyo_bprm_test \ tomoyo_cond_test tomoyo_env_test tomoyo_filesystem_test \ tomoyo_file_test tomoyo_network_test tomoyo_new_file_test \ tomoyo_new_network_test tomoyo_new_test tomoyo_policy_io_test \ tomoyo_policy_memory_test tomoyo_rewrite_test tomoyo_transition_test \ newns all: $(ALL_FILES) chmod 755 testall.sh $(ALL_FILES): include.h # # Tools for kernel testing. # BINDIR = '"'$(shell readlink -f /bin)'"' .c: $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DBINDIR=$(BINDIR) -o $@ $< # # Delete all test programs. # clean: rm -f $(ALL_FILES) tomoyo-tools/kernel_test/tomoyo_env_test.c0000644000000000000000000000517214116520000020152 0ustar rootroot/* * tomoyo_env_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" static int should_fail = 0; static void show_prompt(const char *str) { printf("Testing %40s: (%s) ", str, should_fail ? "must fail" : "should success"); errno = 0; } static void show_result(int result) { if (should_fail) { if (result == EOF) { if (errno == EPERM) printf("OK: Permission denied.\n"); else printf("FAILED: %s\n", strerror(errno)); } else { printf("BUG!\n"); } } else { if (result != EOF) printf("OK\n"); else printf("%s\n", strerror(errno)); } } static void stage_env_test(void) { static char buffer[1024]; char *argv[2] = { "true", NULL }; char *envp[2] = { "env-test", NULL }; int status = 0; memset(buffer, 0, sizeof(buffer)); { should_fail = 0; set_profile(2, "misc::env"); if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(errno); } snprintf(buffer, sizeof(buffer) - 1, "Executing " BINDIR "/true in permissive mode"); show_prompt(buffer); wait(&status); errno = WEXITSTATUS(status); show_result(errno ? EOF : 0); should_fail = 1; set_profile(3, "misc::env"); if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(errno); } snprintf(buffer, sizeof(buffer) - 1, "Executing " BINDIR "/true in enforce mode"); show_prompt(buffer); wait(&status); errno = WEXITSTATUS(status); show_result(errno ? EOF : 0); should_fail = 0; if (fork() == 0) { envp[0] = ""; execve(BINDIR "/true", argv, envp); _exit(errno); } snprintf(buffer, sizeof(buffer) - 1, "Executing " BINDIR "/true in enforce mode"); show_prompt(buffer); wait(&status); errno = WEXITSTATUS(status); show_result(errno ? EOF : 0); } } int main(int argc, char *argv[]) { ccs_test_init(); stage_env_test(); clear_status(); if (0) { /* To suppress "defined but not used" warnings. */ write_domain_policy("", 0); write_exception_policy("", 0); } return 0; } tomoyo-tools/kernel_test/tomoyo_new_file_test.c0000644000000000000000000004653214116520000021157 0ustar rootroot/* * tomoyo_new_file_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" #include static void make_elf_lib(void) { static const struct elf32_phdr eph = { .p_type = PT_LOAD, .p_offset = 4096, .p_filesz = 1, }; static const struct elf32_hdr eh = { .e_ident = ELFMAG, .e_type = ET_EXEC, .e_machine = EM_386, .e_phoff = sizeof(eh), .e_phentsize = sizeof(eph), .e_phnum = 1, }; const int fd = open("/tmp/uselib", O_WRONLY | O_CREAT | O_TRUNC, 0755); if (fd != EOF) { write(fd, &eh, sizeof(eh)); write(fd, &eph, sizeof(eph)); lseek(fd, 4096, SEEK_SET); write(fd, "", 1); close(fd); } } static const char *policy = ""; static void show_result(int result, char should_success) { int err = errno; printf("%s : ", policy); if (should_success) { if (result != EOF) printf("OK\n"); else printf("FAILED: %s\n", strerror(err)); } else { if (result == EOF) { if (err == EPERM) printf("OK: Permission denied.\n"); else printf("FAILED: %s\n", strerror(err)); } else { printf("BUG: didn't fail.\n"); } } } static void create2(const char *pathname) { set_profile(0, "file::create"); set_profile(0, "file::open"); close(creat(pathname, 0600)); set_profile(3, "file::create"); set_profile(3, "file::open"); errno = 0; } static void mkdir2(const char *pathname) { set_profile(0, "file::mkdir"); mkdir(pathname, 0600); set_profile(3, "file::mkdir"); errno = 0; } static void unlink2(const char *pathname) { set_profile(0, "file::unlink"); unlink(pathname); set_profile(3, "file::unlink"); errno = 0; } static void rmdir2(const char *pathname) { set_profile(0, "file::rmdir"); rmdir(pathname); set_profile(3, "file::rmdir"); errno = 0; } static void mkfifo2(const char *pathname) { set_profile(0, "file::mkfifo"); mkfifo(pathname, 0600); set_profile(3, "file::mkfifo"); errno = 0; } static void stage_file_test(void) { static int version_name[] = { CTL_KERN, KERN_VERSION }; static int name[] = { CTL_NET, NET_IPV4, NET_IPV4_LOCAL_PORT_RANGE }; int buffer[2] = { 32768, 61000 }; size_t size = sizeof(buffer); int pipe_fd[2] = { EOF, EOF }; int err = 0; int flags; int fd; char pbuffer[1024]; struct stat sbuf; struct sockaddr_un addr; struct ifreq ifreq; char *filename = ""; int ret_ignored; set_profile(3, "file::execute"); set_profile(3, "file::open"); set_profile(3, "file::create"); set_profile(3, "file::unlink"); set_profile(3, "file::mkdir"); set_profile(3, "file::rmdir"); set_profile(3, "file::mkfifo"); set_profile(3, "file::mksock"); set_profile(3, "file::truncate"); set_profile(3, "file::symlink"); set_profile(3, "file::mkblock"); set_profile(3, "file::mkchar"); set_profile(3, "file::link"); set_profile(3, "file::rename"); set_profile(3, "file::chmod"); set_profile(3, "file::chown"); set_profile(3, "file::chgrp"); set_profile(3, "file::ioctl"); set_profile(3, "file::chroot"); set_profile(3, "file::mount"); set_profile(3, "file::unmount"); set_profile(3, "file::pivot_root"); if (sysctl(version_name, 2, NULL, NULL, 0, 0) != EOF || errno != ENOSYS) { policy = "file read proc:/sys/net/ipv4/ip_local_port_range " "task.uid=0 task.gid=0"; write_domain_policy(policy, 0); show_result(sysctl(name, 3, buffer, &size, 0, 0), 1); write_domain_policy(policy, 1); show_result(sysctl(name, 3, buffer, &size, 0, 0), 0); policy = "file write proc:/sys/net/ipv4/ip_local_port_range " "task.euid=0 0=0 1-100=10-1000"; write_domain_policy(policy, 0); show_result(sysctl(name, 3, 0, 0, buffer, size), 1); write_domain_policy(policy, 1); show_result(sysctl(name, 3, 0, 0, buffer, size), 0); policy = "file read proc:/sys/net/ipv4/ip_local_port_range " "1!=10-100"; write_domain_policy(policy, 0); policy = "file write proc:/sys/net/ipv4/ip_local_port_range " "1!=10-100"; write_domain_policy(policy, 0); show_result(sysctl(name, 3, buffer, &size, buffer, size), 1); policy = "file read proc:/sys/net/ipv4/ip_local_port_range " "1!=10-100"; write_domain_policy(policy, 1); policy = "file write proc:/sys/net/ipv4/ip_local_port_range " "1!=10-100"; write_domain_policy(policy, 1); show_result(sysctl(name, 3, buffer, &size, buffer, size), 0); } if (uselib("/") != EOF || errno != ENOSYS) { policy = "file read /tmp/uselib " "path1.uid=0 path1.parent.uid=0 10=10-100"; write_domain_policy(policy, 0); show_result(uselib("/tmp/uselib"), 1); write_domain_policy(policy, 1); show_result(uselib("/tmp/uselib"), 0); } policy = "file write /dev/null"; fd = open("/dev/null", O_WRONLY); show_result(fd, 0); close(fd); write_domain_policy(policy, 0); fd = open("/dev/null", O_WRONLY); show_result(fd, 1); write_domain_policy(policy, 1); flags = fcntl(fd, F_GETFL, 0) | O_APPEND; policy = "file append /dev/null"; show_result(fcntl(fd, F_SETFL, flags), 0); write_domain_policy(policy, 0); show_result(fcntl(fd, F_SETFL, flags), 1); write_domain_policy(policy, 1); close(fd); policy = "file append /dev/null"; fd = open("/dev/null", O_WRONLY | O_APPEND); show_result(fd, 0); close(fd); write_domain_policy(policy, 0); fd = open("/dev/null", O_WRONLY | O_APPEND); show_result(fd, 1); write_domain_policy(policy, 1); flags = fcntl(fd, F_GETFL, 0) & ~O_APPEND; policy = "file write /dev/null"; show_result(fcntl(fd, F_SETFL, flags), 0); write_domain_policy(policy, 0); show_result(fcntl(fd, F_SETFL, flags), 1); write_domain_policy(policy, 1); close(fd); policy = "file execute " BINDIR "/true task.uid!=10 path1.parent.uid=0"; write_domain_policy(policy, 0); fflush(stdout); fflush(stderr); ret_ignored = pipe(pipe_fd); if (fork() == 0) { execl(BINDIR "/true", BINDIR "/true", NULL); err = errno; ret_ignored = write(pipe_fd[1], &err, sizeof(err)); _exit(0); } close(pipe_fd[1]); ret_ignored = read(pipe_fd[0], &err, sizeof(err)); close(pipe_fd[0]); wait(NULL); errno = err; show_result(err ? EOF : 0, 1); write_domain_policy(policy, 1); fflush(stdout); fflush(stderr); ret_ignored = pipe(pipe_fd); if (fork() == 0) { execl(BINDIR "/true", BINDIR "/true", NULL); err = errno; ret_ignored = write(pipe_fd[1], &err, sizeof(err)); _exit(0); } close(pipe_fd[1]); ret_ignored = read(pipe_fd[0], &err, sizeof(err)); close(pipe_fd[0]); wait(NULL); errno = err; show_result(err ? EOF : 0, 0); policy = "file read /dev/null path1.type=char path1.dev_major=1 " "path1.dev_minor=3"; write_domain_policy(policy, 0); fd = open("/dev/null", O_RDONLY); show_result(fd, 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); fd = open("/dev/null", O_RDONLY); show_result(fd, 0); if (fd != EOF) close(fd); policy = "file read /dev/null path1.perm=0666"; write_domain_policy(policy, 0); fd = open("/dev/null", O_RDONLY); show_result(fd, 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); fd = open("/dev/null", O_RDONLY); show_result(fd, 0); if (fd != EOF) close(fd); policy = "file read /dev/null path1.perm!=0777"; write_domain_policy(policy, 0); fd = open("/dev/null", O_RDONLY); show_result(fd, 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); fd = open("/dev/null", O_RDONLY); show_result(fd, 0); if (fd != EOF) close(fd); policy = "file read /dev/null path1.perm=owner_read " "path1.perm=owner_write path1.perm!=owner_execute " "path1.perm=group_read path1.perm=group_write " "path1.perm!=group_execute path1.perm=others_read " "path1.perm=others_write path1.perm!=others_execute " "path1.perm!=setuid path1.perm!=setgid path1.perm!=sticky"; write_domain_policy(policy, 0); fd = open("/dev/null", O_RDONLY); show_result(fd, 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); fd = open("/dev/null", O_RDONLY); show_result(fd, 0); if (fd != EOF) close(fd); set_profile(3, "file::mkfifo"); policy = "file mkfifo /tmp/mknod_fifo_test 0644 " "path1.parent.perm=01777 path1.parent.perm=sticky " "path1.parent.uid=0 path1.parent.gid=0"; write_domain_policy(policy, 0); filename = "/tmp/mknod_fifo_test"; show_result(mknod(filename, S_IFIFO | 0644, 0), 1); write_domain_policy(policy, 1); unlink2(filename); show_result(mknod(filename, S_IFIFO | 0644, 0), 0); memset(pbuffer, 0, sizeof(pbuffer)); memset(&sbuf, 0, sizeof(sbuf)); filename = "/dev/null"; stat(filename, &sbuf); snprintf(pbuffer, sizeof(pbuffer) - 1, "file write %s path1.major=%u path1.minor=%u", filename, (unsigned int) MAJOR(sbuf.st_dev), (unsigned int) MINOR(sbuf.st_dev)); policy = pbuffer; write_domain_policy(policy, 0); fd = open(filename, O_WRONLY); show_result(fd, 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); fd = open(filename, O_WRONLY); show_result(fd, 0); if (fd != EOF) close(fd); policy = "file read /tmp/fifo path1.type=fifo\t" "file write /tmp/fifo path1.type=fifo"; mkfifo2("/tmp/fifo"); write_domain_policy(policy, 0); fd = open("/tmp/fifo", O_RDWR); show_result(fd, 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); fd = open("/tmp/fifo", O_RDWR); show_result(fd, 0); if (fd != EOF) close(fd); policy = "file read /dev/null path1.parent.ino=path1.parent.ino"; write_domain_policy(policy, 0); fd = open("/dev/null", O_RDONLY); show_result(fd, 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); fd = open("/dev/null", O_RDONLY); show_result(fd, 0); if (fd != EOF) close(fd); policy = "file write /dev/null path1.uid=path1.gid"; write_domain_policy(policy, 0); fd = open("/dev/null", O_WRONLY); show_result(fd, 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); fd = open("/dev/null", O_WRONLY); show_result(fd, 0); if (fd != EOF) close(fd); policy = "file read /dev/null task.uid=path1.parent.uid\t" "file write /dev/null task.uid=path1.parent.uid"; write_domain_policy(policy, 0); fd = open("/dev/null", O_RDWR); show_result(fd, 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); fd = open("/dev/null", O_RDWR); show_result(fd, 0); if (fd != EOF) close(fd); policy = "file create /tmp/open_test 0644 " "path1.parent.uid=task.uid"; write_domain_policy(policy, 0); policy = "file write /tmp/open_test path1.parent.uid=0"; write_domain_policy(policy, 0); fd = open("/tmp/open_test", O_WRONLY | O_CREAT | O_EXCL, 0644); show_result(fd, 1); if (fd != EOF) close(fd); unlink2("/tmp/open_test"); write_domain_policy(policy, 1); fd = open("/tmp/open_test", O_WRONLY | O_CREAT | O_EXCL, 0644); show_result(fd, 0); if (fd != EOF) close(fd); unlink2("/tmp/open_test"); policy = "file create /tmp/open_test 0644 " "path1.parent.uid=task.uid"; write_domain_policy(policy, 1); policy = "file write /tmp/open_test task.uid=0 path1.ino!=0"; write_domain_policy(policy, 0); policy = "file create /tmp/open_test 0644 0=0"; write_domain_policy(policy, 0); fd = open("/tmp/open_test", O_WRONLY | O_CREAT | O_EXCL, 0644); show_result(fd, 1); if (fd != EOF) close(fd); unlink2("/tmp/open_test"); write_domain_policy(policy, 1); fd = open("/tmp/open_test", O_WRONLY | O_CREAT | O_EXCL, 0644); show_result(fd, 0); if (fd != EOF) close(fd); unlink2("/tmp/open_test"); policy = "file write /tmp/open_test task.uid=0 path1.ino!=0"; write_domain_policy(policy, 1); filename = "/tmp/truncate_test"; create2(filename); policy = "file truncate /tmp/truncate_test task.uid=path1.uid"; write_domain_policy(policy, 0); policy = "file write /tmp/truncate_test 1!=100-1000000"; write_domain_policy(policy, 0); fd = open(filename, O_WRONLY | O_TRUNC); show_result(fd, 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); fd = open(filename, O_WRONLY | O_TRUNC); show_result(fd, 0); if (fd != EOF) close(fd); policy = "file truncate /tmp/truncate_test " "task.uid=path1.uid"; write_domain_policy(policy, 1); policy = "file write /tmp/truncate_test"; write_domain_policy(policy, 0); policy = "file truncate /tmp/truncate_test"; write_domain_policy(policy, 0); fd = open(filename, O_WRONLY | O_TRUNC); show_result(fd, 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); fd = open(filename, O_WRONLY | O_TRUNC); show_result(fd, 0); if (fd != EOF) close(fd); policy = "file write /tmp/truncate_test"; write_domain_policy(policy, 1); policy = "file truncate /tmp/truncate_test"; write_domain_policy(policy, 0); show_result(truncate(filename, 0), 1); write_domain_policy(policy, 1); show_result(truncate(filename, 0), 0); policy = "file truncate /tmp/truncate_test"; write_domain_policy(policy, 0); set_profile(0, "file::open"); fd = open(filename, O_WRONLY); set_profile(3, "file::open"); show_result(ftruncate(fd, 0), 1); write_domain_policy(policy, 1); show_result(ftruncate(fd, 0), 0); if (fd != EOF) close(fd); unlink2(filename); policy = "file create /tmp/mknod_reg_test 0644"; write_domain_policy(policy, 0); filename = "/tmp/mknod_reg_test"; show_result(mknod(filename, S_IFREG | 0644, 0), 1); write_domain_policy(policy, 1); unlink2(filename); show_result(mknod(filename, S_IFREG | 0644, 0), 0); policy = "file mkchar /tmp/mknod_chr_test 0644 1 3"; write_domain_policy(policy, 0); filename = "/tmp/mknod_chr_test"; show_result(mknod(filename, S_IFCHR | 0644, MKDEV(1, 3)), 1); write_domain_policy(policy, 1); unlink2(filename); show_result(mknod(filename, S_IFCHR | 0644, MKDEV(1, 3)), 0); policy = "file mkblock /tmp/mknod_blk_test 0644 1 0"; write_domain_policy(policy, 0); filename = "/tmp/mknod_blk_test"; show_result(mknod(filename, S_IFBLK | 0644, MKDEV(1, 0)), 1); write_domain_policy(policy, 1); unlink2(filename); show_result(mknod(filename, S_IFBLK | 0644, MKDEV(1, 0)), 0); policy = "file mkfifo /tmp/mknod_fifo_test 0644"; write_domain_policy(policy, 0); filename = "/tmp/mknod_fifo_test"; show_result(mknod(filename, S_IFIFO | 0644, 0), 1); write_domain_policy(policy, 1); unlink2(filename); show_result(mknod(filename, S_IFIFO | 0644, 0), 0); policy = "file mksock /tmp/mknod_sock_test 0644"; write_domain_policy(policy, 0); filename = "/tmp/mknod_sock_test"; show_result(mknod(filename, S_IFSOCK | 0644, 0), 1); write_domain_policy(policy, 1); unlink2(filename); show_result(mknod(filename, S_IFSOCK | 0644, 0), 0); policy = "file mkdir /tmp/mkdir_test/ 0600"; write_domain_policy(policy, 0); filename = "/tmp/mkdir_test"; show_result(mkdir(filename, 0600), 1); write_domain_policy(policy, 1); rmdir2(filename); show_result(mkdir(filename, 0600), 0); policy = "file rmdir /tmp/rmdir_test/"; write_domain_policy(policy, 0); filename = "/tmp/rmdir_test"; mkdir2(filename); show_result(rmdir(filename), 1); write_domain_policy(policy, 1); mkdir2(filename); show_result(rmdir(filename), 0); rmdir2(filename); policy = "file unlink /tmp/unlink_test"; write_domain_policy(policy, 0); filename = "/tmp/unlink_test"; create2(filename); show_result(unlink(filename), 1); write_domain_policy(policy, 1); create2(filename); show_result(unlink(filename), 0); unlink2(filename); policy = "file symlink /tmp/symlink_source_test"; write_domain_policy(policy, 0); filename = "/tmp/symlink_source_test"; show_result(symlink("/tmp/symlink_dest_test", filename), 1); write_domain_policy(policy, 1); unlink2(filename); show_result(symlink("/tmp/symlink_dest_test", filename), 0); policy = "file symlink /tmp/symlink_source_test " "symlink.target=\"/tmp/symlink_\\*_test\""; write_domain_policy(policy, 0); filename = "/tmp/symlink_source_test"; show_result(symlink("/tmp/symlink_dest_test", filename), 1); write_domain_policy(policy, 1); unlink2(filename); show_result(symlink("/tmp/symlink_dest_test", filename), 0); policy = "file symlink /tmp/symlink_source_test " "task.uid=0 symlink.target=\"/tmp/symlink_\\*_test\""; write_domain_policy(policy, 0); filename = "/tmp/symlink_source_test"; show_result(symlink("/tmp/symlink_dest_test", filename), 1); write_domain_policy(policy, 1); unlink2(filename); show_result(symlink("/tmp/symlink_dest_test", filename), 0); policy = "file symlink /tmp/symlink_source_test " "symlink.target!=\"\\*\""; write_domain_policy(policy, 0); filename = "/tmp/symlink_source_test"; show_result(symlink("/tmp/symlink_dest_test", filename), 1); write_domain_policy(policy, 1); unlink2(filename); show_result(symlink("/tmp/symlink_dest_test", filename), 0); policy = "file symlink /tmp/symlink_source_test " "symlink.target!=\"/tmp/symlink_\\*_test\""; write_domain_policy(policy, 0); filename = "/tmp/symlink_source_test"; show_result(symlink("/tmp/symlink_dest_test", filename), 0); write_domain_policy(policy, 1); unlink2(filename); show_result(symlink("/tmp/symlink_dest_test", filename), 0); policy = "file link /tmp/link_source_test /tmp/link_dest_test"; write_domain_policy(policy, 0); filename = "/tmp/link_source_test"; create2(filename); show_result(link(filename, "/tmp/link_dest_test"), 1); write_domain_policy(policy, 1); unlink2("/tmp/link_dest_test"); show_result(link(filename, "/tmp/link_dest_test"), 0); unlink2(filename); policy = "file rename /tmp/rename_source_test /tmp/rename_dest_test"; write_domain_policy(policy, 0); filename = "/tmp/rename_source_test"; create2(filename); show_result(rename(filename, "/tmp/rename_dest_test"), 1); write_domain_policy(policy, 1); unlink2("/tmp/rename_dest_test"); create2(filename); show_result(rename(filename, "/tmp/rename_dest_test"), 0); unlink2(filename); policy = "file mksock /tmp/socket_test 0755"; write_domain_policy(policy, 0); filename = "/tmp/socket_test"; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, filename, sizeof(addr.sun_path) - 1); fd = socket(AF_UNIX, SOCK_STREAM, 0); show_result(bind(fd, (struct sockaddr *) &addr, sizeof(addr)), 1); if (fd != EOF) close(fd); write_domain_policy(policy, 1); unlink2(filename); fd = socket(AF_UNIX, SOCK_STREAM, 0); show_result(bind(fd, (struct sockaddr *) &addr, sizeof(addr)), 0); if (fd != EOF) close(fd); unlink2(filename); policy = "file ioctl socket:[\\$] 35122-35124 task.uid=0"; write_domain_policy(policy, 0); fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); memset(&ifreq, 0, sizeof(ifreq)); snprintf(ifreq.ifr_name, sizeof(ifreq.ifr_name) - 1, "lo"); show_result(ioctl(fd, 35123, &ifreq), 1); write_domain_policy(policy, 1); policy = "file ioctl socket:[\\$] 0-35122"; write_domain_policy(policy, 0); show_result(ioctl(fd, 35123, &ifreq), 0); write_domain_policy(policy, 1); if (fd != EOF) close(fd); } int main(int argc, char *argv[]) { ccs_test_init(); make_elf_lib(); fprintf(domain_fp, "%s " BINDIR "/true\n", self_domain); fprintf(domain_fp, "use_profile 255\n"); fprintf(domain_fp, "use_group 0\n"); fprintf(domain_fp, "select pid=%u\n", pid); fprintf(profile_fp, "255-PREFERENCE={ max_reject_log=1024 }\n"); stage_file_test(); fprintf(domain_fp, "use_profile 0\n"); clear_status(); if (0) /* To suppress "defined but not used" warnings. */ write_exception_policy("", 0); return 0; } tomoyo-tools/kernel_test/tomoyo_network_test.c0000644000000000000000000003032514116520000021051 0ustar rootroot/* * tomoyo_network_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" static int is_enforce = 0; static void show_prompt(const char *str) { printf("Testing %50s: (%s) ", str, is_enforce ? "must fail" : "should success"); errno = 0; } static void show_result(int result) { if (is_enforce) { if (result == EOF) { if (errno == EPERM) printf("OK: Permission denied.\n"); else printf("FAILED: %s\n", strerror(errno)); } else { printf("BUG!(%d)\n", result); } } else { if (result != EOF) printf("OK\n"); else printf("%s\n", strerror(errno)); } } static void show_result3(int result) { if (result == EOF) { if (errno == EDESTADDRREQ) printf("OK: Destination address required.\n"); else if (errno == ENOTCONN) printf("OK: Transport endpoint is not connected.\n"); else printf("BUG!: %s\n", strerror(errno)); } else { printf("BUG!(%d)\n", result); } } static void set_enforce(int flag) { is_enforce = flag; if (flag) flag = 3; else flag = 2; set_profile(flag, "network::inet_stream_bind"); set_profile(flag, "network::inet_stream_listen"); set_profile(flag, "network::inet_stream_connect"); set_profile(flag, "network::inet_dgram_bind"); set_profile(flag, "network::inet_dgram_send"); set_profile(flag, "network::inet_raw_bind"); set_profile(flag, "network::inet_raw_send"); } static void stage_network_test(void) { int i; static char buf[16]; static char sbuffer[1024]; static char cbuffer[1024]; memset(buf, 0, sizeof(buf)); memset(sbuffer, 0, sizeof(sbuffer)); memset(cbuffer, 0, sizeof(cbuffer)); { /* IPv4 TCP */ struct sockaddr_in saddr; socklen_t size = sizeof(saddr); int fd1 = EOF; int fd2 = EOF; fd1 = socket(PF_INET, SOCK_STREAM, 0); memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); saddr.sin_port = htons(0); snprintf(sbuffer, sizeof(sbuffer) - 1, "Server: Binding TCP 127.0.0.1 0"); set_enforce(1); show_prompt(sbuffer); show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(sbuffer); show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr))); getsockname(fd1, (struct sockaddr *) &saddr, &size); snprintf(sbuffer, sizeof(sbuffer) - 1, "Server: Listening TCP 127.0.0.1 %d", ntohs(saddr.sin_port)); set_enforce(1); show_prompt(sbuffer); show_result(listen(fd1, 5)); set_enforce(0); show_prompt(sbuffer); show_result(listen(fd1, 5)); fd2 = socket(PF_INET, SOCK_STREAM, 0); snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Connecting TCP 127.0.0.1 %d", ntohs(saddr.sin_port)); set_enforce(1); show_prompt(cbuffer); show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(cbuffer); show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr))); close(fd2); close(fd1); } { /* IPv4 UDP */ struct sockaddr_in saddr; socklen_t size = sizeof(saddr); int fd1 = EOF; int fd2 = EOF; int ret_ignored; fd1 = socket(PF_INET, SOCK_DGRAM, 0); memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); saddr.sin_port = htons(0); snprintf(sbuffer, sizeof(sbuffer) - 1, "Server: Binding UDP 127.0.0.1 0"); set_enforce(1); show_prompt(sbuffer); show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(sbuffer); show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr))); getsockname(fd1, (struct sockaddr *) &saddr, &size); fd2 = socket(PF_INET, SOCK_DGRAM, 0); /* send() -> recv() */ snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Sending UDP 127.0.0.1 %d using send()", ntohs(saddr.sin_port)); set_enforce(1); show_prompt(cbuffer); show_result3(send(fd2, "", 1, 0)); set_enforce(0); show_prompt(cbuffer); show_result3(send(fd2, "", 1, 0)); /* write() -> read() */ snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Sending UDP 127.0.0.1 %d using write()", ntohs(saddr.sin_port)); set_enforce(1); show_prompt(cbuffer); show_result3(write(fd2, "", 1)); set_enforce(0); show_prompt(cbuffer); show_result3(write(fd2, "", 1)); /* connect() */ snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Connecting UDP 127.0.0.1 %d", ntohs(saddr.sin_port)); set_enforce(1); show_prompt(cbuffer); show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(cbuffer); show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr))); /* sendto() -> recvfrom() */ snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Sending UDP 127.0.0.1 %d using sendto()", ntohs(saddr.sin_port)); set_enforce(1); show_prompt(cbuffer); show_result(sendto(fd2, "", 1, 0, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(cbuffer); show_result(sendto(fd2, "", 1, 0, (struct sockaddr *) &saddr, sizeof(saddr))); /* send() -> recv() */ snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Sending UDP 127.0.0.1 %d using send()", ntohs(saddr.sin_port)); if (0) { set_enforce(1); show_prompt(cbuffer); show_result(send(fd2, "", 1, 0)); /* This won't fail because dest address is given via connect(). */ } set_enforce(0); show_prompt(cbuffer); show_result(send(fd2, "", 1, 0)); /* write() -> read() */ snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Sending UDP 127.0.0.1 %d using write()", ntohs(saddr.sin_port)); if (0) { set_enforce(1); show_prompt(cbuffer); show_result(write(fd2, "", 1)); /* This won't fail because dest address is given via connect(). */ } set_enforce(0); show_prompt(cbuffer); show_result(write(fd2, "", 1)); set_enforce(0); ret_ignored = write(fd2, "", 1); /* sendmsg() -> recvmsg() */ { struct msghdr msg1; struct msghdr msg2; struct iovec iov1 = { "", 1 }; struct iovec iov2 = { buf, sizeof(buf) - 1 }; memset(&msg1, 0, sizeof(msg1)); memset(&msg2, 0, sizeof(msg2)); msg1.msg_iov = &iov1; msg1.msg_iovlen = 1; msg1.msg_name = &saddr; msg1.msg_namelen = sizeof(saddr); msg2.msg_iov = &iov2; msg2.msg_iovlen = 1; /* msg2.msg_name = &caddr; msg2.msg_namelen = sizeof(caddr); */ snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Sending UDP 127.0.0.1 %d using " "sendmsg()", ntohs(saddr.sin_port)); set_enforce(1); show_prompt(cbuffer); show_result(sendmsg(fd1, &msg1, 0)); set_enforce(0); show_prompt(cbuffer); show_result(sendmsg(fd1, &msg1, 0)); set_enforce(0); sendmsg(fd1, &msg1, 0); } close(fd2); close(fd1); } { /* IPv4 RAW */ static struct iphdr ip; struct sockaddr_in saddr; socklen_t size = sizeof(saddr); int fd1 = EOF; int fd2 = EOF; fd1 = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); saddr.sin_port = htons(0); snprintf(sbuffer, sizeof(sbuffer) - 1, "Server: Binding RAW 127.0.0.1 IPPROTO_RAW"); set_enforce(1); show_prompt(sbuffer); show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(sbuffer); show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr))); fd2 = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); getsockname(fd1, (struct sockaddr *) &saddr, &size); snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Connecting RAW 127.0.0.1 %d", ntohs(saddr.sin_port)); set_enforce(1); show_prompt(cbuffer); show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(cbuffer); show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr))); memset(&ip, 0, sizeof(ip)); ip.version = 4; ip.ihl = sizeof(struct iphdr) / 4; ip.protocol = IPPROTO_RAW; ip.daddr = htonl(INADDR_LOOPBACK); ip.saddr = ip.daddr; snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Sending RAW 127.0.0.1 %d", ntohs(saddr.sin_port)); set_enforce(1); show_prompt(cbuffer); show_result(sendto(fd2, &ip, sizeof(ip), 0, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(cbuffer); show_result(sendto(fd2, &ip, sizeof(ip), 0, (struct sockaddr *) &saddr, sizeof(saddr))); close(fd2); close(fd1); } i = socket(PF_INET6, SOCK_STREAM, 0); if (i == EOF) return; close(i); { struct sockaddr_in6 saddr; socklen_t size = sizeof(saddr); int fd1 = EOF; int fd2 = EOF; fd1 = socket(PF_INET6, SOCK_STREAM, 0); memset(&saddr, 0, sizeof(saddr)); saddr.sin6_family = AF_INET6; saddr.sin6_addr = in6addr_loopback; saddr.sin6_port = htons(0); snprintf(sbuffer, sizeof(sbuffer) - 1, "Server: Binding TCP ::1 0"); set_enforce(1); show_prompt(sbuffer); show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(sbuffer); show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr))); getsockname(fd1, (struct sockaddr *) &saddr, &size); snprintf(sbuffer, sizeof(sbuffer) - 1, "Server: Listening TCP ::1 %d", ntohs(saddr.sin6_port)); set_enforce(1); show_prompt(sbuffer); show_result(listen(fd1, 5)); set_enforce(0); show_prompt(sbuffer); show_result(listen(fd1, 5)); fd2 = socket(PF_INET6, SOCK_STREAM, 0); snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Connecting TCP ::1 %d", ntohs(saddr.sin6_port)); set_enforce(1); show_prompt(cbuffer); show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(cbuffer); show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr))); close(fd2); close(fd1); } { struct sockaddr_in6 saddr; socklen_t size = sizeof(saddr); int fd1 = EOF; int fd2 = EOF; fd1 = socket(PF_INET6, SOCK_DGRAM, 0); memset(&saddr, 0, sizeof(saddr)); saddr.sin6_family = AF_INET6; saddr.sin6_addr = in6addr_loopback; saddr.sin6_port = htons(0); snprintf(sbuffer, sizeof(sbuffer) - 1, "Server: Binding UDP ::1 0"); set_enforce(1); show_prompt(sbuffer); show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(sbuffer); show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr))); getsockname(fd1, (struct sockaddr *) &saddr, &size); fd2 = socket(PF_INET6, SOCK_DGRAM, 0); snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Connecting UDP ::1 %d", ntohs(saddr.sin6_port)); set_enforce(1); show_prompt(cbuffer); show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(cbuffer); show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr))); snprintf(cbuffer, sizeof(cbuffer) - 1, "Client: Sending UDP ::1 %d", ntohs(saddr.sin6_port)); set_enforce(1); show_prompt(cbuffer); show_result(sendto(fd2, "", 1, 0, (struct sockaddr *) &saddr, sizeof(saddr))); set_enforce(0); show_prompt(cbuffer); show_result(sendto(fd2, "", 1, 0, (struct sockaddr *) &saddr, sizeof(saddr))); close(fd2); close(fd1); } /* Where can I find an example program that uses IPv6 raw socket? */ } int main(int argc, char *argv[]) { ccs_test_init(); if (access(proc_policy_domain_policy, F_OK)) { fprintf(stderr, "You can't use this program for this kernel." "\n"); return 1; } stage_network_test(); clear_status(); if (0) { /* To suppress "defined but not used" warnings. */ write_domain_policy("", 0); write_exception_policy("", 0); } return 0; } tomoyo-tools/kernel_test/tomoyo_new_test.c0000644000000000000000000004623214116520000020155 0ustar rootroot/* * tomoyo_new_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" static int result = 0; static int err = 0; static void show_result(const char *test, int should_success) { err = errno; printf("%s : ", test); if (should_success) { if (err == 0) printf("OK (%d)\n", result); else printf("FAILED: %s\n", strerror(err)); } else { if (err == 0) printf("BUG: Didn't fail (%d)\n", result); else if (err == EPERM) printf("OK: permission denied\n"); else printf("FAILED: %s\n", strerror(err)); } } static void test_read_etc_fstab(void) { result = open("/etc/fstab", O_RDONLY); } static void test_write_dev_null(void) { result = open("/dev/null", O_WRONLY); } static void cleanup_file_open(void) { if (result != EOF) close(result); } static void test_mkdir_testdir(void) { result = mkdir("/tmp/testdir", 0755); } static void cleanup_mkdir_testdir(void) { rmdir("/tmp/testdir"); } static void setup_mkdir_testdir(void) { mkdir("/tmp/testdir", 0755); } static void test_rmdir_testdir(void) { result = rmdir("/tmp/testdir"); } static void setup_execute_bin_true(void) { fprintf(domain_fp, "%s " BINDIR "/true\n", self_domain); fprintf(domain_fp, "use_profile 0\n"); fprintf(domain_fp, "select pid=%u\n", pid); } static void cleanup_execute_bin_true(void) { wait(NULL); fprintf(domain_fp, "delete %s " BINDIR "/true\n", self_domain); fprintf(domain_fp, "select pid=%u\n", pid); } static void test_execute_bin_true(void) { char *argv[] = { BINDIR "/true", NULL }; char *envp[] = { "HOME=/", NULL }; int pipe_fd[2] = { EOF, EOF }; int err = 0; int ret_ignored; ret_ignored = pipe(pipe_fd); switch (fork()) { case 0: execve(BINDIR "/true", argv, envp); err = errno; ret_ignored = write(pipe_fd[1], &err, sizeof(err)); _exit(0); break; case -1: err = -ENOMEM; break; } close(pipe_fd[1]); ret_ignored = read(pipe_fd[0], &err, sizeof(err)); close(pipe_fd[0]); result = err ? EOF : 0; errno = err; } static void test_chmod_dev_null(void) { result = chmod("/dev/null", 0666); } static void test_chown_dev_null(void) { result = chown("/dev/null", 0, -1); } static void test_chgrp_dev_null(void) { result = chown("/dev/null", -1, 0); } static void test_ioctl_dev_null(void) { int fd = open("/dev/null", O_RDWR); errno = 0; result = ioctl(fd, 0x5451, NULL); err = errno; close(fd); errno = err; } static void setup_chmod_group(void) { write_exception_policy("path_group CHMOD_TARGET /dev/null", 0); write_exception_policy("number_group CHMOD_MODES 0666", 0); } static void cleanup_chmod_group(void) { write_exception_policy("path_group CHMOD_TARGET /dev/null", 1); write_exception_policy("number_group CHMOD_MODES 0666", 1); } static void setup_chown_group(void) { write_exception_policy("path_group CHOWN_TARGET /dev/\\*", 0); write_exception_policy("number_group CHOWN_IDS 0x0-0xFFFE", 0); } static void cleanup_chown_group(void) { write_exception_policy("path_group CHOWN_TARGET /dev/\\*", 1); write_exception_policy("number_group CHOWN_IDS 0x0-0xFFFE", 1); } static void setup_ioctl_group(void) { write_exception_policy("path_group IOCTL_TARGET /dev/\\*", 0); write_exception_policy("number_group IOCTL_NUMBERS 0x5450-0x5452", 0); } static void cleanup_ioctl_group(void) { write_exception_policy("path_group IOCTL_TARGET /dev/\\*", 1); write_exception_policy("number_group IOCTL_NUMBERS 0x5450-0x5452", 1); } static void setup_open_group(void) { write_exception_policy("path_group READABLE /etc/\\*", 0); write_exception_policy("number_group READABLE_IDS 0-0xFFF", 0); } static void cleanup_open_group(void) { cleanup_file_open(); write_exception_policy("path_group READABLE /etc/\\*", 1); write_exception_policy("number_group READABLE_IDS 0-0xFFF", 1); } static void test_file_open_0(void) { result = open("/tmp/testfile0", O_RDONLY, 0600); } static void test_file_open_1(void) { result = open("/tmp/testfile1", O_CREAT | O_RDONLY, 0600); } static void test_file_open_2(void) { result = open("/tmp/testfile2", O_TRUNC | O_RDONLY, 0600); } static void test_file_open_3(void) { result = open("/tmp/testfile3", O_TRUNC | O_CREAT | O_RDONLY, 0600); } static void test_file_open_4(void) { result = open("/tmp/testfile4", O_APPEND | O_RDONLY, 0600); } static void test_file_open_5(void) { result = open("/tmp/testfile5", O_APPEND | O_CREAT | O_RDONLY, 0600); } static void test_file_open_6(void) { result = open("/tmp/testfile6", O_APPEND | O_TRUNC | O_RDONLY, 0600); } static void test_file_open_7(void) { result = open("/tmp/testfile7", O_APPEND | O_TRUNC | O_CREAT | O_RDONLY, 0600); } static void test_file_open_8(void) { result = open("/tmp/testfile8", O_WRONLY, 0600); } static void test_file_open_9(void) { result = open("/tmp/testfile9", O_CREAT | O_WRONLY, 0600); } static void test_file_open_10(void) { result = open("/tmp/testfile10", O_TRUNC | O_WRONLY, 0600); } static void test_file_open_11(void) { result = open("/tmp/testfile11", O_TRUNC | O_CREAT | O_WRONLY, 0600); } static void test_file_open_12(void) { result = open("/tmp/testfile12", O_APPEND | O_WRONLY, 0600); } static void test_file_open_13(void) { result = open("/tmp/testfile13", O_APPEND | O_CREAT | O_WRONLY, 0600); } static void test_file_open_14(void) { result = open("/tmp/testfile14", O_APPEND | O_TRUNC | O_WRONLY, 0600); } static void test_file_open_15(void) { result = open("/tmp/testfile15", O_APPEND | O_TRUNC | O_CREAT | O_WRONLY, 0600); } static void test_file_open_16(void) { result = open("/tmp/testfile16", O_RDWR, 0600); } static void test_file_open_17(void) { result = open("/tmp/testfile17", O_CREAT | O_RDWR, 0600); } static void test_file_open_18(void) { result = open("/tmp/testfile18", O_TRUNC | O_RDWR, 0600); } static void test_file_open_19(void) { result = open("/tmp/testfile19", O_TRUNC | O_CREAT | O_RDWR, 0600); } static void test_file_open_21(void) { result = open("/tmp/testfile21", O_APPEND | O_CREAT | O_RDWR, 0600); } static void test_file_open_22(void) { result = open("/tmp/testfile22", O_APPEND | O_TRUNC | O_RDWR, 0600); } static void test_file_open_23(void) { result = open("/tmp/testfile23", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, 0600); } static void setup_test_file(void) { int i; char buffer[32]; buffer[31] = '\0'; for (i = 0; i < 24; i += 2) { snprintf(buffer, sizeof(buffer) - 1, "/tmp/testfile%u", i); close(open(buffer, O_WRONLY | O_CREAT, 0600)); close(open(buffer, O_APPEND | O_CREAT, 0600)); } } static void setup_all_test_file(void) { int i; char buffer[32]; buffer[31] = '\0'; for (i = 0; i < 24; i++) { snprintf(buffer, sizeof(buffer) - 1, "/tmp/testfile%u", i); close(open(buffer, O_WRONLY | O_CREAT, 0600)); close(open(buffer, O_APPEND | O_CREAT, 0600)); } } static void cleanup_test_file(void) { int i; char buffer[32]; buffer[31] = '\0'; for (i = 0; i < 24; i++) { snprintf(buffer, sizeof(buffer) - 1, "/tmp/testfile%u", i); unlink(buffer); } cleanup_file_open(); } static struct test_struct { void (*do_setup) (void); void (*do_test) (void); void (*do_cleanup) (void); const char *name; const char *policy; } tests[] = { { NULL, test_read_etc_fstab, cleanup_file_open, "file::open", "file read /etc/fstab" }, { NULL, test_read_etc_fstab, cleanup_file_open, "file::open", "file read /etc/fstab task.uid=0" }, { NULL, test_read_etc_fstab, cleanup_file_open, "file::open", "file read /etc/fstab path1.uid=0 path1.parent.uid=0" }, { setup_open_group, test_read_etc_fstab, cleanup_open_group, "file::open", "file read @READABLE path1.uid=@READABLE_IDS " "path1.parent.uid=0" }, { NULL, test_write_dev_null, cleanup_file_open, "file::open", "file write /dev/null" }, { NULL, test_write_dev_null, cleanup_file_open, "file::open", "file write /dev/null task.uid=0" }, { NULL, test_write_dev_null, cleanup_file_open, "file::open", "file write /dev/null path1.type=char path1.dev_major=1 " "path1.dev_minor=3 path1.perm=0666" }, { cleanup_mkdir_testdir, test_mkdir_testdir, cleanup_mkdir_testdir, "file::mkdir", "file mkdir /tmp/testdir/ 0755" }, { cleanup_mkdir_testdir, test_mkdir_testdir, cleanup_mkdir_testdir, "file::mkdir", "file mkdir /tmp/testdir/ 0755 " "path1.parent.uid=0 path1.parent.perm=01777" }, { cleanup_mkdir_testdir, test_mkdir_testdir, cleanup_mkdir_testdir, "file::mkdir", "file mkdir /tmp/testdir/ 0755 " "task.uid=path1.parent.uid" }, { setup_mkdir_testdir, test_rmdir_testdir, cleanup_mkdir_testdir, "file::rmdir", "file rmdir /tmp/testdir/" }, { setup_mkdir_testdir, test_rmdir_testdir, cleanup_mkdir_testdir, "file::rmdir", "file rmdir /tmp/testdir/ path1.parent.uid=0 " "path1.parent.perm=01777" }, { setup_mkdir_testdir, test_rmdir_testdir, cleanup_mkdir_testdir, "file::rmdir", "file rmdir /tmp/testdir/ task.uid=0-100 " "task.gid=0x0-0xFF path1.uid=0" }, { setup_execute_bin_true, test_execute_bin_true, cleanup_execute_bin_true, "file::execute", "file execute " BINDIR "/true" }, { setup_execute_bin_true, test_execute_bin_true, cleanup_execute_bin_true, "file::execute", "file execute " BINDIR "/true " "exec.argc=1 exec.argv[0]=\"" BINDIR "/true\"" }, { setup_execute_bin_true, test_execute_bin_true, cleanup_execute_bin_true, "file::execute", "file execute " BINDIR "/true " "exec.envc=1 exec.envp[\"HOME\"]=\"/\" exec.envp[\"PATH\"]=NULL" }, { NULL, test_chmod_dev_null, NULL, "file::chmod", "file chmod /dev/null 0666 path1.perm=00-07777 path1.type=char" }, { NULL, test_chown_dev_null, NULL, "file::chown", "file chown /dev/null 0 task.gid=path1.gid path1.type!=block" }, { NULL, test_chgrp_dev_null, NULL, "file::chgrp", "file chgrp /dev/null 0 task.uid=path1.parent.uid" }, { NULL, test_ioctl_dev_null, NULL, "file::ioctl", "file ioctl /dev/null 0x5451 0=0-1000" }, { setup_chmod_group, test_chmod_dev_null, cleanup_chmod_group, "file::chmod", "file chmod @CHMOD_TARGET @CHMOD_MODES" }, { setup_chown_group, test_chown_dev_null, cleanup_chown_group, "file::chown", "file chown @CHOWN_TARGET @CHOWN_IDS" }, { setup_chown_group, test_chgrp_dev_null, cleanup_chown_group, "file::chgrp", "file chgrp @CHOWN_TARGET @CHOWN_IDS" }, { setup_ioctl_group, test_ioctl_dev_null, cleanup_ioctl_group, "file::ioctl", "file ioctl @IOCTL_TARGET @IOCTL_NUMBERS" }, { setup_test_file, test_file_open_0, cleanup_test_file, "file::open", "file read /tmp/testfile0 task.uid=path1.uid" }, { setup_test_file, test_file_open_1, cleanup_test_file, "file::open", "file read /tmp/testfile1 task.uid=path1.uid" }, { setup_test_file, test_file_open_1, cleanup_test_file, "file::create", "file create /tmp/testfile1 0600 task.uid=path1.parent.uid" }, { setup_test_file, test_file_open_2, cleanup_test_file, "file::open", "file read /tmp/testfile2 task.uid=path1.uid" }, { setup_test_file, test_file_open_2, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile2 " "task.uid=path1.uid" }, { setup_test_file, test_file_open_3, cleanup_test_file, "file::open", "file read /tmp/testfile3 task.uid=path1.uid" }, { setup_test_file, test_file_open_3, cleanup_test_file, "file::create", "file create /tmp/testfile3 0600 " "task.uid=path1.parent.uid" }, { setup_test_file, test_file_open_4, cleanup_test_file, "file::open", "file read /tmp/testfile4 task.uid=path1.uid" }, { setup_test_file, test_file_open_5, cleanup_test_file, "file::open", "file read /tmp/testfile5 task.uid=path1.uid" }, { setup_test_file, test_file_open_5, cleanup_test_file, "file::create", "file create /tmp/testfile5 0600 task.uid=path1.parent.uid" }, { setup_test_file, test_file_open_6, cleanup_test_file, "file::open", "file read /tmp/testfile6 task.uid=path1.uid" }, { setup_test_file, test_file_open_6, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile6 " "task.uid=path1.uid" }, { setup_test_file, test_file_open_7, cleanup_test_file, "file::open", "file read /tmp/testfile7 task.uid=path1.uid" }, { setup_test_file, test_file_open_7, cleanup_test_file, "file::create", "file create /tmp/testfile7 0600 task.uid=path1.parent.uid" }, { setup_test_file, test_file_open_8, cleanup_test_file, "file::open", "file write /tmp/testfile8 task.uid=path1.uid" }, { setup_test_file, test_file_open_9, cleanup_test_file, "file::open", "file write /tmp/testfile9 task.uid=path1.uid" }, { setup_test_file, test_file_open_9, cleanup_test_file, "file::create", "file create /tmp/testfile9 0600 task.uid=path1.parent.uid" }, { setup_test_file, test_file_open_10, cleanup_test_file, "file::open", "file write /tmp/testfile10 task.uid=path1.uid" }, { setup_test_file, test_file_open_10, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile10 " "task.uid=path1.uid" }, { setup_test_file, test_file_open_11, cleanup_test_file, "file::open", "file write /tmp/testfile11 task.uid=path1.uid" }, { setup_test_file, test_file_open_11, cleanup_test_file, "file::create", "file create /tmp/testfile11 0600 " "task.uid=path1.parent.uid" }, { setup_test_file, test_file_open_12, cleanup_test_file, "file::open", "file append /tmp/testfile12 task.uid=path1.uid" }, { setup_test_file, test_file_open_13, cleanup_test_file, "file::open", "file append /tmp/testfile13 task.uid=path1.uid" }, { setup_test_file, test_file_open_13, cleanup_test_file, "file::create", "file create /tmp/testfile13 0600 " "task.uid=path1.parent.uid" }, { setup_test_file, test_file_open_14, cleanup_test_file, "file::open", "file append /tmp/testfile14 task.uid=path1.uid" }, { setup_test_file, test_file_open_14, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile14 " "task.uid=path1.uid" }, { setup_test_file, test_file_open_15, cleanup_test_file, "file::open", "file append /tmp/testfile15 task.uid=path1.uid" }, { setup_test_file, test_file_open_15, cleanup_test_file, "file::create", "file create /tmp/testfile15 0600 " "task.uid=path1.parent.uid" }, { setup_test_file, test_file_open_16, cleanup_test_file, "file::open", "file read /tmp/testfile16 task.uid=path1.uid\t" "file write /tmp/testfile16 task.uid=path1.uid" }, { setup_test_file, test_file_open_17, cleanup_test_file, "file::open", "file read /tmp/testfile17 task.uid=path1.uid\t" "file write /tmp/testfile17 task.uid=path1.uid" }, { setup_test_file, test_file_open_17, cleanup_test_file, "file::create", "file create /tmp/testfile17 0600 " "task.uid=path1.parent.uid" }, { setup_test_file, test_file_open_18, cleanup_test_file, "file::open", "file read /tmp/testfile18 task.uid=path1.uid\t" "file write /tmp/testfile18 task.uid=path1.uid" }, { setup_test_file, test_file_open_18, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile18 " "task.uid=path1.uid" }, { setup_test_file, test_file_open_19, cleanup_test_file, "file::open", "file read /tmp/testfile19 task.uid=path1.uid\t" "file write /tmp/testfile19 task.uid=path1.uid" }, { setup_test_file, test_file_open_19, cleanup_test_file, "file::create", "file create /tmp/testfile19 0600 " "task.uid=path1.parent.uid" }, { setup_test_file, test_file_open_21, cleanup_test_file, "file::create", "file create /tmp/testfile21 0600 " "task.uid=path1.parent.uid" }, { setup_test_file, test_file_open_22, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile22 " "task.uid=path1.uid" }, { setup_test_file, test_file_open_23, cleanup_test_file, "file::create", "file create /tmp/testfile23 0600 " "task.uid=path1.parent.uid" }, { setup_all_test_file, test_file_open_0, cleanup_test_file, "file::open", "file read /tmp/testfile0 task.uid=path1.gid" }, { setup_all_test_file, test_file_open_2, cleanup_test_file, "file::open", "file read /tmp/testfile2 task.uid=path1.gid" }, { setup_all_test_file, test_file_open_2, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile2 " "task.uid=path1.gid" }, { setup_all_test_file, test_file_open_4, cleanup_test_file, "file::open", "file read /tmp/testfile4 task.uid=path1.gid" }, { setup_all_test_file, test_file_open_6, cleanup_test_file, "file::open", "file read /tmp/testfile6 task.uid=path1.gid" }, { setup_all_test_file, test_file_open_6, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile6 " "task.uid=path1.gid" }, { setup_all_test_file, test_file_open_8, cleanup_test_file, "file::open", "file write /tmp/testfile8 task.uid=path1.gid" }, { setup_all_test_file, test_file_open_10, cleanup_test_file, "file::open", "file write /tmp/testfile10 task.uid=path1.gid" }, { setup_all_test_file, test_file_open_10, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile10 " "task.uid=path1.gid" }, { setup_all_test_file, test_file_open_12, cleanup_test_file, "file::open", "file append /tmp/testfile12 task.uid=path1.gid" }, { setup_all_test_file, test_file_open_14, cleanup_test_file, "file::open", "file append /tmp/testfile14 task.uid=path1.gid" }, { setup_all_test_file, test_file_open_14, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile14 " "task.uid=path1.gid" }, { setup_all_test_file, test_file_open_16, cleanup_test_file, "file::open", "file read /tmp/testfile16 task.uid=path1.gid\t" "file write /tmp/testfile16 task.uid=path1.gid" }, { setup_all_test_file, test_file_open_18, cleanup_test_file, "file::open", "file read /tmp/testfile18 task.uid=path1.gid\t" "file write /tmp/testfile18 task.uid=path1.gid" }, { setup_all_test_file, test_file_open_18, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile18 " "task.uid=path1.gid" }, { setup_all_test_file, test_file_open_22, cleanup_test_file, "file::truncate", "file truncate /tmp/testfile22 task.uid=path1.gid" }, { NULL } }; int main(int argc, char *argv[]) { int i; ccs_test_init(); for (i = 0; tests[i].do_test; i++) { int trial; for (trial = 0; trial < 2; trial++) { int should_fail; for (should_fail = 0; should_fail < 2; should_fail++) { if (tests[i].do_setup) tests[i].do_setup(); if (!should_fail) write_domain_policy(tests[i].policy, 0); set_profile(3, tests[i].name); tests[i].do_test(); show_result(tests[i].policy, !should_fail); set_profile(0, tests[i].name); if (tests[i].do_cleanup) tests[i].do_cleanup(); if (!should_fail) write_domain_policy(tests[i].policy, 1); } } } for (i = 0; tests[i].do_test; i++) { int mode; for (mode = 0; mode < 4; mode++) { if (tests[i].do_setup) tests[i].do_setup(); set_profile(mode, tests[i].name); tests[i].do_test(); show_result(tests[i].name, 1); set_profile(0, tests[i].name); if (tests[i].do_cleanup) tests[i].do_cleanup(); } } fprintf(domain_fp, "delete %s\n", self_domain); return 0; } tomoyo-tools/kernel_test/tomoyo_transition_test.c0000644000000000000000000000737714116520000021565 0ustar rootroot/* * tomoyo_transition_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" static void check_self_domain(const char *expected) { static char buffer[4096]; static int fd = EOF; int ret_ignored; if (fd == EOF) fd = open("/sys/kernel/security/tomoyo/self_domain", O_RDONLY); memset(buffer, 0, sizeof(buffer)); /* securityfs does not accept seek operation? */ /* ret_ignored = lseek(fd, 0, SEEK_SET); */ ret_ignored = read(fd, buffer, sizeof(buffer) - 1); if (!strcmp(buffer, expected)) printf("OK\n"); else printf("FAILED (expected='%s' result='%s')\n", expected, buffer); close(fd); fd = EOF; } static void write_self_domain(const char *domain) { static int fd = EOF; int ret_ignored; if (fd == EOF) fd = open("/sys/kernel/security/tomoyo/self_domain", O_WRONLY); ret_ignored = write(fd, domain, strlen(domain)); } static void write_policy(const char *policy, _Bool delete) { static int fd = EOF; static char buf[64]; int ret_ignored; memset(buf, 0, sizeof(buf)); if (fd == EOF) fd = open("/sys/kernel/security/tomoyo/domain_policy", O_WRONLY); if (delete) snprintf(buf, sizeof(buf) - 1, "delete "); else snprintf(buf, sizeof(buf) - 1, "select pid=%u\n", pid); ret_ignored = write(fd, buf, strlen(buf)); ret_ignored = write(fd, policy, strlen(policy)); ret_ignored = write(fd, "\n", 1); if (!delete) printf("%s : ", policy); } static void stage_transit_test(void) { char buffer[1024]; memset(buffer, 0, sizeof(buffer)); set_profile(2, "file::open"); /* * task manual_domain_transition with * matched domain and matched condition */ snprintf(buffer, sizeof(buffer) - 1, "task manual_domain_transition " " //transition_test task.pid=%u", pid); write_policy(buffer, 0); write_self_domain(" //transition_test"); check_self_domain(" //transition_test"); write_policy(buffer, 1); /* * task manual_domain_transition with * matched domain and unmatched condition */ snprintf(buffer, sizeof(buffer) - 1, "task manual_domain_transition " " task.pid=%u", pid + 1); write_policy(buffer, 0); write_self_domain(""); check_self_domain(" //transition_test"); write_policy(buffer, 1); /* * task manual_domain_transition with * unmatched domain and matched condition */ snprintf(buffer, sizeof(buffer) - 1, "task manual_domain_transition " " /bad task.pid=%u", pid); write_policy(buffer, 0); write_self_domain(""); check_self_domain(" //transition_test"); write_policy(buffer, 1); /* * task manual_domain_transition with * unmatched domain and unmatched condition */ snprintf(buffer, sizeof(buffer) - 1, "task manual_domain_transition " " /bad task.pid=%u", pid + 2); write_policy(buffer, 0); write_self_domain(""); check_self_domain(" //transition_test"); write_policy(buffer, 1); } int main(int argc, char *argv[]) { ccs_test_init(); stage_transit_test(); clear_status(); if (0) { /* To suppress "defined but not used" warnings. */ write_exception_policy("", 0); write_domain_policy("", 0); } return 0; } tomoyo-tools/kernel_test/testall.sh0000755000000000000000000000355614116520000016564 0ustar rootroot#! /bin/sh # # testall.sh # # Copyright (C) 2005-2011 NTT DATA CORPORATION # # Version: 2.6.1 2021/09/10 # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License v2 as published by the # Free Software Foundation. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # cd ${0%/*} export PATH=$PWD:${PATH} echo "Testing all. (All results are reported)" newns tomoyo_accept_test newns tomoyo_filesystem_test newns tomoyo_file_test newns tomoyo_rewrite_test newns tomoyo_network_test newns tomoyo_argv0_test newns tomoyo_env_test newns tomoyo_new_file_test newns tomoyo_new_network_test newns tomoyo_new_test newns tomoyo_transition_test echo echo echo echo "Testing all. (Only ERRORS are reported)" newns tomoyo_accept_test | grep -vF Done newns tomoyo_filesystem_test | grep -vF OK | grep -F '(' newns tomoyo_file_test | grep -vF OK | grep -F '(' newns tomoyo_rewrite_test | grep -vF OK | grep -F '(' newns tomoyo_network_test | grep -vF OK | grep -F '(' newns tomoyo_argv0_test | grep -vF OK | grep -F '(' newns tomoyo_env_test | grep -vF OK | grep -F '(' newns tomoyo_new_test | grep -vF OK newns tomoyo_transition_test | grep -vF OK echo echo echo echo "Testing policy I/O. (Only ERRORS are reported)" newns tomoyo_bprm_test | grep -vF OK newns tomoyo_cond_test | grep -vF OK newns tomoyo_policy_io_test | grep -vF OK newns tomoyo_new_file_test | grep -vF OK newns tomoyo_new_network_test | grep -vF OK dmesg -c uname -r tomoyo-tools/kernel_test/tomoyo_accept_test.c0000644000000000000000000001006514116520000020616 0ustar rootroot/* * tomoyo_accept_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" static void set_level(const int i) { set_profile(i, "file::chgrp"); set_profile(i, "file::chmod"); set_profile(i, "file::chown"); set_profile(i, "file::chroot"); set_profile(i, "file::create"); set_profile(i, "file::execute"); set_profile(i, "file::ioctl"); set_profile(i, "file::link"); set_profile(i, "file::mkblock"); set_profile(i, "file::mkchar"); set_profile(i, "file::mkdir"); set_profile(i, "file::mkfifo"); set_profile(i, "file::mksock"); set_profile(i, "file::mount"); set_profile(i, "file::open"); set_profile(i, "file::pivot_root"); set_profile(i, "file::rename"); set_profile(i, "file::rmdir"); set_profile(i, "file::symlink"); set_profile(i, "file::truncate"); set_profile(i, "file::unmount"); set_profile(i, "file::unlink"); set_profile(i, "file::getattr"); } static void test(int rw_loop, int truncate_loop, int append_loop, int create_loop) { static const int rw_flags[4] = { 0, O_RDONLY, O_WRONLY, O_RDWR }; static const int create_flags[3] = { 0, O_CREAT /* nonexistent*/ , O_CREAT /* existent */ }; static const int truncate_flags[2] = { 0, O_TRUNC }; static const int append_flags[2] = { 0, O_APPEND }; int level; int flags; int fd; static char buffer[1024]; memset(buffer, 0, sizeof(buffer)); snprintf(buffer, sizeof(buffer) - 1, "/tmp/file:a=%d:t=%d:c=%d:m=%d", append_loop, truncate_loop, create_loop, rw_loop); flags = rw_flags[rw_loop] | truncate_flags[truncate_loop] | append_flags[append_loop] | create_flags[create_loop]; fprintf(domain_fp, "delete file read/write/append/execute/truncate %s\n", buffer); fprintf(domain_fp, "delete file create %s 0644\n", buffer); for (level = 0; level < 4; level++) { set_level(0); if (create_loop == 1) unlink(buffer); else close(open(buffer, O_CREAT, 0644)); set_level(level); fd = open(buffer, flags, 0644); if (fd != EOF) close(fd); else fprintf(stderr, "%d: open(%04o) failed\n", level, flags); /* fd = open(buffer, flags, 0644) if (fd != EOF) close(fd); else fprintf(stderr, "%d: open(%04o) failed\n", level, flags); */ /* fd = open(buffer, flags, 0644); if (fd != EOF) close(fd); else fprintf(stderr, "%d: open(%04o) failed\n", level, flags); */ } fprintf(domain_fp, "delete file read/write/append/execute/truncate/getattr %s\n", buffer); fprintf(domain_fp, "delete file create %s 0644\n", buffer); fd = open(buffer, flags, 0644); if (fd != EOF) { close(fd); fprintf(stderr, "%d: open(%04o) didn't fail\n", 3, flags); } } int main(int argc, char *argv[]) { ccs_test_init(); set_profile(0, "file"); fprintf(profile_fp, "255-PREFERENCE={ max_learning_entry=2048 }\n"); { int append_loop; for (append_loop = 0; append_loop < 2; append_loop++) { int truncate_loop; for (truncate_loop = 0; truncate_loop < 2; truncate_loop++) { int create_loop; for (create_loop = 0; create_loop < 3; create_loop++) { int rw_loop; for (rw_loop = 0; rw_loop < 4; rw_loop++) test(rw_loop, truncate_loop, append_loop, create_loop); } } } } fprintf(profile_fp, "255-CONFIG::file={ mode=disabled }\n"); printf("Done\n"); clear_status(); if (0) { /* To suppress "defined but not used" warnings. */ write_domain_policy("", 0); write_exception_policy("", 0); } return 0; } tomoyo-tools/kernel_test/tomoyo_bprm_test.c0000644000000000000000000001364314116520000020324 0ustar rootroot/* * tomoyo_bprm_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" static void try_exec(const char *policy, char *argv[], char *envp[], const char should_success) { FILE *fp; char buffer[8192]; int domain_found = 0; int policy_found = 0; int err = 0; int pipe_fd[2] = { EOF, EOF }; int ret_ignored; set_profile(3, "file::open"); fp = fopen(proc_policy_domain_policy, "r"); set_profile(3, "file::open"); ret_ignored = pipe(pipe_fd); printf("%s: ", policy); fflush(stdout); fprintf(domain_fp, "%s\n", policy); if (!fp) { printf("BUG: policy read failed\n"); return; } while (fgets(buffer, sizeof(buffer) - 1, fp)) { char *cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; if (!strncmp(buffer, "", 8)) domain_found = !strcmp(self_domain, buffer); if (!domain_found) continue; /* printf("<%s>\n", buffer); */ if (!strcmp(buffer, policy)) { policy_found = 1; break; } } fclose(fp); if (!policy_found) { printf("BUG: policy write failed\n"); return; } if (fork() == 0) { execve(BINDIR "/true", argv, envp); err = errno; ret_ignored = write(pipe_fd[1], &err, sizeof(err)); _exit(0); } close(pipe_fd[1]); ret_ignored = read(pipe_fd[0], &err, sizeof(err)); close(pipe_fd[0]); fprintf(domain_fp, "delete %s\n", policy); if (should_success) { if (!err) printf("OK\n"); else printf("BUG: failed (%d)\n", err); } else { if (err == EPERM) printf("OK: Permission denied.\n"); else printf("BUG: failed (%d)\n", err); } } static void stage_exec_test(void) { int i; static char *argv[128]; static char *envp[128]; for (i = 0; i < 10; i++) { memset(argv, 0, sizeof(argv)); memset(envp, 0, sizeof(envp)); argv[0] = BINDIR "/true"; try_exec("file execute " BINDIR "/true " "task.gid=0-100 exec.argc=1", argv, envp, 1); argv[0] = NULL; try_exec("file execute " BINDIR "/true " "task.gid=0-100 exec.argc=1", argv, envp, 0); envp[0] = ""; try_exec("file execute " BINDIR "/true task.gid!=100 task.euid=0 " "path1.uid=0 path1.parent.uid=0 exec.envc=1", argv, envp, 1); envp[0] = NULL; try_exec("file execute " BINDIR "/true task.gid!=100 task.euid=0 " "path1.uid=0 path1.parent.uid=0 exec.envc=1", argv, envp, 0); argv[0] = BINDIR "/true"; argv[1] = "--"; try_exec("file execute " BINDIR "/true 0=0 exec.argc=1-5", argv, envp, 1); try_exec("file execute " BINDIR "/true 0=0 exec.argc!=1-5", argv, envp, 0); envp[0] = ""; envp[1] = ""; try_exec("file execute " BINDIR "/true task.uid=0 " "task.gid!=1-100 path1.parent.uid!=1 path1.gid=0 " "exec.envc=1-5", argv, envp, 1); try_exec("file execute " BINDIR "/true task.uid=0 " "task.gid!=1-100 path1.parent.uid!=1 path1.gid=0 " "exec.envc!=1-5", argv, envp, 0); argv[0] = BINDIR "/true"; argv[1] = "--"; try_exec("file execute " BINDIR "/true task.uid=0 task.gid=0 " "path1.parent.uid=0 path1.uid=0 exec.argv[1]=\"--\"", argv, envp, 1); try_exec("file execute " BINDIR "/true task.uid=0 task.gid=0 " "path1.parent.uid=0 path1.uid=0 exec.argv[1]!=\"--\"", argv, envp, 0); argv[0] = BINDIR "/true"; argv[1] = "-"; try_exec("file execute " BINDIR "/true 1!=0 exec.argv[1]=\"--\"", argv, envp, 0); try_exec("file execute " BINDIR "/true 1!=0 exec.argv[1]!=\"--\"", argv, envp, 1); envp[0] = "HOME=/"; try_exec("file execute " BINDIR "/true task.euid=0 " "exec.envp[\"HOME\"]!=NULL", argv, envp, 1); try_exec("file execute " BINDIR "/true task.euid=0 " "exec.envp[\"HOME\"]=NULL", argv, envp, 0); try_exec("file execute " BINDIR "/true 0!=1 " "exec.envp[\"HOME\"]=\"/\"", argv, envp, 1); try_exec("file execute " BINDIR "/true 0!=1 " "exec.envp[\"HOME\"]!=\"/\"", argv, envp, 0); envp[0] = "HOME2=/"; try_exec("file execute " BINDIR "/true path1.uid=0 " "exec.envp[\"HOME\"]!=NULL", argv, envp, 0); try_exec("file execute " BINDIR "/true path1.uid=0 " "exec.envp[\"HOME\"]=NULL", argv, envp, 1); try_exec("file execute " BINDIR "/true 100=1-1000 " "exec.envp[\"HOME\"]=\"/\"", argv, envp, 0); try_exec("file execute " BINDIR "/true 100=1-1000 " "exec.envp[\"HOME\"]!=\"/\"", argv, envp, 1); try_exec("file execute " BINDIR "/true path1.parent.gid!=100 " "exec.envp[\"HOME\"]!=NULL exec.envp[\"HOME3\"]=NULL", argv, envp, 0); try_exec("file execute " BINDIR "/true path1.parent.gid!=100 " "exec.envp[\"HOME\"]=NULL exec.envp[\"HOME3\"]=NULL", argv, envp, 1); try_exec("file execute " BINDIR "/true path1.parent.gid=0 " "exec.envp[\"HOME\"]=\"/\" exec.envp[\"HOME3\"]=NULL", argv, envp, 0); try_exec("file execute " BINDIR "/true path1.parent.gid=0 " "exec.envp[\"HOME\"]!=\"/\" exec.envp[\"HOME3\"]=NULL", argv, envp, 1); } } int main(int argc, char *argv[]) { ccs_test_init(); fprintf(domain_fp, "%s " BINDIR "/true\n", self_domain); fprintf(domain_fp, "use_profile 255\n"); fprintf(domain_fp, "use_group 0\n"); fprintf(domain_fp, "select pid=%u\n", pid); fprintf(domain_fp, "file read/write %s\n", proc_policy_domain_policy); set_profile(3, "file::execute"); stage_exec_test(); set_profile(3, "file::execute"); clear_status(); if (0) { /* To suppress "defined but not used" warnings. */ write_domain_policy("", 0); write_exception_policy("", 0); } return 0; } tomoyo-tools/kernel_test/fcntl.c0000644000000000000000000000655414116520000016030 0ustar rootroot#include #include #include #include #include #include int main(int argc, char *argv[]) { int fd; system("touch " "/tmp/O_RDONLY /tmp/O_RDONLY+O_APPEND " "/tmp/O_WRONLY /tmp/O_WRONLY+O_APPEND " "/tmp/O_RDWR /tmp/O_RDWR+O_APPEND " "/tmp/O_IOCTL /tmp/O_IOCTL+O_APPEND"); fprintf(stderr, "file read /tmp/O_RDONLY\n"); fd = open("/tmp/O_RDONLY", O_RDONLY); fprintf(stderr, "file append /tmp/O_RDONLY\n"); if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_APPEND) == EOF) fprintf(stderr, "fcntl(O_APPEND) rejected for O_RDONLY.\n"); if (write(fd, "", 1) != EOF) fprintf(stderr, "write() not rejected for " "O_RDONLY | O_APPEND.\n"); close(fd); fprintf(stderr, "file read /tmp/O_RDONLY+O_APPEND\n"); fd = open("/tmp/O_RDONLY+O_APPEND", O_RDONLY | O_APPEND); fprintf(stderr, "file write /tmp/O_RDONLY+O_APPEND\n"); if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_APPEND) == EOF) fprintf(stderr, "fcntl(~O_APPEND) rejected for " "O_RDONLY | O_APPEND.\n"); if (write(fd, "", 1) != EOF) fprintf(stderr, "write() not rejected for O_RDONLY.\n"); close(fd); fprintf(stderr, "file write /tmp/O_WRONLY\n"); fd = open("/tmp/O_WRONLY", O_WRONLY); fprintf(stderr, "file append /tmp/O_WRONLY\n"); if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_APPEND) == EOF) fprintf(stderr, "fcntl(O_APPEND) rejected for O_WRONLY.\n"); if (write(fd, "", 1) == EOF) fprintf(stderr, "write() rejected for O_WRONLY | O_APPEND.\n"); close(fd); fprintf(stderr, "file append /tmp/O_WRONLY+O_APPEND\n"); fd = open("/tmp/O_WRONLY+O_APPEND", O_WRONLY | O_APPEND); fprintf(stderr, "file write /tmp/O_WRONLY+O_APPEND\n"); if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_APPEND) == EOF) fprintf(stderr, "fcntl(~O_APPEND) rejected for " "O_WRONLY | O_APPEND.\n"); if (write(fd, "", 1) == EOF) fprintf(stderr, "write() rejected for O_WRONLY.\n"); close(fd); fprintf(stderr, "file read/write /tmp/O_RDWR\n"); fd = open("/tmp/O_RDWR", O_RDWR); fprintf(stderr, "file append /tmp/O_RDWR\n"); if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_APPEND) == EOF) fprintf(stderr, "fcntl(O_APPEND) rejected for O_RDWR.\n"); if (write(fd, "", 1) == EOF) fprintf(stderr, "write() rejected for O_RDWR | O_APPEND.\n"); close(fd); fprintf(stderr, "file read/append /tmp/O_RDWR+O_APPEND\n"); fd = open("/tmp/O_RDWR+O_APPEND", O_RDWR | O_APPEND); fprintf(stderr, "file write /tmp/O_RDWR+O_APPEND\n"); if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_APPEND) == EOF) fprintf(stderr, "fcntl(~O_APPEND) rejected for " "O_RDWR | O_APPEND.\n"); if (write(fd, "", 1) == EOF) fprintf(stderr, "write() rejected for O_RDWR.\n"); close(fd); fd = open("/tmp/O_IOCTL", 3); fprintf(stderr, "file append /tmp/O_IOCTL\n"); if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_APPEND) == EOF) fprintf(stderr, "fcntl(O_APPEND) rejected for O_IOCTL.\n"); if (write(fd, "", 1) != EOF) fprintf(stderr, "write() not rejected for " "O_IOCTL | O_APPEND.\n"); close(fd); fd = open("/tmp/O_IOCTL+O_APPEND", 3 | O_APPEND); fprintf(stderr, "file write /tmp/O_IOCTL+O_APPEND\n"); if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_APPEND) == EOF) fprintf(stderr, "fcntl(~O_APPEND) rejected for " "O_IOCTL | O_APPEND.\n"); if (write(fd, "", 1) != EOF) fprintf(stderr, "write() not rejected for O_IOCTL.\n"); close(fd); return 0; } tomoyo-tools/kernel_test/tomoyo_new_network_test.c0000644000000000000000000003040114116520000021715 0ustar rootroot/* * tomoyo_new_network_test.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "include.h" static const char *policy = ""; static int write_policy(void) { FILE *fp = fopen(proc_policy_domain_policy, "r"); char buffer[8192]; char *cp; int domain_found = 0; int policy_found = 0; memset(buffer, 0, sizeof(buffer)); fprintf(domain_fp, "%s\n", policy); if (!fp) { printf("%s : BUG: policy read failed\n", policy); return 0; } while (fgets(buffer, sizeof(buffer) - 1, fp)) { cp = strchr(buffer, '\n'); if (cp) *cp = '\0'; if (!strncmp(buffer, "", 8)) domain_found = !strcmp(self_domain, buffer); if (domain_found) { if (!strcmp(buffer, policy)) { policy_found = 1; break; } } } fclose(fp); if (!policy_found) { printf("%s : BUG: policy write failed\n", policy); return 0; } errno = 0; return 1; } static void delete_policy(void) { fprintf(domain_fp, "delete %s\n", policy); } static void show_result(int result, char should_success) { printf("%s : ", policy); if (should_success) { if (result != EOF) printf("OK\n"); else printf("FAILED: %s\n", strerror(errno)); } else { if (result == EOF) { if (errno == EPERM) printf("OK: Permission denied.\n"); else printf("FAILED: %s\n", strerror(errno)); } else { printf("BUG\n"); } } } static void stage_network_test(void) { int i; { /* IPv4 stream */ char buffer[1024]; struct sockaddr_in saddr; struct sockaddr_in caddr; socklen_t size = sizeof(saddr); int fd1 = socket(PF_INET, SOCK_STREAM, 0); int fd2 = socket(PF_INET, SOCK_STREAM, 0); memset(buffer, 0, sizeof(buffer)); policy = buffer; memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); saddr.sin_port = htons(0); snprintf(buffer, sizeof(buffer) - 1, "network inet stream bind 127.0.0.1 0-1"); errno = 0; show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr)), 0); if (write_policy()) { show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr)), 1); delete_policy(); } getsockname(fd1, (struct sockaddr *) &saddr, &size); snprintf(buffer, sizeof(buffer) - 1, "network inet stream listen 127.0.0.0-127.255.255.255 " "%u-%u", ntohs(saddr.sin_port) - 1, ntohs(saddr.sin_port) + 1); errno = 0; show_result(listen(fd1, 5), 0); if (write_policy()) { show_result(listen(fd1, 5), 1); delete_policy(); } snprintf(buffer, sizeof(buffer) - 1, "network inet stream connect 127.0.0.1 %u-%u", ntohs(saddr.sin_port) - 1, ntohs(saddr.sin_port) + 1); errno = 0; show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr)), 0); if (write_policy()) { show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr)), 1); delete_policy(); } getsockname(fd2, (struct sockaddr *) &caddr, &size); close(fd2); fd2 = socket(PF_INET, SOCK_STREAM, 0); snprintf(buffer, sizeof(buffer) - 1, "network inet stream connect " "127.0.0.0-127.255.255.255 %u-%u", ntohs(saddr.sin_port) - 1, ntohs(saddr.sin_port) + 1); if (write_policy()) { connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr)); delete_policy(); } getsockname(fd2, (struct sockaddr *) &caddr, &size); if (fd2 != EOF) close(fd2); if (fd1 != EOF) close(fd1); } { /* IPv4 address_group */ char buffer[1024]; int fd1 = socket(PF_INET, SOCK_STREAM, 0); int fd2 = socket(PF_INET, SOCK_STREAM, 0); struct sockaddr_in saddr; memset(buffer, 0, sizeof(buffer)); policy = buffer; memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); saddr.sin_port = htons(10001); fprintf(exception_fp, "address_group TESTADDRESS 127.0.0.1\n"); snprintf(buffer, sizeof(buffer) - 1, "network inet stream bind @TESTADDRESS 10001"); errno = 0; show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr)), 0); if (write_policy()) { show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr)), 1); delete_policy(); } fprintf(exception_fp, "delete address_group TESTADDRESS 127.0.0.1\n"); saddr.sin_port = htons(20002); fprintf(exception_fp, "address_group TESTADDRESS " "127.0.0.0-127.0.0.2\n"); snprintf(buffer, sizeof(buffer) - 1, "network inet stream bind @TESTADDRESS 20002"); errno = 0; show_result(bind(fd2, (struct sockaddr *) &saddr, sizeof(saddr)), 0); if (write_policy()) { show_result(bind(fd2, (struct sockaddr *) &saddr, sizeof(saddr)), 1); delete_policy(); } fprintf(exception_fp, "delete address_group TESTADDRESS " "127.0.0.0-127.0.0.2\n"); if (fd1 != EOF) close(fd1); if (fd2 != EOF) close(fd2); } i = socket(PF_INET6, SOCK_STREAM, 0); if (i == EOF) return; close(i); { /* IPv6 stream */ char buffer[1024]; struct sockaddr_in6 saddr, caddr; socklen_t size = sizeof(saddr); int fd1 = socket(PF_INET6, SOCK_STREAM, 0); int fd2 = socket(PF_INET6, SOCK_STREAM, 0); memset(buffer, 0, sizeof(buffer)); policy = buffer; memset(&saddr, 0, sizeof(saddr)); saddr.sin6_family = AF_INET6; saddr.sin6_addr = in6addr_loopback; saddr.sin6_port = htons(0); snprintf(buffer, sizeof(buffer) - 1, "network inet stream bind ::1 0-1"); errno = 0; show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr)), 0); if (write_policy()) { show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr)), 1); delete_policy(); } getsockname(fd1, (struct sockaddr *) &saddr, &size); snprintf(buffer, sizeof(buffer) - 1, "network inet stream listen ::-::ff %u-%u", ntohs(saddr.sin6_port) - 1, ntohs(saddr.sin6_port) + 1); errno = 0; show_result(listen(fd1, 5), 0); if (write_policy()) { show_result(listen(fd1, 5), 1); delete_policy(); } snprintf(buffer, sizeof(buffer) - 1, "network inet stream connect ::1 %u-%u", ntohs(saddr.sin6_port) - 1, ntohs(saddr.sin6_port) + 1); errno = 0; show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr)), 0); if (write_policy()) { show_result(connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr)), 1); delete_policy(); } getsockname(fd2, (struct sockaddr *) &caddr, &size); close(fd2); fd2 = socket(PF_INET6, SOCK_STREAM, 0); snprintf(buffer, sizeof(buffer) - 1, "network inet stream " "connect ::-::ff %u-%u", ntohs(saddr.sin6_port) - 1, ntohs(saddr.sin6_port) + 1); if (write_policy()) { connect(fd2, (struct sockaddr *) &saddr, sizeof(saddr)); delete_policy(); } getsockname(fd2, (struct sockaddr *) &caddr, &size); if (fd2 != EOF) close(fd2); if (fd1 != EOF) close(fd1); } { /* IPv6 address_group */ char buffer[1024]; int fd1 = socket(PF_INET6, SOCK_STREAM, 0); int fd2 = socket(PF_INET6, SOCK_STREAM, 0); struct sockaddr_in6 saddr; memset(buffer, 0, sizeof(buffer)); policy = buffer; memset(&saddr, 0, sizeof(saddr)); saddr.sin6_family = AF_INET6; saddr.sin6_addr = in6addr_loopback; saddr.sin6_port = htons(30003); fprintf(exception_fp, "address_group TESTADDRESS ::1\n"); snprintf(buffer, sizeof(buffer) - 1, "network inet stream bind @TESTADDRESS 30003"); errno = 0; show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr)), 0); if (write_policy()) { show_result(bind(fd1, (struct sockaddr *) &saddr, sizeof(saddr)), 1); delete_policy(); } fprintf(exception_fp, "delete address_group " "TESTADDRESS ::1\n"); saddr.sin6_port = htons(40004); fprintf(exception_fp, "address_group TESTADDRESS " "::-::2\n"); snprintf(buffer, sizeof(buffer) - 1, "network inet stream bind @TESTADDRESS 40004"); errno = 0; show_result(bind(fd2, (struct sockaddr *) &saddr, sizeof(saddr)), 0); if (write_policy()) { show_result(bind(fd2, (struct sockaddr *) &saddr, sizeof(saddr)), 1); delete_policy(); } fprintf(exception_fp, "delete address_group TESTADDRESS " "::-::2\n"); if (fd1 != EOF) close(fd1); if (fd2 != EOF) close(fd2); } } static void do_unix_bind_test(int i, int protocol, const char *proto_str, int should_success) { struct { unsigned short int family; char address[512]; } buf = { AF_UNIX, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }; int fd = socket(PF_UNIX, protocol, 0); int ret; int err; if (fd == EOF && errno == ESOCKTNOSUPPORT) return; printf("Testing network unix %s bind with %d bytes (%s): ", proto_str, i, should_success ? "should success" : "must fail"); if (i > 2) { buf.address[i - 2] = '\0'; unlink(buf.address); buf.address[i - 2] = 'a'; } errno = 0; ret = bind(fd, (struct sockaddr *) &buf, i); err = errno; close(fd); if (i > 2) { buf.address[i - 2] = '\0'; unlink(buf.address); buf.address[i - 2] = 'a'; } if (should_success) { if (ret == EOF && err != EINVAL) printf("Failed. %s\n", strerror(err)); else printf("OK\n"); } else { if (ret != EOF || (err != EPERM && err != EINVAL)) printf("BUG! %s\n", strerror(err)); else printf("OK: Permission denied.\n"); } } static void update_policy(int i, const char *proto_str, int is_delete) { if (is_delete) fprintf(domain_fp, "delete "); fprintf(domain_fp, "network unix %s bind ", proto_str); if (i > 2) { char buf[512] = { }; memset(buf, 'a', i - 2); fprintf(domain_fp, "%s\n", buf); } else { fprintf(domain_fp, "%s\n", "anonymous"); } } static void stage_unix_network_test(void) { int j; int i; const char *profile_str; const char *proto_str; int proto; for (j = 0; j < 3; j++) { switch (j) { case 0: profile_str = "network::unix_stream_bind"; proto_str = "stream"; proto = SOCK_STREAM; break; case 1: profile_str = "network::unix_dgram_bind"; proto_str = "dgram"; proto = SOCK_DGRAM; break; default: profile_str = "network::unix_seqpacket_bind"; proto_str = "seqpacket"; proto = SOCK_SEQPACKET; break; } for (i = 0; i <= 130; i++) { if (i >= 5 && i <= 104) continue; set_profile(0, profile_str); do_unix_bind_test(i, proto, proto_str, 1); set_profile(3, profile_str); do_unix_bind_test(i, proto, proto_str, 0); set_profile(2, profile_str); do_unix_bind_test(i, proto, proto_str, 1); set_profile(1, profile_str); do_unix_bind_test(i, proto, proto_str, 1); set_profile(3, profile_str); do_unix_bind_test(i, proto, proto_str, 1); update_policy(i, proto_str, 1); do_unix_bind_test(i, proto, proto_str, 0); update_policy(i, proto_str, 0); do_unix_bind_test(i, proto, proto_str, 1); update_policy(i, proto_str, 1); } set_profile(0, profile_str); } } int main(int argc, char *argv[]) { ccs_test_init(); set_profile(3, "network::inet_stream_bind"); set_profile(3, "network::inet_stream_listen"); set_profile(3, "network::inet_stream_connect"); set_profile(3, "network::inet_dgram_bind"); set_profile(3, "network::inet_dgram_send"); set_profile(3, "network::inet_raw_bind"); set_profile(3, "network::inet_raw_send"); fprintf(profile_fp, "255-PREFERENCE={ max_reject_log=1024 }\n"); stage_network_test(); stage_unix_network_test(); clear_status(); if (0) { /* To suppress "defined but not used" warnings. */ write_domain_policy("", 0); write_exception_policy("", 0); } return 0; } tomoyo-tools/kernel_test/arglog.c0000644000000000000000000000327014116520000016165 0ustar rootroot/* * arglog.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include int main(int argc0, char *argv0[]) { int fd = open("/sys/kernel/security/tomoyo/audit", O_RDONLY); char *argv[12]; char *envp[12]; int j; memset(argv, 0, sizeof(argv)); memset(envp, 0, sizeof(envp)); for (j = 0; j < 11; j++) { char buffer1[8192]; char buffer2[8192]; int i; /* printf("%d\n", j); */ memset(buffer1, 0, sizeof(buffer1)); memset(buffer2, 0, sizeof(buffer2)); for (i = 0; i < 11; i++) { argv[i] = "arg"; envp[i] = "env"; } argv[j] = buffer1; envp[j] = buffer2; for (i = 0; i < sizeof(buffer1) - 1; i++) { int len; char buffer[8192]; buffer1[i] = '\n'; buffer2[i] = '\t'; if (fork() == 0) { execve(BINDIR "/true", argv, envp); _exit(0); } while ((len = read(fd, buffer, sizeof(buffer))) > 0) write(1, buffer, len); wait(NULL); } } close(fd); return 0; } tomoyo-tools/kernel_test/patterntest.c0000644000000000000000000003235714116520000017277 0ustar rootroot/* * patterntest.c * * Copyright (C) 2005-2011 NTT DATA CORPORATION * * Version: 2.6.1 2021/09/10 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #define bool _Bool #define true 1 #define false 0 #define s8 char #define u8 unsigned char #define u16 unsigned short int #define u32 unsigned int struct ccs_path_info { const char *name; u32 hash; /* = full_name_hash(name, strlen(name)) */ u16 total_len; /* = strlen(name) */ u16 const_len; /* = ccs_const_part_length(name) */ bool is_dir; /* = ccs_strendswith(name, "/") */ bool is_patterned; /* = const_len < total_len */ }; /* Copied from kernel source. */ static inline unsigned long partial_name_hash(unsigned long c, unsigned long prevhash) { return (prevhash + (c << 4) + (c >> 4)) * 11; } /* Copied from kernel source. */ static inline unsigned int full_name_hash(const unsigned char *name, unsigned int len) { unsigned long hash = 0; while (len--) hash = partial_name_hash(*name++, hash); return (unsigned int) hash; } static bool ccs_pathcmp(const struct ccs_path_info *a, const struct ccs_path_info *b) { return a->hash != b->hash || strcmp(a->name, b->name); } static bool ccs_is_byte_range(const char *str) { return *str >= '0' && *str++ <= '3' && *str >= '0' && *str++ <= '7' && *str >= '0' && *str <= '7'; } static bool ccs_is_decimal(const char c) { return c >= '0' && c <= '9'; } static bool ccs_is_hexadecimal(const char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } static bool ccs_is_alphabet_char(const char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } static u8 ccs_make_byte(const u8 c1, const u8 c2, const u8 c3) { return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); } static bool ccs_is_correct_path(const char *filename, const s8 start_type, const s8 pattern_type, const s8 end_type) { const char *const start = filename; bool in_repetition = false; bool contains_pattern = false; unsigned char c; unsigned char d; unsigned char e; if (!filename) goto out; c = *filename; if (start_type == 1) { /* Must start with '/' */ if (c != '/') goto out; } else if (start_type == -1) { /* Must not start with '/' */ if (c == '/') goto out; } if (c) c = *(filename + strlen(filename) - 1); if (end_type == 1) { /* Must end with '/' */ if (c != '/') goto out; } else if (end_type == -1) { /* Must not end with '/' */ if (c == '/') goto out; } while (true) { c = *filename++; if (!c) break; if (c == '\\') { c = *filename++; switch (c) { case '\\': /* "\\" */ continue; case '$': /* "\$" */ case '+': /* "\+" */ case '?': /* "\?" */ case '*': /* "\*" */ case '@': /* "\@" */ case 'x': /* "\x" */ case 'X': /* "\X" */ case 'a': /* "\a" */ case 'A': /* "\A" */ case '-': /* "\-" */ if (pattern_type == -1) break; /* Must not contain pattern */ contains_pattern = true; continue; case '{': /* "/\{" */ if (filename - 3 < start || *(filename - 3) != '/') break; if (pattern_type == -1) break; /* Must not contain pattern */ contains_pattern = true; in_repetition = true; continue; case '}': /* "\}/" */ if (*filename != '/') break; if (!in_repetition) break; in_repetition = false; continue; case '0': /* "\ooo" */ case '1': case '2': case '3': d = *filename++; if (d < '0' || d > '7') break; e = *filename++; if (e < '0' || e > '7') break; c = ccs_make_byte(c, d, e); if (c <= ' ' || c >= 127) continue; } goto out; } else if (in_repetition && c == '/') { goto out; } else if (c <= ' ' || c >= 127) { goto out; } } if (pattern_type == 1) { /* Must contain pattern */ if (!contains_pattern) goto out; } if (in_repetition) goto out; return true; out: return false; } static int ccs_const_part_length(const char *filename) { int len = 0; if (filename) { while (true) { char c = *filename++; if (!c) break; if (c != '\\') { len++; continue; } c = *filename++; switch (c) { case '\\': /* "\\" */ len += 2; continue; case '0': /* "\ooo" */ case '1': case '2': case '3': c = *filename++; if (c < '0' || c > '7') break; c = *filename++; if (c < '0' || c > '7') break; len += 4; continue; } break; } } return len; } static bool ccs_file_matches_pattern2(const char *filename, const char *filename_end, const char *pattern, const char *pattern_end) { while (filename < filename_end && pattern < pattern_end) { char c; if (*pattern != '\\') { if (*filename++ != *pattern++) return false; continue; } c = *filename; pattern++; switch (*pattern) { int i; int j; case '?': if (c == '/') { return false; } else if (c == '\\') { if (filename[1] == '\\') filename++; else if (ccs_is_byte_range(filename + 1)) filename += 3; else return false; } break; case '\\': if (c != '\\') return false; if (*++filename != '\\') return false; break; case '+': if (!ccs_is_decimal(c)) return false; break; case 'x': if (!ccs_is_hexadecimal(c)) return false; break; case 'a': if (!ccs_is_alphabet_char(c)) return false; break; case '0': case '1': case '2': case '3': if (c == '\\' && ccs_is_byte_range(filename + 1) && !strncmp(filename + 1, pattern, 3)) { filename += 3; pattern += 2; break; } return false; /* Not matched. */ case '*': case '@': for (i = 0; i <= filename_end - filename; i++) { if (ccs_file_matches_pattern2(filename + i, filename_end, pattern + 1, pattern_end)) return true; c = filename[i]; if (c == '.' && *pattern == '@') break; if (c != '\\') continue; if (filename[i + 1] == '\\') i++; else if (ccs_is_byte_range(filename + i + 1)) i += 3; else break; /* Bad pattern. */ } return false; /* Not matched. */ default: j = 0; c = *pattern; if (c == '$') { while (ccs_is_decimal(filename[j])) j++; } else if (c == 'X') { while (ccs_is_hexadecimal(filename[j])) j++; } else if (c == 'A') { while (ccs_is_alphabet_char(filename[j])) j++; } for (i = 1; i <= j; i++) { if (ccs_file_matches_pattern2(filename + i, filename_end, pattern + 1, pattern_end)) return true; } return false; /* Not matched or bad pattern. */ } filename++; pattern++; } while (*pattern == '\\' && (*(pattern + 1) == '*' || *(pattern + 1) == '@')) pattern += 2; return filename == filename_end && pattern == pattern_end; } static bool ccs_file_matches_pattern(const char *filename, const char *filename_end, const char *pattern, const char *pattern_end) { const char *pattern_start = pattern; bool first = true; bool result; while (pattern < pattern_end - 1) { /* Split at "\-" pattern. */ if (*pattern++ != '\\' || *pattern++ != '-') continue; result = ccs_file_matches_pattern2(filename, filename_end, pattern_start, pattern - 2); if (first) result = !result; if (result) return false; first = false; pattern_start = pattern; } result = ccs_file_matches_pattern2(filename, filename_end, pattern_start, pattern_end); return first ? result : !result; } static bool ccs_path_matches_pattern2(const char *f, const char *p) { const char *f_delimiter; const char *p_delimiter; while (*f && *p) { f_delimiter = strchr(f, '/'); if (!f_delimiter) f_delimiter = f + strlen(f); p_delimiter = strchr(p, '/'); if (!p_delimiter) p_delimiter = p + strlen(p); if (*p == '\\' && *(p + 1) == '{') goto recursive; if (!ccs_file_matches_pattern(f, f_delimiter, p, p_delimiter)) return false; f = f_delimiter; if (*f) f++; p = p_delimiter; if (*p) p++; } /* Ignore trailing "\*" and "\@" in @pattern. */ while (*p == '\\' && (*(p + 1) == '*' || *(p + 1) == '@')) p += 2; return !*f && !*p; recursive: /* * The "\{" pattern is permitted only after '/' character. * This guarantees that below "*(p - 1)" is safe. * Also, the "\}" pattern is permitted only before '/' character * so that "\{" + "\}" pair will not break the "\-" operator. */ if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') return false; /* Bad pattern. */ do { /* Compare current component with pattern. */ if (!ccs_file_matches_pattern(f, f_delimiter, p + 2, p_delimiter - 2)) break; /* Proceed to next component. */ f = f_delimiter; if (!*f) break; f++; /* Continue comparison. */ if (ccs_path_matches_pattern2(f, p_delimiter + 1)) return true; f_delimiter = strchr(f, '/'); } while (f_delimiter); return false; /* Not matched. */ } static bool ccs_path_matches_pattern(const struct ccs_path_info *filename, const struct ccs_path_info *pattern) { const char *f = filename->name; const char *p = pattern->name; const int len = pattern->const_len; /* If @pattern doesn't contain pattern, I can use strcmp(). */ if (!pattern->is_patterned) return !ccs_pathcmp(filename, pattern); /* Don't compare directory and non-directory. */ if (filename->is_dir != pattern->is_dir) return false; /* Compare the initial length without patterns. */ if (strncmp(f, p, len)) return false; f += len; p += len; return ccs_path_matches_pattern2(f, p); } static void ccs_fill_path_info(struct ccs_path_info *ptr) { const char *name = ptr->name; const int len = strlen(name); ptr->total_len = len; ptr->const_len = ccs_const_part_length(name); ptr->is_dir = len && (name[len - 1] == '/'); ptr->is_patterned = (ptr->const_len < len); ptr->hash = full_name_hash(name, len); } static const struct { const char *pathname; const char *pattern; const bool match; } testcases[] = { { BINDIR "/true", BINDIR "/\\*", 1 }, { BINDIR "/true", BINDIR "\\@\\*/\\*", 1 }, { "/usr/local/", "/usr/\\*/", 1 }, { "/usr/local/", "/usr/\\*\\*\\@\\*/", 1 }, { "pipe:[12345]", "pipe:[\\$]", 1 }, { "socket:[12345]", "socket:[\\$]", 1 }, { "https://tomoyo.osdn.jp/", "\\*/\\*/\\*/", 1 }, { "https://tomoyo.osdn.jp/index.html", "\\*/\\*/\\*/\\*", 1 }, { "https://tomoyo.osdn.jp/index.html", "\\*/\\*/\\*/\\*\\*\\@\\*\\@", 1 }, { "https://tomoyo.osdn.jp/index.html", "\\*/\\@\\*/\\*\\@/\\*\\@\\*\\@\\*", 1 }, { "https://tomoyo.osdn.jp/1.7/index.html", "https://\\{\\*\\}/\\@.html", 1 }, { "https://tomoyo.osdn.jp/index.html", "\\*://\\@.osdn.jp/\\*", 1 }, { "https://tomoyo.osdn.jp/index.html", "\\*://\\@.osdn.jp/\\*", 1 }, { "https://osdn.jp/projects/tomoyo/svn/view/trunk/1.7.x/" "ccs-patch/security/ccsecurity/?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\}/?root=tomoyo", 1 }, { "https://osdn.jp/projects/tomoyo/svn/view/trunk/1.7.x/" "ccs-patch/security/?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\}/?root=tomoyo", 1 }, { "https://osdn.jp/projects/tomoyo/svn/view/trunk/1.7.x/" "ccs-patch/?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\}/?root=tomoyo", 1 }, { "https://osdn.jp/projects/tomoyo/svn/view/trunk/1.7.x/" "/ccs-patch///security//ccsecurity///?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\-.\\-..\\-\\*%\\*\\}/" "?root=tomoyo\\*\\*", 1 }, { "/var/www/html/test/test/test/index.html", "/var/www/html/\\{test\\}/\\*.html", 1 }, { "/etc/skel/", "/etc/\\{\\*\\}/\\*/", 0 }, { "/etc/passwd", "/etc/\\{\\*\\}/\\*", 0 }, { BINDIR "/true", BINDIR "/\\*/", 0 }, { BINDIR "/", BINDIR "/\\*", 0 }, { BINDIR "/", BINDIR "/\\@", 0 }, { BINDIR "/", BINDIR "/\\@\\@", 0 }, { "https://tomoyo.osdn.jp/", "\\*/\\*/\\*/\?", 0 }, { "https://tomoyo.osdn.jp/index.html", "\\*/\\*/\\*/\\@", 0 }, { "https://tomoyo.osdn.jp/index.html", "https://\\*/\\@", 0 }, { "socket:[12345]", "/\\{\\*\\}/socket:[\\*]", 0 }, { NULL, NULL, 0 } }; int main(int argc, char *argv[]) { int i; struct ccs_path_info f; struct ccs_path_info p; for (i = 0; testcases[i].pathname; i++) { f.name = testcases[i].pathname; p.name = testcases[i].pattern; if (!ccs_is_correct_path(f.name, 0, -1, 0)) { printf("Bad pathname: %s\n", f.name); continue; } else if (!ccs_is_correct_path(p.name, 0, 0, 0)) { printf("Bad pattern: %s\n", p.name); continue; } ccs_fill_path_info(&f); ccs_fill_path_info(&p); if (ccs_path_matches_pattern(&f, &p) == testcases[i].match) continue; printf("Bad result: %s %s %u\n", f.name, p.name, testcases[i].match); return 1; } printf("OK\n"); return 0; }