From a1f9bb6a375a8dbf7797ffbd6739c46b338a77f7 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 17 May 2010 10:09:15 +0900 Subject: TOMOYO: Split file access control functions by type of parameters. Check numeric parameters for operations that deal them (e.g. chmod/chown/ioctl). Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 105 ++++++++- security/tomoyo/common.h | 126 ++++++++-- security/tomoyo/file.c | 588 ++++++++++++++++++++++++++++++++++++++++------- security/tomoyo/gc.c | 35 ++- security/tomoyo/tomoyo.c | 21 +- 5 files changed, 752 insertions(+), 123 deletions(-) (limited to 'security') diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 34d65871096c..0706b175fdb1 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1043,12 +1043,11 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) return true; list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { switch (ptr->type) { - struct tomoyo_path_acl *acl; - u32 perm; + u16 perm; u8 i; case TOMOYO_TYPE_PATH_ACL: - acl = container_of(ptr, struct tomoyo_path_acl, head); - perm = acl->perm | (((u32) acl->perm_high) << 16); + perm = container_of(ptr, struct tomoyo_path_acl, head) + ->perm; for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) if (perm & (1 << i)) count++; @@ -1062,6 +1061,20 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) if (perm & (1 << i)) count++; break; + case TOMOYO_TYPE_PATH_NUMBER_ACL: + perm = container_of(ptr, struct tomoyo_path_number_acl, + head)->perm; + for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++) + if (perm & (1 << i)) + count++; + break; + case TOMOYO_TYPE_PATH_NUMBER3_ACL: + perm = container_of(ptr, struct tomoyo_path_number3_acl, + head)->perm; + for (i = 0; i < TOMOYO_MAX_PATH_NUMBER3_OPERATION; i++) + if (perm & (1 << i)) + count++; + break; } } if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) @@ -1579,7 +1592,7 @@ static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head, { int pos; u8 bit; - const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16); + const u16 perm = ptr->perm; for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { if (!(perm & (1 << bit))) @@ -1637,6 +1650,76 @@ static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head, return false; } +/** + * tomoyo_print_path_number_acl - Print a path_number ACL entry. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_path_number_acl". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_print_path_number_acl(struct tomoyo_io_buffer *head, + struct tomoyo_path_number_acl *ptr) +{ + int pos; + u8 bit; + const u8 perm = ptr->perm; + for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; + bit++) { + if (!(perm & (1 << bit))) + continue; + pos = head->read_avail; + if (!tomoyo_io_printf(head, "allow_%s", + tomoyo_path_number2keyword(bit)) || + !tomoyo_print_name_union(head, &ptr->name) || + !tomoyo_print_number_union(head, &ptr->number) || + !tomoyo_io_printf(head, "\n")) + goto out; + } + head->read_bit = 0; + return true; + out: + head->read_bit = bit; + head->read_avail = pos; + return false; +} + +/** + * tomoyo_print_path_number3_acl - Print a path_number3 ACL entry. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_path_number3_acl". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_print_path_number3_acl(struct tomoyo_io_buffer *head, + struct tomoyo_path_number3_acl *ptr) +{ + int pos; + u8 bit; + const u16 perm = ptr->perm; + for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_NUMBER3_OPERATION; + bit++) { + if (!(perm & (1 << bit))) + continue; + pos = head->read_avail; + if (!tomoyo_io_printf(head, "allow_%s", + tomoyo_path_number32keyword(bit)) || + !tomoyo_print_name_union(head, &ptr->name) || + !tomoyo_print_number_union(head, &ptr->mode) || + !tomoyo_print_number_union(head, &ptr->major) || + !tomoyo_print_number_union(head, &ptr->minor) || + !tomoyo_io_printf(head, "\n")) + goto out; + } + head->read_bit = 0; + return true; + out: + head->read_bit = bit; + head->read_avail = pos; + return false; +} + /** * tomoyo_print_entry - Print an ACL entry. * @@ -1660,6 +1743,18 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, = container_of(ptr, struct tomoyo_path2_acl, head); return tomoyo_print_path2_acl(head, acl); } + if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) { + struct tomoyo_path_number_acl *acl + = container_of(ptr, struct tomoyo_path_number_acl, + head); + return tomoyo_print_path_number_acl(head, acl); + } + if (acl_type == TOMOYO_TYPE_PATH_NUMBER3_ACL) { + struct tomoyo_path_number3_acl *acl + = container_of(ptr, struct tomoyo_path_number3_acl, + head); + return tomoyo_print_path_number3_acl(head, acl); + } BUG(); /* This must not happen. */ return false; } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 91e2bcfd56e3..565a1c11da53 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -88,17 +88,21 @@ enum tomoyo_mac_index { enum tomoyo_acl_entry_type_index { TOMOYO_TYPE_PATH_ACL, TOMOYO_TYPE_PATH2_ACL, + TOMOYO_TYPE_PATH_NUMBER_ACL, + TOMOYO_TYPE_PATH_NUMBER3_ACL, }; /* Index numbers for File Controls. */ /* - * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set - * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and - * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set. - * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or - * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are - * automatically cleared if TYPE_READ_WRITE_ACL is cleared. + * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically + * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set. + * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if + * TOMOYO_TYPE_READ_WRITE is set. + * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ + * or TOMOYO_TYPE_WRITE is cleared. + * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically cleared if + * TOMOYO_TYPE_READ_WRITE is cleared. */ enum tomoyo_path_acl_index { @@ -106,27 +110,23 @@ enum tomoyo_path_acl_index { TOMOYO_TYPE_EXECUTE, TOMOYO_TYPE_READ, TOMOYO_TYPE_WRITE, - TOMOYO_TYPE_CREATE, TOMOYO_TYPE_UNLINK, - TOMOYO_TYPE_MKDIR, TOMOYO_TYPE_RMDIR, - TOMOYO_TYPE_MKFIFO, - TOMOYO_TYPE_MKSOCK, - TOMOYO_TYPE_MKBLOCK, - TOMOYO_TYPE_MKCHAR, TOMOYO_TYPE_TRUNCATE, TOMOYO_TYPE_SYMLINK, TOMOYO_TYPE_REWRITE, - TOMOYO_TYPE_IOCTL, - TOMOYO_TYPE_CHMOD, - TOMOYO_TYPE_CHOWN, - TOMOYO_TYPE_CHGRP, TOMOYO_TYPE_CHROOT, TOMOYO_TYPE_MOUNT, TOMOYO_TYPE_UMOUNT, TOMOYO_MAX_PATH_OPERATION }; +enum tomoyo_path_number3_acl_index { + TOMOYO_TYPE_MKBLOCK, + TOMOYO_TYPE_MKCHAR, + TOMOYO_MAX_PATH_NUMBER3_OPERATION +}; + enum tomoyo_path2_acl_index { TOMOYO_TYPE_LINK, TOMOYO_TYPE_RENAME, @@ -134,6 +134,18 @@ enum tomoyo_path2_acl_index { TOMOYO_MAX_PATH2_OPERATION }; +enum tomoyo_path_number_acl_index { + TOMOYO_TYPE_CREATE, + TOMOYO_TYPE_MKDIR, + TOMOYO_TYPE_MKFIFO, + TOMOYO_TYPE_MKSOCK, + TOMOYO_TYPE_IOCTL, + TOMOYO_TYPE_CHMOD, + TOMOYO_TYPE_CHOWN, + TOMOYO_TYPE_CHGRP, + TOMOYO_MAX_PATH_NUMBER_OPERATION +}; + enum tomoyo_securityfs_interface_index { TOMOYO_DOMAINPOLICY, TOMOYO_EXCEPTIONPOLICY, @@ -347,19 +359,61 @@ struct tomoyo_domain_info { * (3) "name" is the pathname. * * Directives held by this structure are "allow_read/write", "allow_execute", - * "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir", - * "allow_rmdir", "allow_mkfifo", "allow_mksock", "allow_mkblock", - * "allow_mkchar", "allow_truncate", "allow_symlink", "allow_rewrite", - * "allow_ioctl", "allow_chmod", "allow_chown", "allow_chgrp", "allow_chroot", + * "allow_read", "allow_write", "allow_unlink", "allow_rmdir", + * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot", * "allow_mount" and "allow_unmount". */ struct tomoyo_path_acl { struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */ - u8 perm_high; u16 perm; struct tomoyo_name_union name; }; +/* + * tomoyo_path_number_acl is a structure which is used for holding an + * entry with one pathname and one number operation. + * It has following fields. + * + * (1) "head" which is a "struct tomoyo_acl_info". + * (2) "perm" which is a bitmask of permitted operations. + * (3) "name" is the pathname. + * (4) "number" is the numeric value. + * + * Directives held by this structure are "allow_create", "allow_mkdir", + * "allow_ioctl", "allow_mkfifo", "allow_mksock", "allow_chmod", "allow_chown" + * and "allow_chgrp". + * + */ +struct tomoyo_path_number_acl { + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER_ACL */ + u8 perm; + struct tomoyo_name_union name; + struct tomoyo_number_union number; +}; + +/* + * tomoyo_path_number3_acl is a structure which is used for holding an + * entry with one pathname and three numbers operation. + * It has following fields. + * + * (1) "head" which is a "struct tomoyo_acl_info". + * (2) "perm" which is a bitmask of permitted operations. + * (3) "mode" is the create mode. + * (4) "major" is the major number of device node. + * (5) "minor" is the minor number of device node. + * + * Directives held by this structure are "allow_mkchar", "allow_mkblock". + * + */ +struct tomoyo_path_number3_acl { + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER3_ACL */ + u8 perm; + struct tomoyo_name_union name; + struct tomoyo_number_union mode; + struct tomoyo_number_union major; + struct tomoyo_number_union minor; +}; + /* * tomoyo_path2_acl is a structure which is used for holding an * entry with two pathnames operation (i.e. link(), rename() and pivot_root()). @@ -639,6 +693,8 @@ bool tomoyo_tokenize(char *buffer, char *w[], size_t size); bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); /* Convert double path operation to operation name. */ const char *tomoyo_path22keyword(const u8 operation); +const char *tomoyo_path_number2keyword(const u8 operation); +const char *tomoyo_path_number32keyword(const u8 operation); /* Get the last component of the given domainname. */ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); /* Convert single path operation to operation name. */ @@ -736,11 +792,18 @@ int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, const struct tomoyo_path_info *filename); int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag); +int tomoyo_path_number_perm(const u8 operation, struct path *path, + unsigned long number); +int tomoyo_path_number3_perm(const u8 operation, struct path *path, + const unsigned int mode, unsigned int dev); int tomoyo_path_perm(const u8 operation, struct path *path); int tomoyo_path2_perm(const u8 operation, struct path *path1, struct path *path2); int tomoyo_find_next_domain(struct linux_binprm *bprm); +void tomoyo_print_ulong(char *buffer, const int buffer_len, + const unsigned long value, const u8 type); + /* Drop refcount on tomoyo_name_union. */ void tomoyo_put_name_union(struct tomoyo_name_union *ptr); @@ -880,6 +943,18 @@ static inline bool tomoyo_is_same_path_acl(const struct tomoyo_path_acl *p1, tomoyo_is_same_name_union(&p1->name, &p2->name); } +static inline bool tomoyo_is_same_path_number3_acl +(const struct tomoyo_path_number3_acl *p1, + const struct tomoyo_path_number3_acl *p2) +{ + return tomoyo_is_same_acl_head(&p1->head, &p2->head) + && tomoyo_is_same_name_union(&p1->name, &p2->name) + && tomoyo_is_same_number_union(&p1->mode, &p2->mode) + && tomoyo_is_same_number_union(&p1->major, &p2->major) + && tomoyo_is_same_number_union(&p1->minor, &p2->minor); +} + + static inline bool tomoyo_is_same_path2_acl(const struct tomoyo_path2_acl *p1, const struct tomoyo_path2_acl *p2) { @@ -888,6 +963,15 @@ static inline bool tomoyo_is_same_path2_acl(const struct tomoyo_path2_acl *p1, tomoyo_is_same_name_union(&p1->name2, &p2->name2); } +static inline bool tomoyo_is_same_path_number_acl +(const struct tomoyo_path_number_acl *p1, + const struct tomoyo_path_number_acl *p2) +{ + return tomoyo_is_same_acl_head(&p1->head, &p2->head) + && tomoyo_is_same_name_union(&p1->name, &p2->name) + && tomoyo_is_same_number_union(&p1->number, &p2->number); +} + static inline bool tomoyo_is_same_domain_initializer_entry (const struct tomoyo_domain_initializer_entry *p1, const struct tomoyo_domain_initializer_entry *p2) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index f1d2adfd33bc..727cc723f87d 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -12,39 +12,49 @@ #include "common.h" #include -/* Keyword array for single path operations. */ +/* Keyword array for operations with one pathname. */ 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_CREATE] = "create", [TOMOYO_TYPE_UNLINK] = "unlink", - [TOMOYO_TYPE_MKDIR] = "mkdir", [TOMOYO_TYPE_RMDIR] = "rmdir", - [TOMOYO_TYPE_MKFIFO] = "mkfifo", - [TOMOYO_TYPE_MKSOCK] = "mksock", - [TOMOYO_TYPE_MKBLOCK] = "mkblock", - [TOMOYO_TYPE_MKCHAR] = "mkchar", [TOMOYO_TYPE_TRUNCATE] = "truncate", [TOMOYO_TYPE_SYMLINK] = "symlink", [TOMOYO_TYPE_REWRITE] = "rewrite", - [TOMOYO_TYPE_IOCTL] = "ioctl", - [TOMOYO_TYPE_CHMOD] = "chmod", - [TOMOYO_TYPE_CHOWN] = "chown", - [TOMOYO_TYPE_CHGRP] = "chgrp", [TOMOYO_TYPE_CHROOT] = "chroot", [TOMOYO_TYPE_MOUNT] = "mount", [TOMOYO_TYPE_UMOUNT] = "unmount", }; -/* Keyword array for double path operations. */ +/* Keyword array for operations with one pathname and three numbers. */ +static const char *tomoyo_path_number3_keyword +[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = { + [TOMOYO_TYPE_MKBLOCK] = "mkblock", + [TOMOYO_TYPE_MKCHAR] = "mkchar", +}; + +/* Keyword array for operations with two pathnames. */ static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { - [TOMOYO_TYPE_LINK] = "link", - [TOMOYO_TYPE_RENAME] = "rename", + [TOMOYO_TYPE_LINK] = "link", + [TOMOYO_TYPE_RENAME] = "rename", [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root", }; +/* Keyword array for operations with one pathname and one number. */ +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", +}; + void tomoyo_put_name_union(struct tomoyo_name_union *ptr) { if (!ptr) @@ -158,6 +168,19 @@ const char *tomoyo_path2keyword(const u8 operation) ? tomoyo_path_keyword[operation] : NULL; } +/** + * tomoyo_path_number32keyword - Get the name of path/number/number/number operations. + * + * @operation: Type of operation. + * + * Returns the name of path/number/number/number 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. * @@ -171,6 +194,19 @@ const char *tomoyo_path22keyword(const u8 operation) ? tomoyo_path2_keyword[operation] : NULL; } +/** + * tomoyo_path_number2keyword - Get the name of path/number operations. + * + * @operation: Type of operation. + * + * Returns the name of path/number operation. + */ +const char *tomoyo_path_number2keyword(const u8 operation) +{ + return (operation < TOMOYO_MAX_PATH_NUMBER_OPERATION) + ? tomoyo_path_number_keyword[operation] : NULL; +} + /** * tomoyo_strendswith - Check whether the token ends with the given token. * @@ -665,8 +701,8 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) /** * tomoyo_update_file_acl - Update file's read/write/execute ACL. * - * @filename: Filename. * @perm: Permission (between 1 to 7). + * @filename: Filename. * @domain: Pointer to "struct tomoyo_domain_info". * @is_delete: True if it is a delete request. * @@ -679,7 +715,7 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_update_file_acl(const char *filename, u8 perm, +static int tomoyo_update_file_acl(u8 perm, const char *filename, struct tomoyo_domain_info * const domain, const bool is_delete) { @@ -731,14 +767,8 @@ static int tomoyo_path_acl(const struct tomoyo_request_info *r, if (ptr->type != TOMOYO_TYPE_PATH_ACL) continue; acl = container_of(ptr, struct tomoyo_path_acl, head); - if (perm <= 0xFFFF) { - if (!(acl->perm & perm)) - continue; - } else { - if (!(acl->perm_high & (perm >> 16))) - continue; - } - if (!tomoyo_compare_name_union_pattern(filename, &acl->name, + if (!(acl->perm & perm) || + !tomoyo_compare_name_union_pattern(filename, &acl->name, may_use_pattern)) continue; error = 0; @@ -796,60 +826,12 @@ static int tomoyo_file_perm(struct tomoyo_request_info *r, /* Don't use patterns for execute permission. */ const struct tomoyo_path_info *patterned_file = (mode != 1) ? tomoyo_get_file_pattern(filename) : filename; - tomoyo_update_file_acl(patterned_file->name, mode, - r->domain, false); + tomoyo_update_file_acl(mode, patterned_file->name, r->domain, + false); } return 0; } -/** - * tomoyo_write_file_policy - Update file related list. - * - * @data: String to parse. - * @domain: Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, - const bool is_delete) -{ - char *filename = strchr(data, ' '); - char *filename2; - unsigned int perm; - u8 type; - - if (!filename) - return -EINVAL; - *filename++ = '\0'; - if (sscanf(data, "%u", &perm) == 1) - return tomoyo_update_file_acl(filename, (u8) perm, domain, - is_delete); - if (strncmp(data, "allow_", 6)) - goto out; - data += 6; - for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { - if (strcmp(data, tomoyo_path_keyword[type])) - continue; - return tomoyo_update_path_acl(type, filename, domain, - is_delete); - } - filename2 = strchr(filename, ' '); - if (!filename2) - goto out; - *filename2++ = '\0'; - for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) { - if (strcmp(data, tomoyo_path2_keyword[type])) - continue; - return tomoyo_update_path2_acl(type, filename, filename2, - domain, is_delete); - } - out: - return -EINVAL; -} - /** * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list. * @@ -866,13 +848,12 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, struct tomoyo_domain_info *const domain, const bool is_delete) { - static const u32 tomoyo_rw_mask = + static const u16 tomoyo_rw_mask = (1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE); - const u32 perm = 1 << type; + const u16 perm = 1 << type; struct tomoyo_acl_info *ptr; struct tomoyo_path_acl e = { .head.type = TOMOYO_TYPE_PATH_ACL, - .perm_high = perm >> 16, .perm = perm }; int error = is_delete ? -ENOENT : -ENOMEM; @@ -891,19 +872,13 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, if (!tomoyo_is_same_path_acl(acl, &e)) continue; if (is_delete) { - if (perm <= 0xFFFF) - acl->perm &= ~perm; - else - acl->perm_high &= ~(perm >> 16); + 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; } else { - if (perm <= 0xFFFF) - acl->perm |= perm; - else - acl->perm_high |= (perm >> 16); + 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)) @@ -927,6 +902,71 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, 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". + * @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, + 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, + .perm = perm + }; + 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 (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + 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 (!tomoyo_is_same_path_number3_acl(acl, &e)) + continue; + if (is_delete) + acl->perm &= ~perm; + else + acl->perm |= perm; + error = 0; + break; + } + if (!is_delete && error) { + struct tomoyo_path_number3_acl *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->head.list, + &domain->acl_info_list); + 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); + return error; +} + /** * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list. * @@ -988,6 +1028,50 @@ static int tomoyo_update_path2_acl(const u8 type, const char *filename1, return error; } +/** + * tomoyo_path_number3_acl - Check permission for path/number/number/number 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->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)) + continue; + if (!tomoyo_compare_name_union(filename, &acl->name)) + continue; + error = 0; + break; + } + return error; +} + /** * tomoyo_path2_acl - Check permission for double path operation. * @@ -1068,6 +1152,195 @@ static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, 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->type != TOMOYO_TYPE_PATH_NUMBER_ACL) + continue; + acl = container_of(ptr, struct tomoyo_path_number_acl, + head); + if (!(acl->perm & perm) || + !tomoyo_compare_number_union(number, &acl->number) || + !tomoyo_compare_name_union(filename, &acl->name)) + continue; + error = 0; + break; + } + 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". + * @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, + 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, + .perm = perm + }; + 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 (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + 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 (!tomoyo_is_same_path_number_acl(acl, &e)) + continue; + if (is_delete) + acl->perm &= ~perm; + else + acl->perm |= perm; + error = 0; + break; + } + if (!is_delete && error) { + struct tomoyo_path_number_acl *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->head.list, + &domain->acl_info_list); + error = 0; + } + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name_union(&e.name); + tomoyo_put_number_union(&e.number); + 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. + * @number: Number. + * + * Returns 0 on success, 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; + + 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); + error = tomoyo_path_number_acl(r, type, filename, number); + if (!error) + return 0; + tomoyo_warn_log(r, "%s %s %s", tomoyo_path_number2keyword(type), + filename->name, buffer); + if (tomoyo_domain_quota_is_ok(r)) + tomoyo_update_path_number_acl(type, + tomoyo_get_file_pattern(filename) + ->name, buffer, r->domain, false); + 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". + * + * @type: Type of operation. + * @path: Pointer to "struct path". + * @number: Number. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_path_number_perm(const u8 type, struct path *path, + unsigned long number) +{ + struct tomoyo_request_info r; + int error = -ENOMEM; + struct tomoyo_path_info *buf; + int idx; + + if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || + !path->mnt || !path->dentry) + return 0; + idx = tomoyo_read_lock(); + buf = tomoyo_get_path(path); + if (!buf) + goto out; + if (type == TOMOYO_TYPE_MKDIR && !buf->is_dir) { + /* + * tomoyo_get_path() reserves space for appending "/." + */ + strcat((char *) buf->name, "/"); + tomoyo_fill_path_info(buf); + } + error = tomoyo_path_number_perm2(&r, type, buf, number); + out: + kfree(buf); + tomoyo_read_unlock(idx); + if (r.mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + /** * tomoyo_check_exec_perm - Check permission for "execute". * @@ -1145,7 +1418,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, } /** - * tomoyo_path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate", "symlink", "rewrite", "ioctl", "chmod", "chown", "chgrp", "chroot", "mount" and "unmount". + * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot", "mount" and "unmount". * * @operation: Type of operation. * @path: Pointer to "struct path". @@ -1173,7 +1446,6 @@ int tomoyo_path_perm(const u8 operation, struct path *path) goto out; } break; - case TOMOYO_TYPE_MKDIR: case TOMOYO_TYPE_RMDIR: case TOMOYO_TYPE_CHROOT: if (!buf->is_dir) { @@ -1193,6 +1465,91 @@ int tomoyo_path_perm(const u8 operation, struct path *path) return error; } +/** + * tomoyo_path_number3_perm2 - Check permission for path/number/number/number 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 unsigned int major = MAJOR(dev); + const unsigned int minor = MINOR(dev); + + error = tomoyo_path_number3_acl(r, filename, 1 << operation, mode, + major, minor); + if (!error) + return 0; + tomoyo_warn_log(r, "%s %s 0%o %u %u", + tomoyo_path_number32keyword(operation), + filename->name, mode, major, minor); + if (tomoyo_domain_quota_is_ok(r)) { + char mode_buf[64]; + char major_buf[64]; + char minor_buf[64]; + memset(mode_buf, 0, sizeof(mode_buf)); + memset(major_buf, 0, sizeof(major_buf)); + memset(minor_buf, 0, sizeof(minor_buf)); + snprintf(mode_buf, sizeof(mode_buf) - 1, "0%o", mode); + snprintf(major_buf, sizeof(major_buf) - 1, "%u", major); + snprintf(minor_buf, sizeof(minor_buf) - 1, "%u", minor); + tomoyo_update_path_number3_acl(operation, + tomoyo_get_file_pattern(filename) + ->name, mode_buf, major_buf, + minor_buf, r->domain, false); + } + if (r->mode != TOMOYO_CONFIG_ENFORCING) + 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) + * @path: Pointer to "struct path". + * @mode: Create mode. + * @dev: Device number. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_path_number3_perm(const u8 operation, struct path *path, + const unsigned int mode, unsigned int dev) +{ + struct tomoyo_request_info r; + int error = -ENOMEM; + struct tomoyo_path_info *buf; + int idx; + + if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || + !path->mnt) + return 0; + idx = tomoyo_read_lock(); + error = -ENOMEM; + buf = tomoyo_get_path(path); + if (buf) { + error = tomoyo_path_number3_perm2(&r, operation, buf, mode, + new_decode_dev(dev)); + kfree(buf); + } + tomoyo_read_unlock(idx); + if (r.mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + /** * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root". * @@ -1254,3 +1611,60 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, error = 0; return error; } + +/** + * tomoyo_write_file_policy - Update file related list. + * + * @data: String to parse. + * @domain: Pointer to "struct tomoyo_domain_info". + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, + const bool is_delete) +{ + char *w[5]; + u8 type; + if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) + return -EINVAL; + if (strncmp(w[0], "allow_", 6)) { + unsigned int perm; + if (sscanf(w[0], "%u", &perm) == 1) + return tomoyo_update_file_acl((u8) perm, w[1], domain, + is_delete); + goto out; + } + 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, 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, + 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, + 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, is_delete); + } + out: + return -EINVAL; +} diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 6a48197f6ce5..78100180d23d 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -106,6 +106,24 @@ static void tomoyo_del_acl(struct tomoyo_acl_info *acl) tomoyo_put_name_union(&entry->name2); } break; + case TOMOYO_TYPE_PATH_NUMBER_ACL: + { + struct tomoyo_path_number_acl *entry + = container_of(acl, typeof(*entry), head); + tomoyo_put_name_union(&entry->name); + tomoyo_put_number_union(&entry->number); + } + break; + case TOMOYO_TYPE_PATH_NUMBER3_ACL: + { + struct tomoyo_path_number3_acl *entry + = container_of(acl, typeof(*entry), head); + tomoyo_put_name_union(&entry->name); + tomoyo_put_number_union(&entry->mode); + tomoyo_put_number_union(&entry->major); + tomoyo_put_number_union(&entry->minor); + } + break; default: printk(KERN_WARNING "Unknown type\n"); break; @@ -268,10 +286,7 @@ static void tomoyo_collect_entry(void) case TOMOYO_TYPE_PATH_ACL: if (container_of(acl, struct tomoyo_path_acl, - head)->perm || - container_of(acl, - struct tomoyo_path_acl, - head)->perm_high) + head)->perm) continue; break; case TOMOYO_TYPE_PATH2_ACL: @@ -280,6 +295,18 @@ static void tomoyo_collect_entry(void) head)->perm) continue; break; + case TOMOYO_TYPE_PATH_NUMBER_ACL: + if (container_of(acl, + struct tomoyo_path_number_acl, + head)->perm) + continue; + break; + case TOMOYO_TYPE_PATH_NUMBER3_ACL: + if (container_of(acl, + struct tomoyo_path_number3_acl, + head)->perm) + continue; + break; default: continue; } diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 4120f5a0e1bc..bbe00429b3f5 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -112,7 +112,8 @@ static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry, int mode) { struct path path = { parent->mnt, dentry }; - return tomoyo_path_perm(TOMOYO_TYPE_MKDIR, &path); + return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path, + mode & S_IALLUGO); } static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry) @@ -133,6 +134,7 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, { struct path path = { parent->mnt, dentry }; int type = TOMOYO_TYPE_CREATE; + const unsigned int perm = mode & S_IALLUGO; switch (mode & S_IFMT) { case S_IFCHR: @@ -141,6 +143,12 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, case S_IFBLK: type = TOMOYO_TYPE_MKBLOCK; break; + default: + goto no_dev; + } + return tomoyo_path_number3_perm(type, &path, perm, dev); + no_dev: + switch (mode & S_IFMT) { case S_IFIFO: type = TOMOYO_TYPE_MKFIFO; break; @@ -148,7 +156,7 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, type = TOMOYO_TYPE_MKSOCK; break; } - return tomoyo_path_perm(type, &path); + return tomoyo_path_number_perm(type, &path, perm); } static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir, @@ -189,23 +197,24 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred) static int tomoyo_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return tomoyo_path_perm(TOMOYO_TYPE_IOCTL, &file->f_path); + return tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, &file->f_path, cmd); } static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt, mode_t mode) { struct path path = { mnt, dentry }; - return tomoyo_path_perm(TOMOYO_TYPE_CHMOD, &path); + return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, &path, + mode & S_IALLUGO); } static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid) { int error = 0; if (uid != (uid_t) -1) - error = tomoyo_path_perm(TOMOYO_TYPE_CHOWN, path); + error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path, uid); if (!error && gid != (gid_t) -1) - error = tomoyo_path_perm(TOMOYO_TYPE_CHGRP, path); + error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path, gid); return error; } -- cgit v1.2.2