This patch contains code for checking file access. Functions are separated by type of parameters. (1) Hooks which take one pathname. They are read/write/execute/unlink/rmdir/truncate/symlink/rewrite/chroot/ umount. (2) Hooks which take one pathname and three numeric parameters. They are mkblock/mkchar. (3) Hooks which take two pathnames. They are link/rename/pivot_root. (4) Hooks which take one pathname and one numeric parameter. They are create/mkdir/mkfifo/mksock/ioctl/chmod/chown/chgrp. (5) Hooks which take three pathname and one numeric parameter. That is mount, and handled by separate patch. I want to call may_create() and may_delete() for directory modification operations in order to perform DAC checks before MAC. (For now, I'm not calling may_create() and may_delete().) Signed-off-by: Tetsuo Handa --- security/tomoyo/new-file.c | 2249 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2249 insertions(+) --- /dev/null +++ security-testing-2.6/security/tomoyo/new-file.c @@ -0,0 +1,2249 @@ +/* + * security/tomoyo/file.c + * + * Copyright (C) 2005-2009 NTT DATA CORPORATION + */ +#include "internal.h" +#include +#include +#include + +#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) + +static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { + [TOMOYO_TYPE_READ_WRITE] = "read/write", + [TOMOYO_TYPE_EXECUTE] = "execute", + [TOMOYO_TYPE_READ] = "read", + [TOMOYO_TYPE_WRITE] = "write", + [TOMOYO_TYPE_UNLINK] = "unlink", + [TOMOYO_TYPE_RMDIR] = "rmdir", + [TOMOYO_TYPE_TRUNCATE] = "truncate", + [TOMOYO_TYPE_SYMLINK] = "symlink", + [TOMOYO_TYPE_REWRITE] = "rewrite", + [TOMOYO_TYPE_CHROOT] = "chroot", + [TOMOYO_TYPE_UMOUNT] = "unmount", +}; + +static const char * +tomoyo_path_number3_keyword[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = { + [TOMOYO_TYPE_MKBLOCK] = "mkblock", + [TOMOYO_TYPE_MKCHAR] = "mkchar", +}; + +static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { + [TOMOYO_TYPE_LINK] = "link", + [TOMOYO_TYPE_RENAME] = "rename", + [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root", +}; + +static const char * +tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { + [TOMOYO_TYPE_CREATE] = "create", + [TOMOYO_TYPE_MKDIR] = "mkdir", + [TOMOYO_TYPE_MKFIFO] = "mkfifo", + [TOMOYO_TYPE_MKSOCK] = "mksock", + [TOMOYO_TYPE_IOCTL] = "ioctl", + [TOMOYO_TYPE_CHMOD] = "chmod", + [TOMOYO_TYPE_CHOWN] = "chown", + [TOMOYO_TYPE_CHGRP] = "chgrp", +}; + +static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = { + [TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN, + [TOMOYO_TYPE_EXECUTE] = TOMOYO_MAC_FILE_EXECUTE, + [TOMOYO_TYPE_READ] = TOMOYO_MAC_FILE_OPEN, + [TOMOYO_TYPE_WRITE] = TOMOYO_MAC_FILE_OPEN, + [TOMOYO_TYPE_UNLINK] = TOMOYO_MAC_FILE_UNLINK, + [TOMOYO_TYPE_RMDIR] = TOMOYO_MAC_FILE_RMDIR, + [TOMOYO_TYPE_TRUNCATE] = TOMOYO_MAC_FILE_TRUNCATE, + [TOMOYO_TYPE_SYMLINK] = TOMOYO_MAC_FILE_SYMLINK, + [TOMOYO_TYPE_REWRITE] = TOMOYO_MAC_FILE_REWRITE, + [TOMOYO_TYPE_CHROOT] = TOMOYO_MAC_FILE_CHROOT, + [TOMOYO_TYPE_UMOUNT] = TOMOYO_MAC_FILE_UMOUNT, +}; + +static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = { + [TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK, + [TOMOYO_TYPE_MKCHAR] = TOMOYO_MAC_FILE_MKCHAR, +}; + +static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = { + [TOMOYO_TYPE_LINK] = TOMOYO_MAC_FILE_LINK, + [TOMOYO_TYPE_RENAME] = TOMOYO_MAC_FILE_RENAME, + [TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT, +}; + +static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { + [TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE, + [TOMOYO_TYPE_MKDIR] = TOMOYO_MAC_FILE_MKDIR, + [TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO, + [TOMOYO_TYPE_MKSOCK] = TOMOYO_MAC_FILE_MKSOCK, + [TOMOYO_TYPE_IOCTL] = TOMOYO_MAC_FILE_IOCTL, + [TOMOYO_TYPE_CHMOD] = TOMOYO_MAC_FILE_CHMOD, + [TOMOYO_TYPE_CHOWN] = TOMOYO_MAC_FILE_CHOWN, + [TOMOYO_TYPE_CHGRP] = TOMOYO_MAC_FILE_CHGRP, +}; + + +void tomoyo_put_name_union(struct tomoyo_name_union *ptr) +{ + if (!ptr) + return; + if (ptr->is_group) + tomoyo_put_path_group(ptr->group); + else + tomoyo_put_name(ptr->filename); +} + +void tomoyo_put_number_union(struct tomoyo_number_union *ptr) +{ + if (ptr && ptr->is_group) + tomoyo_put_number_group(ptr->group); +} + +bool tomoyo_compare_number_union(const unsigned long value, + const struct tomoyo_number_union *ptr) +{ + if (ptr->is_group) + return tomoyo_number_matches_group(value, value, ptr->group); + return value >= ptr->values[0] && value <= ptr->values[1]; +} + +bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, + const struct tomoyo_name_union *ptr) +{ + if (ptr->is_group) + return tomoyo_path_matches_group(name, ptr->group, 1); + return tomoyo_path_matches_pattern(name, ptr->filename); +} + +static bool tomoyo_compare_name_union_pattern +(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr, + const bool may_use_pattern) +{ + if (ptr->is_group) + return tomoyo_path_matches_group(name, ptr->group, + may_use_pattern); + if (may_use_pattern || !ptr->filename->is_patterned) + return tomoyo_path_matches_pattern(name, ptr->filename); + return false; +} + +/** + * tomoyo_path2keyword - Get the name of single path operation. + * + * @operation: Type of operation. + * + * Returns the name of single path operation. + */ +const char *tomoyo_path2keyword(const u8 operation) +{ + return (operation < TOMOYO_MAX_PATH_OPERATION) + ? tomoyo_path_keyword[operation] : NULL; +} + +/** + * tomoyo_path_number32keyword - Get the name of mkdev operation. + * + * @operation: Type of operation. + * + * Returns the name of mkdev operation. + */ +const char *tomoyo_path_number32keyword(const u8 operation) +{ + return (operation < TOMOYO_MAX_PATH_NUMBER3_OPERATION) + ? tomoyo_path_number3_keyword[operation] : NULL; +} + +/** + * tomoyo_path22keyword - Get the name of double path operation. + * + * @operation: Type of operation. + * + * Returns the name of double path operation. + */ +const char *tomoyo_path22keyword(const u8 operation) +{ + return (operation < TOMOYO_MAX_PATH2_OPERATION) + ? tomoyo_path2_keyword[operation] : NULL; +} + +const char *tomoyo_path_number2keyword(const u8 operation) +{ + return (operation < TOMOYO_MAX_PATH_NUMBER_OPERATION) + ? tomoyo_path_number_keyword[operation] : NULL; +} + +static void tomoyo_add_slash(struct tomoyo_path_info *buf) +{ + if (buf->is_dir) + return; + /* + * This is OK because tomoyo_encode() reserves space for appending "/". + */ + strcat((char *) buf->name, "/"); + tomoyo_fill_path_info(buf); +} + +/** + * tomoyo_strendswith - Check whether the token ends with the given token. + * + * @name: The token to check. + * @tail: The token to find. + * + * Returns true if @name ends with @tail, false otherwise. + */ +static bool tomoyo_strendswith(const char *name, const char *tail) +{ + int len; + if (!name || !tail) + return false; + len = strlen(name) - strlen(tail); + return len >= 0 && !strcmp(name + len, tail); +} + +/** + * tomoyo_get_realpath - Get realpath. + * + * @buf: Pointer to "struct tomoyo_path_info". + * @dentry: Pointer to "struct dentry". + * @mnt: Pointer to "struct vfsmount". + * + * Returns true success, false otherwise. + */ +static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, + struct dentry *dentry, struct vfsmount *mnt) +{ + struct path path = { mnt, dentry }; + buf->name = tomoyo_realpath_from_path(&path); + if (buf->name) { + tomoyo_fill_path_info(buf); + return true; + } + return false; +} + +static int tomoyo_update_path_acl(const u8 type, const char *filename, + struct tomoyo_domain_info * const domain, + struct tomoyo_condition *condition, + const bool is_delete); + +/** + * tomoyo_audit_path_log - Audit single path request log. + * + * @r: Pointer to "struct tomoyo_request_info". + * @operation: The name of operation. + * @filename: Pathname. + * @is_granted: True if this is a granted log. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_path_log(struct tomoyo_request_info *r, + const char *operation, const char *filename, + const bool is_granted) +{ + if (!is_granted) + tomoyo_warn_log(r, "%s %s", operation, filename); + return tomoyo_write_audit_log(is_granted, r, "allow_%s %s\n", + operation, filename); +} + +/** + * tomoyo_audit_path2_log - Audit double path request log. + * + * @r: Pointer to "struct tomoyo_request_info". + * @operation: The name of operation. + * @filename1: First pathname. + * @filename2: Second pathname. + * @is_granted: True if this is a granted log. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_path2_log(struct tomoyo_request_info *r, + const char *operation, const char *filename1, + const char *filename2, const bool is_granted) +{ + if (!is_granted) + tomoyo_warn_log(r, "%s %s %s", operation, filename1, + filename2); + return tomoyo_write_audit_log(is_granted, r, "allow_%s %s %s\n", + operation, filename1, filename2); +} + +/** + * tomoyo_audit_path_number3_log - Audit mkdev request log. + * + * @r: Pointer to "struct tomoyo_request_info". + * @operation: The name of operation. + * @filename: First pathname. + * @mode: Create mode. + * @major: Device major number. + * @minor: Device minor number. + * @is_granted: True if this is a granted log. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_path_number3_log(struct tomoyo_request_info *r, + const char *operation, + const char *filename, + const unsigned int mode, + const unsigned int major, + const unsigned int minor, + const bool is_granted) +{ + if (!is_granted) + tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename, + mode, major, minor); + return tomoyo_write_audit_log(is_granted, r, "allow_%s %s 0%o %u %u\n", + operation, filename, mode, major, minor); +} + +/** + * tomoyo_audit_path_number_log - Audit ioctl/chmod/chown/chgrp related request log. + * + * @r: Pointer to "struct tomoyo_request_info". + * @type: Type of operation. + * @filename: Pathname. + * @value: Value. + * @is_granted: True if this is a granted log. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r, + const char *operation, + const char *filename, + const char *value, + const bool is_granted) +{ + if (!is_granted) + tomoyo_warn_log(r, "%s %s %s", operation, filename, value); + return tomoyo_write_audit_log(is_granted, r, "allow_%s %s %s\n", + operation, filename, value); +} + +/* + * tomoyo_globally_readable_list is used for holding list of pathnames which + * are by default allowed to be open()ed for reading by any process. + * + * An entry is added by + * + * # echo 'allow_read /lib/libc-2.5.so' > \ + * /sys/kernel/security/tomoyo/exception_policy + * + * and is deleted by + * + * # echo 'delete allow_read /lib/libc-2.5.so' > \ + * /sys/kernel/security/tomoyo/exception_policy + * + * and all entries are retrieved by + * + * # grep ^allow_read /sys/kernel/security/tomoyo/exception_policy + * + * In the example above, any process is allowed to + * open("/lib/libc-2.5.so", O_RDONLY). + * One exception is, if the domain which current process belongs to is marked + * as "ignore_global_allow_read", current process can't do so unless explicitly + * given "allow_read /lib/libc-2.5.so" to the domain which current process + * belongs to. + */ +LIST_HEAD(tomoyo_globally_readable_list); + +/** + * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading. + * + * @filename: The filename to check. + * + * Returns true if any domain can open @filename for reading, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static bool tomoyo_is_globally_readable_file +(const struct tomoyo_path_info *filename) +{ + struct tomoyo_globally_readable_file_entry *ptr; + bool found = false; + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { + if (ptr->is_deleted || + !tomoyo_path_matches_pattern(filename, ptr->filename)) + continue; + found = true; + break; + } + return found; +} + +/** + * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list. + * + * @data: String to parse. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_globally_readable_policy(char *data, const bool is_delete) +{ + struct tomoyo_globally_readable_file_entry *entry = NULL; + struct tomoyo_globally_readable_file_entry *ptr; + struct tomoyo_globally_readable_file_entry e = { }; + int error = is_delete ? -ENOENT : -ENOMEM; + if (!tomoyo_is_correct_path(data, 1, 0, -1)) + return -EINVAL; + e.filename = tomoyo_get_name(data); + if (!e.filename) + return -ENOMEM; + if (!is_delete) + entry = kmalloc(sizeof(e), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { + if (ptr->filename != e.filename) + continue; + ptr->is_deleted = is_delete; + error = 0; + break; + } + if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) { + list_add_tail_rcu(&entry->list, + &tomoyo_globally_readable_list); + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + tomoyo_put_name(e.filename); + kfree(entry); + return error; +} + +/** + * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head) +{ + struct list_head *pos; + bool done = true; + list_for_each_cookie(pos, head->read_var2, + &tomoyo_globally_readable_list) { + struct tomoyo_globally_readable_file_entry *ptr; + ptr = list_entry(pos, + struct tomoyo_globally_readable_file_entry, + list); + if (ptr->is_deleted) + continue; + done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n", + ptr->filename->name); + if (!done) + break; + } + return done; +} + +/* + * tomoyo_pattern_list is used for holding list of pathnames which are used for + * converting pathnames to pathname patterns during learning mode. + * + * An entry is added by + * + * # echo 'file_pattern /proc/\$/mounts' > \ + * /sys/kernel/security/tomoyo/exception_policy + * + * and is deleted by + * + * # echo 'delete file_pattern /proc/\$/mounts' > \ + * /sys/kernel/security/tomoyo/exception_policy + * + * and all entries are retrieved by + * + * # grep ^file_pattern /sys/kernel/security/tomoyo/exception_policy + * + * In the example above, if a process which belongs to a domain which is in + * learning mode requested open("/proc/1/mounts", O_RDONLY), + * "allow_read /proc/\$/mounts" is automatically added to the domain which that + * process belongs to. + */ +LIST_HEAD(tomoyo_pattern_list); + +/** + * tomoyo_file_pattern - Get patterned pathname. + * + * @filename: Pointer to "struct tomoyo_path_info". + * + * Returns pointer to patterned pathname. + * + * Caller holds tomoyo_read_lock(). + */ +const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename) +{ + struct tomoyo_pattern_entry *ptr; + const struct tomoyo_path_info *pattern = NULL; + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { + if (ptr->is_deleted) + continue; + if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) + continue; + pattern = ptr->pattern; + if (tomoyo_strendswith(pattern->name, "/\\*")) { + /* Do nothing. Try to find the better match. */ + } else { + /* This would be the better match. Use this. */ + break; + } + } + return pattern ? pattern->name : filename->name; +} + +/** + * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list. + * + * @data: String to parse. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_pattern_policy(char *data, const bool is_delete) +{ + struct tomoyo_pattern_entry *entry = NULL; + struct tomoyo_pattern_entry *ptr; + struct tomoyo_pattern_entry e = { .pattern = tomoyo_get_name(data) }; + int error = is_delete ? -ENOENT : -ENOMEM; + if (!e.pattern) + return error; + if (!e.pattern->is_patterned) + goto out; + if (!is_delete) + entry = kmalloc(sizeof(e), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { + if (e.pattern != ptr->pattern) + continue; + ptr->is_deleted = is_delete; + error = 0; + break; + } + if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) { + list_add_tail_rcu(&entry->list, &tomoyo_pattern_list); + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name(e.pattern); + kfree(entry); + return error; +} + +/** + * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head) +{ + struct list_head *pos; + bool done = true; + list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) { + struct tomoyo_pattern_entry *ptr; + ptr = list_entry(pos, struct tomoyo_pattern_entry, list); + if (ptr->is_deleted) + continue; + done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN + "%s\n", ptr->pattern->name); + if (!done) + break; + } + return done; +} + +/* + * tomoyo_no_rewrite_list is used for holding list of pathnames which are by + * default forbidden to modify already written content of a file. + * + * An entry is added by + * + * # echo 'deny_rewrite /var/log/messages' > \ + * /sys/kernel/security/tomoyo/exception_policy + * + * and is deleted by + * + * # echo 'delete deny_rewrite /var/log/messages' > \ + * /sys/kernel/security/tomoyo/exception_policy + * + * and all entries are retrieved by + * + * # grep ^deny_rewrite /sys/kernel/security/tomoyo/exception_policy + * + * In the example above, if a process requested to rewrite /var/log/messages , + * the process can't rewrite unless the domain which that process belongs to + * has "allow_rewrite /var/log/messages" entry. + */ +LIST_HEAD(tomoyo_no_rewrite_list); + +/** + * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited. + * + * @filename: Filename to check. + * + * Returns true if @filename is specified by "deny_rewrite" directive, + * false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) +{ + struct tomoyo_no_rewrite_entry *ptr; + bool matched = false; + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { + if (ptr->is_deleted) + continue; + if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) + continue; + matched = true; + break; + } + return matched; +} + +/** + * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list. + * + * @data: String to parse. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete) +{ + struct tomoyo_no_rewrite_entry *entry = NULL; + struct tomoyo_no_rewrite_entry *ptr; + struct tomoyo_no_rewrite_entry e = { }; + int error = is_delete ? -ENOENT : -ENOMEM; + if (!tomoyo_is_correct_path(data, 0, 0, 0)) + return -EINVAL; + e.pattern = tomoyo_get_name(data); + if (!e.pattern) + return error; + if (!is_delete) + entry = kmalloc(sizeof(e), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { + if (ptr->pattern != e.pattern) + continue; + ptr->is_deleted = is_delete; + error = 0; + break; + } + if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) { + list_add_tail_rcu(&entry->list, &tomoyo_no_rewrite_list); + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + tomoyo_put_name(e.pattern); + kfree(entry); + return error; +} + +/** + * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) +{ + struct list_head *pos; + bool done = true; + list_for_each_cookie(pos, head->read_var2, + &tomoyo_no_rewrite_list) { + struct tomoyo_no_rewrite_entry *ptr; + ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list); + if (ptr->is_deleted) + continue; + done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE + "%s\n", ptr->pattern->name); + if (!done) + break; + } + return done; +} + +/** + * tomoyo_update_file_acl - Update file's read/write/execute ACL. + * + * @perm: Permission (between 1 to 7). + * @filename: Filename. + * @domain: Pointer to "struct tomoyo_domain_info". + * @condition: Pointer to "struct tomoyo_condition". May be NULL. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + * + * This is legacy support interface for older policy syntax. + * Current policy syntax uses "allow_read/write" instead of "6", + * "allow_read" instead of "4", "allow_write" instead of "2", + * "allow_execute" instead of "1". + */ +static inline int tomoyo_update_file_acl +(u8 perm, const char *filename, struct tomoyo_domain_info * const domain, + struct tomoyo_condition *condition, const bool is_delete) +{ + if (perm > 7 || !perm) + return -EINVAL; + if (filename[0] != '@' && tomoyo_strendswith(filename, "/")) + /* + * Only 'allow_mkdir' and 'allow_rmdir' are valid for + * directory permissions. + */ + return 0; + if (perm & 4) + tomoyo_update_path_acl(TOMOYO_TYPE_READ, filename, domain, + condition, is_delete); + if (perm & 2) + tomoyo_update_path_acl(TOMOYO_TYPE_WRITE, filename, + domain, condition, is_delete); + if (perm & 1) + tomoyo_update_path_acl(TOMOYO_TYPE_EXECUTE, filename, + domain, condition, is_delete); + return 0; +} + +/** + * tomoyo_path_acl - Check permission for single path operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @filename: Filename to check. + * @perm: Permission. + * @may_use_pattern: True if patterned ACL is permitted. + * + * Returns 0 on success, -EPERM otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_path_acl(struct tomoyo_request_info *r, + const struct tomoyo_path_info *filename, + const u16 perm, + const bool may_use_pattern) +{ + struct tomoyo_domain_info *domain = r->domain; + struct tomoyo_acl_info *ptr; + int error = -EPERM; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_acl *acl; + if (ptr->is_deleted || ptr->type != TOMOYO_TYPE_PATH_ACL) + continue; + acl = container_of(ptr, struct tomoyo_path_acl, + head); + if (!(acl->perm & perm) || !tomoyo_condition(r, ptr) || + !tomoyo_compare_name_union_pattern(filename, &acl->name, + may_use_pattern)) + continue; + r->cond = ptr->cond; + error = 0; + break; + } + return error; +} + +/** + * tomoyo_path_number3_acl - Check permission for mkdev operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @filename: Filename to check. + * @perm: Permission. + * @mode: Create mode. + * @major: Device major number. + * @minor: Device minor number. + * + * Returns 0 on success, -EPERM otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_path_number3_acl(struct tomoyo_request_info *r, + const struct tomoyo_path_info *filename, + const u16 perm, const unsigned int mode, + const unsigned int major, + const unsigned int minor) +{ + struct tomoyo_domain_info *domain = r->domain; + struct tomoyo_acl_info *ptr; + int error = -EPERM; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_number3_acl *acl; + if (ptr->is_deleted || + ptr->type != TOMOYO_TYPE_PATH_NUMBER3_ACL) + continue; + acl = container_of(ptr, struct tomoyo_path_number3_acl, head); + if (!tomoyo_compare_number_union(mode, &acl->mode)) + continue; + if (!tomoyo_compare_number_union(major, &acl->major)) + continue; + if (!tomoyo_compare_number_union(minor, &acl->minor)) + continue; + if (!(acl->perm & perm) || !tomoyo_condition(r, ptr)) + continue; + if (!tomoyo_compare_name_union(filename, &acl->name)) + continue; + r->cond = ptr->cond; + error = 0; + break; + } + return error; +} + +/** + * tomoyo_file_perm - Check permission for opening files. + * + * @r: Pointer to "strct tomoyo_request_info". + * @filename: Filename to check. + * @mode: Mode ("read" or "write" or "read/write" or "execute"). + * + * Returns 0 on success, 1 on retry, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_file_perm(struct tomoyo_request_info *r, + const struct tomoyo_path_info *filename, const u8 mode) +{ + const char *msg = ""; + int error = 0; + u16 perm = 0; + if (!filename) + return 0; + if (mode == 6) { + msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE); + perm = 1 << TOMOYO_TYPE_READ_WRITE; + } else if (mode == 4) { + msg = tomoyo_path2keyword(TOMOYO_TYPE_READ); + perm = 1 << TOMOYO_TYPE_READ; + } else if (mode == 2) { + msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE); + perm = 1 << TOMOYO_TYPE_WRITE; + } else if (mode == 1) { + msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE); + perm = 1 << TOMOYO_TYPE_EXECUTE; + } else + BUG(); + do { + error = tomoyo_path_acl(r, filename, perm, mode != 1); + if (error && mode == 4 && !r->domain->ignore_global_allow_read + && tomoyo_is_globally_readable_file(filename)) + error = 0; + tomoyo_audit_path_log(r, msg, filename->name, !error); + if (!error) + break; + error = tomoyo_supervisor(r, "allow_%s %s\n", msg, + mode == 1 ? filename->name : + tomoyo_file_pattern(filename)); + /* + * Do not retry for execute request, for aggregator may have + * changed. + */ + } while (error == 1 && !r->ee); + if (r->mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + +/** + * tomoyo_update_execute_handler - Update "struct tomoyo_execute_handler_record" list. + * + * @type: Type of execute handler. + * @filename: Pathname to the execute handler. + * @domain: Pointer to "struct tomoyo_domain_info". + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +static inline int tomoyo_update_execute_handler +(const u8 type, const char *filename, struct tomoyo_domain_info * const domain, + const bool is_delete) +{ + struct tomoyo_acl_info *ptr; + struct tomoyo_execute_handler_record e = { .head.type = type }; + struct tomoyo_execute_handler_record *entry = NULL; + int error = is_delete ? -ENOENT : -ENOMEM; + if (!domain) + return -EINVAL; + if (!tomoyo_is_correct_path(filename, 1, -1, -1)) + return -EINVAL; + e.handler = tomoyo_get_name(filename); + if (!e.handler) + return -ENOMEM; + if (!is_delete) + entry = kmalloc(sizeof(e), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_execute_handler_record *acl; + if (ptr->type != type) + continue; + /* Condition not supported. */ + acl = container_of(ptr, struct tomoyo_execute_handler_record, + head); + if (acl->handler != e.handler) + continue; + if (!is_delete) { + /* Only one entry can exist in a domain. */ + struct tomoyo_acl_info *ptr2; + list_for_each_entry_rcu(ptr2, &domain->acl_info_list, + list) { + if (ptr2->type == type) + ptr2->is_deleted = true; + } + } + ptr->is_deleted = is_delete; + error = 0; + break; + } + if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) { + /* Only one entry can exist in a domain. */ + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + if (ptr->type == type) + ptr->is_deleted = true; + } + tomoyo_add_domain_acl(domain, &entry->head); + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + tomoyo_put_name(e.handler); + kfree(entry); + return error; +} + +/** + * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list. + * + * @type: Type of operation. + * @filename: Filename. + * @domain: Pointer to "struct tomoyo_domain_info". + * @condition: Pointer to "struct tomoyo_condition". May be NULL. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_update_path_acl(const u8 type, const char *filename, + struct tomoyo_domain_info * const domain, + struct tomoyo_condition *condition, + const bool is_delete) +{ + static const u16 tomoyo_rw_mask = + (1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE); + const u16 perm = 1 << type; + struct tomoyo_acl_info *ptr; + struct tomoyo_path_acl e = { + .head.type = TOMOYO_TYPE_PATH_ACL, + .head.cond = condition, + .perm = perm + }; + struct tomoyo_path_acl *entry = NULL; + int error = is_delete ? -ENOENT : -ENOMEM; + if (type == TOMOYO_TYPE_READ_WRITE) + e.perm |= tomoyo_rw_mask; + if (!tomoyo_parse_name_union(filename, &e.name)) + return -EINVAL; + if (!is_delete) + entry = kmalloc(sizeof(e), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_acl *acl = + container_of(ptr, struct tomoyo_path_acl, + head); + if (ptr->type != TOMOYO_TYPE_PATH_ACL || + ptr->cond != condition || + tomoyo_memcmp(acl, &e, offsetof(typeof(e), name), + sizeof(e))) + continue; + if (is_delete) { + acl->perm &= ~perm; + if ((acl->perm & tomoyo_rw_mask) != tomoyo_rw_mask) + acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); + else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))) + acl->perm &= ~tomoyo_rw_mask; + if (!acl->perm) + ptr->is_deleted = true; + } else { + if (ptr->is_deleted) + acl->perm = 0; + acl->perm |= perm; + if ((acl->perm & tomoyo_rw_mask) == tomoyo_rw_mask) + acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE; + else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)) + acl->perm |= tomoyo_rw_mask; + ptr->is_deleted = false; + } + error = 0; + break; + } + if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) { + tomoyo_add_domain_acl(domain, &entry->head); + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + tomoyo_put_name_union(&e.name); + kfree(entry); + return error; +} + +/** + * tomoyo_update_path_number3_acl - Update "struct tomoyo_path_number3_acl" list. + * + * @type: Type of operation. + * @filename: Filename. + * @mode: Create mode. + * @major: Device major number. + * @minor: Device minor number. + * @domain: Pointer to "struct tomoyo_domain_info". + * @condition: Pointer to "struct tomoyo_condition". May be NULL. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +static inline int tomoyo_update_path_number3_acl +(const u8 type, const char *filename, char *mode, char *major, char *minor, + struct tomoyo_domain_info * const domain, struct tomoyo_condition *condition, + const bool is_delete) +{ + const u8 perm = 1 << type; + struct tomoyo_acl_info *ptr; + struct tomoyo_path_number3_acl e = { + .head.type = TOMOYO_TYPE_PATH_NUMBER3_ACL, + .head.cond = condition, + .perm = perm + }; + struct tomoyo_path_number3_acl *entry = NULL; + int error = is_delete ? -ENOENT : -ENOMEM; + if (!tomoyo_parse_name_union(filename, &e.name) || + !tomoyo_parse_number_union(mode, &e.mode) || + !tomoyo_parse_number_union(major, &e.major) || + !tomoyo_parse_number_union(minor, &e.minor)) + goto out; + if (!is_delete) + entry = kmalloc(sizeof(e), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_number3_acl *acl = + container_of(ptr, struct tomoyo_path_number3_acl, + head); + if (ptr->type != TOMOYO_TYPE_PATH_NUMBER3_ACL || + ptr->cond != condition || + tomoyo_memcmp(acl, &e, offsetof(typeof(e), name), + sizeof(e))) + continue; + if (is_delete) { + acl->perm &= ~perm; + if (!acl->perm) + ptr->is_deleted = true; + } else { + if (ptr->is_deleted) + acl->perm = 0; + acl->perm |= perm; + ptr->is_deleted = false; + } + error = 0; + break; + } + if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) { + tomoyo_add_domain_acl(domain, &entry->head); + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name_union(&e.name); + tomoyo_put_number_union(&e.mode); + tomoyo_put_number_union(&e.major); + tomoyo_put_number_union(&e.minor); + kfree(entry); + return error; +} + +/** + * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list. + * + * @type: Type of operation. + * @filename1: First filename. + * @filename2: Second filename. + * @domain: Pointer to "struct tomoyo_domain_info". + * @condition: Pointer to "struct tomoyo_condition". May be NULL. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +static inline int tomoyo_update_path2_acl +(const u8 type, const char *filename1, const char *filename2, + struct tomoyo_domain_info * const domain, struct tomoyo_condition *condition, + const bool is_delete) +{ + const u8 perm = 1 << type; + struct tomoyo_acl_info *ptr; + struct tomoyo_path2_acl e = { + .head.type = TOMOYO_TYPE_PATH2_ACL, + .head.cond = condition, + .perm = perm + }; + struct tomoyo_path2_acl *entry = NULL; + int error = is_delete ? -ENOENT : -ENOMEM; + if (!tomoyo_parse_name_union(filename1, &e.name1) || + !tomoyo_parse_name_union(filename2, &e.name2)) + goto out; + if (!is_delete) + entry = kmalloc(sizeof(e), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path2_acl *acl = + container_of(ptr, struct tomoyo_path2_acl, + head); + if (ptr->type != TOMOYO_TYPE_PATH2_ACL || + ptr->cond != condition || + tomoyo_memcmp(acl, &e, offsetof(typeof(e), name1), + sizeof(e))) + continue; + if (is_delete) { + acl->perm &= ~perm; + if (!acl->perm) + ptr->is_deleted = true; + } else { + if (ptr->is_deleted) + acl->perm = 0; + acl->perm |= perm; + ptr->is_deleted = false; + } + error = 0; + break; + } + if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) { + tomoyo_add_domain_acl(domain, &entry->head); + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name_union(&e.name1); + tomoyo_put_name_union(&e.name2); + kfree(entry); + return error; +} + +/** + * tomoyo_path2_acl - Check permission for double path operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @type: Type of operation. + * @filename1: First filename to check. + * @filename2: Second filename to check. + * + * Returns 0 on success, -EPERM otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_path2_acl(struct tomoyo_request_info *r, const u8 type, + const struct tomoyo_path_info *filename1, + const struct tomoyo_path_info *filename2) +{ + const struct tomoyo_domain_info *domain = r->domain; + struct tomoyo_acl_info *ptr; + const u8 perm = 1 << type; + int error = -EPERM; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path2_acl *acl; + if (ptr->is_deleted || ptr->type != TOMOYO_TYPE_PATH2_ACL) + continue; + acl = container_of(ptr, struct tomoyo_path2_acl, + head); + if (!(acl->perm & perm) || !tomoyo_condition(r, ptr) || + !tomoyo_compare_name_union(filename1, &acl->name1) || + !tomoyo_compare_name_union(filename2, &acl->name2)) + continue; + r->cond = ptr->cond; + error = 0; + break; + } + return error; +} + +/** + * tomoyo_path_permission - Check permission for single path operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @operation: Type of operation. + * @filename: Filename to check. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_path_permission(struct tomoyo_request_info *r, + u8 operation, + const struct tomoyo_path_info * + filename) +{ + const char *msg; + int error; + repeat: + r->mode = tomoyo_get_mode(r->profile, tomoyo_p2mac[operation]); + if (r->mode == TOMOYO_CONFIG_DISABLED) + return 0; + do { + error = tomoyo_path_acl(r, filename, 1 << operation, 1); + msg = tomoyo_path2keyword(operation); + tomoyo_audit_path_log(r, msg, filename->name, !error); + if (!error) + break; + error = tomoyo_supervisor(r, "allow_%s %s\n", msg, + tomoyo_file_pattern(filename)); + } while (error == 1); + if (r->mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + /* + * Since "allow_truncate" doesn't imply "allow_rewrite" permission, + * we need to check "allow_rewrite" permission if the filename is + * specified by "deny_rewrite" keyword. + */ + if (!error && operation == TOMOYO_TYPE_TRUNCATE && + tomoyo_is_no_rewrite_file(filename)) { + operation = TOMOYO_TYPE_REWRITE; + goto repeat; + } + return error; +} + +/** + * tomoyo_path_number3_perm2 - Check permission for mkdev operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @operation: Type of operation. + * @filename: Filename to check. + * @mode: Create mode. + * @dev: Device number. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_path_number3_perm2(struct tomoyo_request_info *r, + const u8 operation, + const struct tomoyo_path_info *filename, + const unsigned int mode, + const unsigned int dev) +{ + int error; + const char *msg = tomoyo_path_number32keyword(operation); + const unsigned int major = MAJOR(dev); + const unsigned int minor = MINOR(dev); + if (!r->mode) + return 0; + do { + error = tomoyo_path_number3_acl(r, filename, 1 << operation, + mode, major, minor); + tomoyo_audit_path_number3_log(r, msg, filename->name, mode, + major, minor, !error); + if (!error) + break; + error = tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", msg, + tomoyo_file_pattern(filename), mode, + major, minor); + } while (error == 1); + if (r->mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + +/** + * tomoyo_exec_perm - Check permission for "execute". + * + * @r: Pointer to "struct tomoyo_request_info". + * @filename: Check permission for "execute". + * + * Returns 0 on success, 1 on retry, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_exec_perm(struct tomoyo_request_info *r, + const struct tomoyo_path_info *filename) +{ + if (r->mode == TOMOYO_CONFIG_DISABLED) + return 0; + return tomoyo_file_perm(r, filename, 1); +} + +/** + * tomoyo_open_permission - Check permission for "read" and "write". + * + * @dentry: Pointer to "struct dentry". + * @mnt: Pointer to "struct vfsmount". + * @flag: Flags for open(). + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_open_permission(struct dentry *dentry, struct vfsmount *mnt, + const int flag) +{ + struct tomoyo_request_info r; + struct tomoyo_obj_info obj = { + .path1.dentry = dentry, + .path1.mnt = mnt + }; + struct task_struct * const task = current; + const u8 acc_mode = ACC_MODE(flag); + int error = 0; + struct tomoyo_path_info buf; + int idx; + if (task->in_execve && + !(task->tomoyo_flags & TOMOYO_TASK_IS_IN_EXECVE)) + return 0; + if (!mnt || (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode))) + return 0; + buf.name = NULL; + r.mode = 0; + idx = tomoyo_read_lock(); + /* + * If the filename is specified by "deny_rewrite" keyword, + * we need to check "allow_rewrite" permission when the filename is not + * opened for append mode or the filename is truncated at open time. + */ + if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND) + && tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_REWRITE) + != TOMOYO_CONFIG_DISABLED) { + if (!tomoyo_get_realpath(&buf, dentry, mnt)) { + error = -ENOMEM; + goto out; + } + if (tomoyo_is_no_rewrite_file(&buf)) { + r.obj = &obj; + error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, + &buf); + } + } + if (!error && acc_mode && + tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_OPEN) + != TOMOYO_CONFIG_DISABLED) { + if (!buf.name && !tomoyo_get_realpath(&buf, dentry, mnt)) { + error = -ENOMEM; + goto out; + } + r.obj = &obj; + error = tomoyo_file_perm(&r, &buf, acc_mode); + } + out: + kfree(buf.name); + tomoyo_read_unlock(idx); + if (r.mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + +/** + * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "chroot" and "unmount". + * + * @operation: Type of operation. + * @dentry: Pointer to "struct dentry". + * @mnt: Pointer to "struct vfsmount". + * @target: Symlink's target if @operation is TOMOYO_TYPE_SYMLINK. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_path_perm(const u8 operation, struct dentry *dentry, + struct vfsmount *mnt, const char *target) +{ + struct tomoyo_request_info r; + struct tomoyo_obj_info obj = { + .path1.dentry = dentry, + .path1.mnt = mnt + }; + int error = -ENOMEM; + struct tomoyo_path_info buf; + bool is_enforce = false; + struct tomoyo_path_info symlink_target; + int idx; + buf.name = NULL; + symlink_target.name = NULL; + idx = tomoyo_read_lock(); + if (!mnt || tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation]) + == TOMOYO_CONFIG_DISABLED) { + error = 0; + goto out; + } + is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING); + if (!tomoyo_get_realpath(&buf, dentry, mnt)) + goto out; + r.obj = &obj; + switch (operation) { + case TOMOYO_TYPE_RMDIR: + case TOMOYO_TYPE_CHROOT: + case TOMOYO_TYPE_UMOUNT: + tomoyo_add_slash(&buf); + break; + case TOMOYO_TYPE_SYMLINK: + symlink_target.name = tomoyo_encode(target); + if (!symlink_target.name) + goto out; + tomoyo_fill_path_info(&symlink_target); + obj.symlink_target = &symlink_target; + break; + } + error = tomoyo_path_permission(&r, operation, &buf); + if (operation == TOMOYO_TYPE_SYMLINK) + kfree(symlink_target.name); + out: + kfree(buf.name); + tomoyo_read_unlock(idx); + if (!is_enforce) + error = 0; + return error; +} + +/** + * tomoyo_path_number3_perm - Check permission for "mkblock" and "mkchar". + * + * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK) + * @dentry: Pointer to "struct dentry". + * @mnt: Pointer to "struct vfsmount". + ` @mode: Create mode. + * @dev: Device number. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_path_number3_perm(const u8 operation, struct dentry *dentry, + struct vfsmount *mnt, + const unsigned int mode, + const unsigned int dev) +{ + struct tomoyo_request_info r; + struct tomoyo_obj_info obj = { + .path1.dentry = dentry, + .path1.mnt = mnt, + .dev = dev + }; + int error = -ENOMEM; + struct tomoyo_path_info buf; + int idx; + buf.name = NULL; + idx = tomoyo_read_lock(); + if (!mnt || tomoyo_init_request_info(&r, NULL, + tomoyo_pnnn2mac[operation]) + == TOMOYO_CONFIG_DISABLED) { + error = 0; + goto out; + } + if (!tomoyo_get_realpath(&buf, dentry, mnt)) + goto out; + r.obj = &obj; + error = tomoyo_path_number3_perm2(&r, operation, &buf, mode, dev); + out: + kfree(buf.name); + tomoyo_read_unlock(idx); + if (r.mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + +/** + * tomoyo_rewrite_permission - Check permission for "rewrite". + * + * @filp: Pointer to "struct file". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_rewrite_permission(struct file *filp) +{ + struct tomoyo_request_info r; + struct tomoyo_obj_info obj = { + .path1.dentry = filp->f_dentry, + .path1.mnt = filp->f_vfsmnt + }; + int error = -ENOMEM; + bool is_enforce = false; + struct tomoyo_path_info buf; + int idx; + buf.name = NULL; + idx = tomoyo_read_lock(); + if (!filp->f_vfsmnt || + tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_REWRITE) + == TOMOYO_CONFIG_DISABLED) { + error = 0; + goto out; + } + is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING); + if (!tomoyo_get_realpath(&buf, filp->f_dentry, filp->f_vfsmnt)) + goto out; + if (!tomoyo_is_no_rewrite_file(&buf)) { + error = 0; + goto out; + } + r.obj = &obj; + error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, &buf); + out: + kfree(buf.name); + tomoyo_read_unlock(idx); + if (!is_enforce) + error = 0; + return error; +} + +/** + * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root". + * + * @operation: Type of operation. + * @dentry1: Pointer to "struct dentry". + * @mnt1: Pointer to "struct vfsmount". + * @dentry2: Pointer to "struct dentry". + * @mnt2: Pointer to "struct vfsmount". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_path2_perm(const u8 operation, struct dentry *dentry1, + struct vfsmount *mnt1, struct dentry *dentry2, + struct vfsmount *mnt2) +{ + struct tomoyo_request_info r; + int error = -ENOMEM; + const char *msg = tomoyo_path22keyword(operation); + struct tomoyo_path_info buf1; + struct tomoyo_path_info buf2; + bool is_enforce = false; + struct tomoyo_obj_info obj = { + .path1.dentry = dentry1, + .path1.mnt = mnt1, + .path2.dentry = dentry2, + .path2.mnt = mnt2 + }; + int idx; + buf1.name = NULL; + buf2.name = NULL; + idx = tomoyo_read_lock(); + if (!mnt1 || !mnt2 || + tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation]) + == TOMOYO_CONFIG_DISABLED) { + error = 0; + goto out; + } + is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING); + if (!tomoyo_get_realpath(&buf1, dentry1, mnt1) || + !tomoyo_get_realpath(&buf2, dentry2, mnt2)) + goto out; + switch (operation) { + case TOMOYO_TYPE_RENAME: + case TOMOYO_TYPE_LINK: + if (!dentry1->d_inode || !S_ISDIR(dentry1->d_inode->i_mode)) + break; + /* fall through */ + case TOMOYO_TYPE_PIVOT_ROOT: + tomoyo_add_slash(&buf1); + tomoyo_add_slash(&buf2); + } + r.obj = &obj; + do { + error = tomoyo_path2_acl(&r, operation, &buf1, &buf2); + tomoyo_audit_path2_log(&r, msg, buf1.name, buf2.name, !error); + if (!error) + break; + error = tomoyo_supervisor(&r, "allow_%s %s %s\n", msg, + tomoyo_file_pattern(&buf1), + tomoyo_file_pattern(&buf2)); + } while (error == 1); + out: + kfree(buf1.name); + kfree(buf2.name); + tomoyo_read_unlock(idx); + if (!is_enforce) + error = 0; + return error; +} + +/** + * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL. + * + * @type: Type of operation. + * @filename: Filename. + * @number: Number. + * @domain: Pointer to "struct tomoyo_domain_info". + * @condition: Pointer to "struct tomoyo_condition". May be NULL. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +static inline int tomoyo_update_path_number_acl +(const u8 type, const char *filename, char *number, + struct tomoyo_domain_info * const domain, struct tomoyo_condition *condition, + const bool is_delete) +{ + const u8 perm = 1 << type; + struct tomoyo_acl_info *ptr; + struct tomoyo_path_number_acl e = { + .head.type = TOMOYO_TYPE_PATH_NUMBER_ACL, + .head.cond = condition, + .perm = perm + }; + struct tomoyo_path_number_acl *entry = NULL; + int error = is_delete ? -ENOENT : -ENOMEM; + if (!domain) + return -EINVAL; + if (!tomoyo_parse_name_union(filename, &e.name)) + return -EINVAL; + if (!tomoyo_parse_number_union(number, &e.number)) + goto out; + if (!is_delete) + entry = kmalloc(sizeof(e), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_number_acl *acl = + container_of(ptr, struct tomoyo_path_number_acl, + head); + if (ptr->type != TOMOYO_TYPE_PATH_NUMBER_ACL || + ptr->cond != condition || + tomoyo_memcmp(acl, &e, offsetof(typeof(e), name), + sizeof(e))) + continue; + if (is_delete) { + acl->perm &= ~perm; + if (!acl->perm) + ptr->is_deleted = true; + } else { + if (ptr->is_deleted) + acl->perm = 0; + acl->perm |= perm; + ptr->is_deleted = false; + } + error = 0; + break; + } + if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) { + tomoyo_add_domain_acl(domain, &entry->head); + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name_union(&e.name); + tomoyo_put_number_union(&e.number); + kfree(entry); + return error; +} + +/** + * tomoyo_path_number_acl - Check permission for ioctl/chmod/chown/chgrp operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @type: Operation. + * @filename: Filename to check. + * @number: Number. + * + * Returns 0 on success, -EPERM otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_path_number_acl(struct tomoyo_request_info *r, const u8 type, + const struct tomoyo_path_info *filename, + const unsigned long number) +{ + struct tomoyo_domain_info *domain = r->domain; + struct tomoyo_acl_info *ptr; + const u8 perm = 1 << type; + int error = -EPERM; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_number_acl *acl; + if (ptr->is_deleted || + ptr->type != TOMOYO_TYPE_PATH_NUMBER_ACL) + continue; + acl = container_of(ptr, struct tomoyo_path_number_acl, + head); + if (!(acl->perm & perm) || !tomoyo_condition(r, ptr) || + !tomoyo_compare_number_union(number, &acl->number) || + !tomoyo_compare_name_union(filename, &acl->name)) + continue; + r->cond = ptr->cond; + error = 0; + break; + } + return error; +} + +/** + * tomoyo_path_number_perm2 - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". + * + * @r: Pointer to "strct tomoyo_request_info". + * @filename: Filename to check. + * @numr: Number. + * + * Returns 0 on success, 1 on retry, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_path_number_perm2(struct tomoyo_request_info *r, + const u8 type, + const struct tomoyo_path_info *filename, + const unsigned long number) +{ + char buffer[64]; + int error; + u8 radix; + const char *msg = tomoyo_path_number2keyword(type); + if (!filename) + return 0; + switch (type) { + case TOMOYO_TYPE_CREATE: + case TOMOYO_TYPE_MKDIR: + case TOMOYO_TYPE_MKFIFO: + case TOMOYO_TYPE_MKSOCK: + case TOMOYO_TYPE_CHMOD: + radix = TOMOYO_VALUE_TYPE_OCTAL; + break; + case TOMOYO_TYPE_IOCTL: + radix = TOMOYO_VALUE_TYPE_HEXADECIMAL; + break; + default: + radix = TOMOYO_VALUE_TYPE_DECIMAL; + break; + } + tomoyo_print_ulong(buffer, sizeof(buffer), number, radix); + do { + error = tomoyo_path_number_acl(r, type, filename, number); + tomoyo_audit_path_number_log(r, msg, filename->name, buffer, + !error); + if (!error) + return 0; + error = tomoyo_supervisor(r, "allow_%s %s %s\n", msg, + tomoyo_file_pattern(filename), + buffer); + } while (error == 1); + if (r->mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + +/** + * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". + * + * @dentry: Pointer to "struct dentry". + * @vfsmnt: Pointer to "struct vfsmount". + * @number: Number. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_path_number_perm(const u8 type, struct dentry *dentry, + struct vfsmount *vfsmnt, + unsigned long number) +{ + struct tomoyo_request_info r; + struct tomoyo_obj_info obj = { + .path1.dentry = dentry, + .path1.mnt = vfsmnt + }; + int error = -ENOMEM; + struct tomoyo_path_info buf; + int idx; + if (!vfsmnt || !dentry) + return 0; + buf.name = NULL; + idx = tomoyo_read_lock(); + if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type]) + == TOMOYO_CONFIG_DISABLED) { + error = 0; + goto out; + } + if (!tomoyo_get_realpath(&buf, dentry, vfsmnt)) + goto out; + r.obj = &obj; + if (type == TOMOYO_TYPE_MKDIR) + tomoyo_add_slash(&buf); + error = tomoyo_path_number_perm2(&r, type, &buf, number); + out: + kfree(buf.name); + tomoyo_read_unlock(idx); + if (r.mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + +/** + * tomoyo_ioctl_permission - Check permission for "ioctl". + * + * @file: Pointer to "struct file". + * @cmd: Ioctl command number. + * @arg: Param for @cmd . + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_ioctl_permission(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int error = tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, filp->f_dentry, + filp->f_vfsmnt, cmd); + if (error) + goto out; + switch (cmd) { + case FIOCLEX: + case FIONCLEX: + case FIONBIO: + case FIOASYNC: + case FIOQSIZE: + case FIFREEZE: + case FITHAW: + case FS_IOC_FIEMAP: + case FIGETBSZ: + goto out; + } + if (S_ISREG(filp->f_path.dentry->d_inode->i_mode)) { + switch (cmd) { + case FIBMAP: + case FIONREAD: + case FS_IOC_RESVSP: + case FS_IOC_RESVSP64: + goto out; + } + } + /* Check permission to call vfs_ioctl(). */ + if (!tomoyo_capable(TOMOYO_SYS_IOCTL)) + error = -EPERM; + out: + return error; +} + +/** + * tomoyo_chmod_permission - Check permission for "chmod". + * + * @dentry: Pointer to "struct dentry". + * @vfsmnt: Pointer to "struct vfsmount". + * @mode: Mode. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_chmod_permission(struct dentry *dentry, struct vfsmount *vfsmnt, + mode_t mode) +{ + if (mode == (mode_t) -1) + return 0; + if (!tomoyo_capable(TOMOYO_SYS_CHMOD)) + return -EPERM; + return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, dentry, vfsmnt, + mode & S_IALLUGO); +} + +/** + * tomoyo_chown_permission - Check permission for "chown/chgrp". + * + * @dentry: Pointer to "struct dentry". + * @vfsmnt: Pointer to "struct vfsmount". + * @user: User ID. + * @group: Group ID. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_chown_permission(struct dentry *dentry, struct vfsmount *vfsmnt, + uid_t user, gid_t group) +{ + int error = 0; + if (user == (uid_t) -1 && group == (gid_t) -1) + return 0; + if (!tomoyo_capable(TOMOYO_SYS_CHOWN)) + return -EPERM; + if (user != (uid_t) -1) + error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, dentry, + vfsmnt, user); + if (!error && group != (gid_t) -1) + error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, dentry, + vfsmnt, group); + return error; +} + +/** + * tomoyo_pivot_root_permission - Check permission for pivot_root(). + * + * @old_path: Pointer to "struct path". + * @new_path: Pointer to "struct path". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_pivot_root_permission(struct path *old_path, struct path*new_path) +{ + if (!tomoyo_capable(TOMOYO_SYS_PIVOT_ROOT)) + return -EPERM; + return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path->dentry, + new_path->mnt, old_path->dentry, + old_path->mnt); +} + +/** + * tomoyo_chroot_permission - Check permission for chroot(). + * + * @path: Pointer to "struct path". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_chroot_permission(struct path *path) +{ + if (!tomoyo_capable(TOMOYO_SYS_CHROOT)) + return -EPERM; + return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path->dentry, path->mnt, + NULL); +} + +/** + * tomoyo_umount_permission - Check permission for unmount. + * + * @mnt: Pointer to "struct vfsmount". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_umount_permission(struct vfsmount *mnt) +{ + if (!tomoyo_capable(TOMOYO_SYS_UMOUNT)) + return -EPERM; + return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, mnt->mnt_root, mnt, NULL); +} + +/** + * tomoyo_write_file_policy - Update file related list. + * + * @data: String to parse. + * @domain: Pointer to "struct tomoyo_domain_info". + * @condition: Pointer to "struct tomoyo_condition". May be NULL. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, + struct tomoyo_condition *condition, + const bool is_delete) +{ + char *w[5]; + unsigned int perm; + u8 type; + if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) + return -EINVAL; + if (strncmp(w[0], "allow_", 6)) { + if (sscanf(w[0], "%u", &perm) == 1) + return tomoyo_update_file_acl((u8) perm, w[1], domain, + condition, is_delete); + if (!strcmp(w[0], TOMOYO_KEYWORD_EXECUTE_HANDLER)) + type = TOMOYO_TYPE_EXECUTE_HANDLER; + else if (!strcmp(w[0], TOMOYO_KEYWORD_DENIED_EXECUTE_HANDLER)) + type = TOMOYO_TYPE_DENIED_EXECUTE_HANDLER; + else + goto out; + return tomoyo_update_execute_handler(type, w[1], domain, + is_delete); + } + w[0] += 6; + for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { + if (strcmp(w[0], tomoyo_path_keyword[type])) + continue; + return tomoyo_update_path_acl(type, w[1], domain, condition, + is_delete); + } + if (!w[2][0]) + goto out; + for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) { + if (strcmp(w[0], tomoyo_path2_keyword[type])) + continue; + return tomoyo_update_path2_acl(type, w[1], w[2], domain, + condition, is_delete); + } + for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) { + if (strcmp(w[0], tomoyo_path_number_keyword[type])) + continue; + return tomoyo_update_path_number_acl(type, w[1], w[2], domain, + condition, is_delete); + } + if (!w[3][0] || !w[4][0]) + goto out; + for (type = 0; type < TOMOYO_MAX_PATH_NUMBER3_OPERATION; type++) { + if (strcmp(w[0], tomoyo_path_number3_keyword[type])) + continue; + return tomoyo_update_path_number3_acl(type, w[1], w[2], w[3], + w[4], domain, condition, + is_delete); + } + out: + return -EINVAL; +} + +/* + * Below part contains copy of some of VFS helper functions. + * + * Since TOMOYO Linux requires "struct vfsmount" parameter to calculate + * an absolute pathname of the requested "struct dentry" parameter + * but the VFS helper functions don't receive "struct vfsmount" parameter, + * TOMOYO Linux checks permission outside VFS helper functions. + * To keep the DAC's permission checks are performed before the + * TOMOYO Linux's permission checks are performed, I'd like to call + * may_create() and may_delete() from LSM. + */ + +static inline int tomoyo_may_create(struct inode *dir, struct dentry *dentry) +{ + /* return may_create(dir, dentry); */ + return 0; +} + +static inline int tomoyo_may_delete(struct inode *dir, struct dentry *dentry, + int is_dir) +{ + /* return may_delete(dir, dentry, is_dir); */ + return 0; +} + +/* Permission checks from vfs_create(). */ +static int tomoyo_pre_vfs_create(struct inode *dir, struct dentry *dentry) +{ + int error = tomoyo_may_create(dir, dentry); + if (error) + return error; + if (!dir->i_op || !dir->i_op->create) + return -EACCES; /* shouldn't it be ENOSYS? */ + return 0; +} + +/* Permission checks from vfs_mknod(). */ +static int tomoyo_pre_vfs_mknod(struct inode *dir, struct dentry *dentry, + int mode) +{ + int error = tomoyo_may_create(dir, dentry); + if (error) + return error; + if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD)) + return -EPERM; + if (!dir->i_op || !dir->i_op->mknod) + return -EPERM; + return 0; +} + +/* Permission checks from vfs_mkdir(). */ +static int tomoyo_pre_vfs_mkdir(struct inode *dir, struct dentry *dentry) +{ + int error = tomoyo_may_create(dir, dentry); + if (error) + return error; + if (!dir->i_op || !dir->i_op->mkdir) + return -EPERM; + return 0; +} + +/* Permission checks from vfs_rmdir(). */ +static int tomoyo_pre_vfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int error = tomoyo_may_delete(dir, dentry, 1); + if (error) + return error; + if (!dir->i_op || !dir->i_op->rmdir) + return -EPERM; + return 0; +} + +/* Permission checks from vfs_unlink(). */ +static int tomoyo_pre_vfs_unlink(struct inode *dir, struct dentry *dentry) +{ + int error = tomoyo_may_delete(dir, dentry, 0); + if (error) + return error; + if (!dir->i_op || !dir->i_op->unlink) + return -EPERM; + return 0; +} + +/* Permission checks from vfs_link(). */ +static int tomoyo_pre_vfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + struct inode *inode = old_dentry->d_inode; + int error; + if (!inode) + return -ENOENT; + error = tomoyo_may_create(dir, new_dentry); + if (error) + return error; + if (dir->i_sb != inode->i_sb) + return -EXDEV; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return -EPERM; + if (!dir->i_op || !dir->i_op->link) + return -EPERM; + if (S_ISDIR(old_dentry->d_inode->i_mode)) + return -EPERM; + return 0; +} + +/* Permission checks from vfs_symlink(). */ +static int tomoyo_pre_vfs_symlink(struct inode *dir, struct dentry *dentry) +{ + int error = tomoyo_may_create(dir, dentry); + if (error) + return error; + if (!dir->i_op || !dir->i_op->symlink) + return -EPERM; + return 0; +} + +/* Permission checks from vfs_rename(). */ +static int tomoyo_pre_vfs_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry) +{ + int error; + const int is_dir = S_ISDIR(old_dentry->d_inode->i_mode); + if (old_dentry->d_inode == new_dentry->d_inode) + return 0; + error = tomoyo_may_delete(old_dir, old_dentry, is_dir); + if (error) + return error; + if (!new_dentry->d_inode) + error = tomoyo_may_create(new_dir, new_dentry); + else + error = tomoyo_may_delete(new_dir, new_dentry, is_dir); + if (error) + return error; + if (!old_dir->i_op || !old_dir->i_op->rename) + return -EPERM; + if (is_dir && new_dir != old_dir) + error = inode_permission(old_dentry->d_inode, MAY_WRITE); + return error; +} + +/* Permission checks from vfs_mknod(). */ +int tomoyo_mknod_permission(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, const unsigned int mode, + unsigned int dev) +{ + int error; + if (S_ISCHR(mode) && !tomoyo_capable(TOMOYO_CREATE_CHAR_DEV)) + return -EPERM; + if (S_ISBLK(mode) && !tomoyo_capable(TOMOYO_CREATE_BLOCK_DEV)) + return -EPERM; + if (S_ISFIFO(mode) && !tomoyo_capable(TOMOYO_CREATE_FIFO)) + return -EPERM; + if (S_ISSOCK(mode) && !tomoyo_capable(TOMOYO_CREATE_UNIX_SOCKET)) + return -EPERM; + switch (mode & S_IFMT) { + case 0: + case S_IFREG: + error = tomoyo_pre_vfs_create(dir, dentry); + if (!error) + error = tomoyo_path_number_perm(TOMOYO_TYPE_CREATE, + dentry, mnt, + mode & S_IALLUGO); + return error; + } + error = tomoyo_pre_vfs_mknod(dir, dentry, mode); + dev = new_decode_dev(dev); + if (error) + return error; + switch (mode & S_IFMT) { + case S_IFCHR: + error = tomoyo_path_number3_perm(TOMOYO_TYPE_MKCHAR, dentry, + mnt, mode & S_IALLUGO, dev); + break; + case S_IFBLK: + error = tomoyo_path_number3_perm(TOMOYO_TYPE_MKBLOCK, dentry, + mnt, mode & S_IALLUGO, dev); + break; + case S_IFIFO: + error = tomoyo_path_number_perm(TOMOYO_TYPE_MKFIFO, dentry, + mnt, mode & S_IALLUGO); + break; + case S_IFSOCK: + error = tomoyo_path_number_perm(TOMOYO_TYPE_MKSOCK, dentry, + mnt, mode & S_IALLUGO); + break; + } + return error; +} + +/* Permission checks for vfs_mkdir(). */ +int tomoyo_mkdir_permission(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, unsigned int mode) +{ + int error = tomoyo_pre_vfs_mkdir(dir, dentry); + if (!error) + error = tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, dentry, mnt, + mode); + return error; +} + +/* Permission checks for vfs_rmdir(). */ +int tomoyo_rmdir_permission(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt) +{ + int error = tomoyo_pre_vfs_rmdir(dir, dentry); + if (!error) + error = tomoyo_path_perm(TOMOYO_TYPE_RMDIR, dentry, mnt, + NULL); + return error; +} + +/* Permission checks for vfs_unlink(). */ +int tomoyo_unlink_permission(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt) +{ + int error; + if (!tomoyo_capable(TOMOYO_SYS_UNLINK)) + return -EPERM; + error = tomoyo_pre_vfs_unlink(dir, dentry); + if (!error) + error = tomoyo_path_perm(TOMOYO_TYPE_UNLINK, dentry, mnt, + NULL); + return error; +} + +/* Permission checks for vfs_symlink(). */ +int tomoyo_symlink_permission(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, const char *from) +{ + int error; + if (!tomoyo_capable(TOMOYO_SYS_SYMLINK)) + return -EPERM; + error = tomoyo_pre_vfs_symlink(dir, dentry); + if (!error) + error = tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, dentry, mnt, + from); + return error; +} + +/* Permission checks for notify_change(). */ +int tomoyo_truncate_permission(struct dentry *dentry, struct vfsmount *mnt, + loff_t length, unsigned int time_attrs) +{ + return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, dentry, mnt, NULL); +} + +/* Permission checks for vfs_rename(). */ +int tomoyo_rename_permission(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + struct vfsmount *mnt) +{ + int error; + if (!tomoyo_capable(TOMOYO_SYS_RENAME)) + return -EPERM; + error = tomoyo_pre_vfs_rename(old_dir, old_dentry, new_dir, + new_dentry); + if (!error) + error = tomoyo_path2_perm(TOMOYO_TYPE_RENAME, old_dentry, mnt, + new_dentry, mnt); + return error; +} + +/* Permission checks for vfs_link(). */ +int tomoyo_link_permission(struct dentry *old_dentry, struct inode *new_dir, + struct dentry *new_dentry, struct vfsmount *mnt) +{ + int error; + if (!tomoyo_capable(TOMOYO_SYS_LINK)) + return -EPERM; + error = tomoyo_pre_vfs_link(old_dentry, new_dir, new_dentry); + if (!error) + error = tomoyo_path2_perm(TOMOYO_TYPE_LINK, old_dentry, mnt, + new_dentry, mnt); + return error; +} -- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/