📄 auditfilter.c
字号:
/* auditfilter.c -- filtering of audit events * * Copyright 2003-2004 Red Hat, Inc. * Copyright 2005 Hewlett-Packard Development Company, L.P. * Copyright 2005 IBM Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <linux/kernel.h>#include <linux/audit.h>#include <linux/kthread.h>#include <linux/mutex.h>#include <linux/fs.h>#include <linux/namei.h>#include <linux/netlink.h>#include <linux/sched.h>#include <linux/inotify.h>#include <linux/selinux.h>#include "audit.h"/* * Locking model: * * audit_filter_mutex: * Synchronizes writes and blocking reads of audit's filterlist * data. Rcu is used to traverse the filterlist and access * contents of structs audit_entry, audit_watch and opaque * selinux rules during filtering. If modified, these structures * must be copied and replace their counterparts in the filterlist. * An audit_parent struct is not accessed during filtering, so may * be written directly provided audit_filter_mutex is held. *//* * Reference counting: * * audit_parent: lifetime is from audit_init_parent() to receipt of an IN_IGNORED * event. Each audit_watch holds a reference to its associated parent. * * audit_watch: if added to lists, lifetime is from audit_init_watch() to * audit_remove_watch(). Additionally, an audit_watch may exist * temporarily to assist in searching existing filter data. Each * audit_krule holds a reference to its associated watch. */struct audit_parent { struct list_head ilist; /* entry in inotify registration list */ struct list_head watches; /* associated watches */ struct inotify_watch wdata; /* inotify watch data */ unsigned flags; /* status flags */};/* * audit_parent status flags: * * AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to * a filesystem event to ensure we're adding audit watches to a valid parent. * Technically not needed for IN_DELETE_SELF or IN_UNMOUNT events, as we cannot * receive them while we have nameidata, but must be used for IN_MOVE_SELF which * we can receive while holding nameidata. */#define AUDIT_PARENT_INVALID 0x001/* Audit filter lists, defined in <linux/audit.h> */struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { LIST_HEAD_INIT(audit_filter_list[0]), LIST_HEAD_INIT(audit_filter_list[1]), LIST_HEAD_INIT(audit_filter_list[2]), LIST_HEAD_INIT(audit_filter_list[3]), LIST_HEAD_INIT(audit_filter_list[4]), LIST_HEAD_INIT(audit_filter_list[5]),#if AUDIT_NR_FILTERS != 6#error Fix audit_filter_list initialiser#endif};static DEFINE_MUTEX(audit_filter_mutex);/* Inotify handle */extern struct inotify_handle *audit_ih;/* Inotify events we care about. */#define AUDIT_IN_WATCH IN_MOVE|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELFvoid audit_free_parent(struct inotify_watch *i_watch){ struct audit_parent *parent; parent = container_of(i_watch, struct audit_parent, wdata); WARN_ON(!list_empty(&parent->watches)); kfree(parent);}static inline void audit_get_watch(struct audit_watch *watch){ atomic_inc(&watch->count);}static void audit_put_watch(struct audit_watch *watch){ if (atomic_dec_and_test(&watch->count)) { WARN_ON(watch->parent); WARN_ON(!list_empty(&watch->rules)); kfree(watch->path); kfree(watch); }}static void audit_remove_watch(struct audit_watch *watch){ list_del(&watch->wlist); put_inotify_watch(&watch->parent->wdata); watch->parent = NULL; audit_put_watch(watch); /* match initial get */}static inline void audit_free_rule(struct audit_entry *e){ int i; /* some rules don't have associated watches */ if (e->rule.watch) audit_put_watch(e->rule.watch); if (e->rule.fields) for (i = 0; i < e->rule.field_count; i++) { struct audit_field *f = &e->rule.fields[i]; kfree(f->se_str); selinux_audit_rule_free(f->se_rule); } kfree(e->rule.fields); kfree(e->rule.filterkey); kfree(e);}static inline void audit_free_rule_rcu(struct rcu_head *head){ struct audit_entry *e = container_of(head, struct audit_entry, rcu); audit_free_rule(e);}/* Initialize a parent watch entry. */static struct audit_parent *audit_init_parent(struct nameidata *ndp){ struct audit_parent *parent; s32 wd; parent = kzalloc(sizeof(*parent), GFP_KERNEL); if (unlikely(!parent)) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&parent->watches); parent->flags = 0; inotify_init_watch(&parent->wdata); /* grab a ref so inotify watch hangs around until we take audit_filter_mutex */ get_inotify_watch(&parent->wdata); wd = inotify_add_watch(audit_ih, &parent->wdata, ndp->dentry->d_inode, AUDIT_IN_WATCH); if (wd < 0) { audit_free_parent(&parent->wdata); return ERR_PTR(wd); } return parent;}/* Initialize a watch entry. */static struct audit_watch *audit_init_watch(char *path){ struct audit_watch *watch; watch = kzalloc(sizeof(*watch), GFP_KERNEL); if (unlikely(!watch)) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&watch->rules); atomic_set(&watch->count, 1); watch->path = path; watch->dev = (dev_t)-1; watch->ino = (unsigned long)-1; return watch;}/* Initialize an audit filterlist entry. */static inline struct audit_entry *audit_init_entry(u32 field_count){ struct audit_entry *entry; struct audit_field *fields; entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (unlikely(!entry)) return NULL; fields = kzalloc(sizeof(*fields) * field_count, GFP_KERNEL); if (unlikely(!fields)) { kfree(entry); return NULL; } entry->rule.fields = fields; return entry;}/* Unpack a filter field's string representation from user-space * buffer. */static char *audit_unpack_string(void **bufp, size_t *remain, size_t len){ char *str; if (!*bufp || (len == 0) || (len > *remain)) return ERR_PTR(-EINVAL); /* Of the currently implemented string fields, PATH_MAX * defines the longest valid length. */ if (len > PATH_MAX) return ERR_PTR(-ENAMETOOLONG); str = kmalloc(len + 1, GFP_KERNEL); if (unlikely(!str)) return ERR_PTR(-ENOMEM); memcpy(str, *bufp, len); str[len] = 0; *bufp += len; *remain -= len; return str;}/* Translate an inode field to kernel respresentation. */static inline int audit_to_inode(struct audit_krule *krule, struct audit_field *f){ if (krule->listnr != AUDIT_FILTER_EXIT || krule->watch || krule->inode_f) return -EINVAL; krule->inode_f = f; return 0;}/* Translate a watch string to kernel respresentation. */static int audit_to_watch(struct audit_krule *krule, char *path, int len, u32 op){ struct audit_watch *watch; if (!audit_ih) return -EOPNOTSUPP; if (path[0] != '/' || path[len-1] == '/' || krule->listnr != AUDIT_FILTER_EXIT || op & ~AUDIT_EQUAL || krule->inode_f || krule->watch) /* 1 inode # per rule, for hash */ return -EINVAL; watch = audit_init_watch(path); if (unlikely(IS_ERR(watch))) return PTR_ERR(watch); audit_get_watch(watch); krule->watch = watch; return 0;}static __u32 *classes[AUDIT_SYSCALL_CLASSES];int __init audit_register_class(int class, unsigned *list){ __u32 *p = kzalloc(AUDIT_BITMASK_SIZE * sizeof(__u32), GFP_KERNEL); if (!p) return -ENOMEM; while (*list != ~0U) { unsigned n = *list++; if (n >= AUDIT_BITMASK_SIZE * 32 - AUDIT_SYSCALL_CLASSES) { kfree(p); return -EINVAL; } p[AUDIT_WORD(n)] |= AUDIT_BIT(n); } if (class >= AUDIT_SYSCALL_CLASSES || classes[class]) { kfree(p); return -EINVAL; } classes[class] = p; return 0;}int audit_match_class(int class, unsigned syscall){ if (unlikely(syscall >= AUDIT_BITMASK_SIZE * sizeof(__u32))) return 0; if (unlikely(class >= AUDIT_SYSCALL_CLASSES || !classes[class])) return 0; return classes[class][AUDIT_WORD(syscall)] & AUDIT_BIT(syscall);}#ifdef CONFIG_AUDITSYSCALLstatic inline int audit_match_class_bits(int class, u32 *mask){ int i; if (classes[class]) { for (i = 0; i < AUDIT_BITMASK_SIZE; i++) if (mask[i] & classes[class][i]) return 0; } return 1;}static int audit_match_signal(struct audit_entry *entry){ struct audit_field *arch = entry->rule.arch_f; if (!arch) { /* When arch is unspecified, we must check both masks on biarch * as syscall number alone is ambiguous. */ return (audit_match_class_bits(AUDIT_CLASS_SIGNAL, entry->rule.mask) && audit_match_class_bits(AUDIT_CLASS_SIGNAL_32, entry->rule.mask)); } switch(audit_classify_arch(arch->val)) { case 0: /* native */ return (audit_match_class_bits(AUDIT_CLASS_SIGNAL, entry->rule.mask)); case 1: /* 32bit on biarch */ return (audit_match_class_bits(AUDIT_CLASS_SIGNAL_32, entry->rule.mask)); default: return 1; }}#endif/* Common user-space to kernel rule translation. */static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule){ unsigned listnr; struct audit_entry *entry; int i, err; err = -EINVAL; listnr = rule->flags & ~AUDIT_FILTER_PREPEND; switch(listnr) { default: goto exit_err; case AUDIT_FILTER_USER: case AUDIT_FILTER_TYPE:#ifdef CONFIG_AUDITSYSCALL case AUDIT_FILTER_ENTRY: case AUDIT_FILTER_EXIT: case AUDIT_FILTER_TASK:#endif ; } if (unlikely(rule->action == AUDIT_POSSIBLE)) { printk(KERN_ERR "AUDIT_POSSIBLE is deprecated\n"); goto exit_err; } if (rule->action != AUDIT_NEVER && rule->action != AUDIT_ALWAYS) goto exit_err; if (rule->field_count > AUDIT_MAX_FIELDS) goto exit_err; err = -ENOMEM; entry = audit_init_entry(rule->field_count); if (!entry) goto exit_err; entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND; entry->rule.listnr = listnr; entry->rule.action = rule->action; entry->rule.field_count = rule->field_count; for (i = 0; i < AUDIT_BITMASK_SIZE; i++) entry->rule.mask[i] = rule->mask[i]; for (i = 0; i < AUDIT_SYSCALL_CLASSES; i++) { int bit = AUDIT_BITMASK_SIZE * 32 - i - 1; __u32 *p = &entry->rule.mask[AUDIT_WORD(bit)]; __u32 *class; if (!(*p & AUDIT_BIT(bit))) continue; *p &= ~AUDIT_BIT(bit); class = classes[i]; if (class) { int j; for (j = 0; j < AUDIT_BITMASK_SIZE; j++) entry->rule.mask[j] |= class[j]; } } return entry;exit_err: return ERR_PTR(err);}/* Translate struct audit_rule to kernel's rule respresentation. * Exists for backward compatibility with userspace. */static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule){ struct audit_entry *entry; struct audit_field *f; int err = 0; int i; entry = audit_to_entry_common(rule); if (IS_ERR(entry)) goto exit_nofree; for (i = 0; i < rule->field_count; i++) { struct audit_field *f = &entry->rule.fields[i]; f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS); f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); f->val = rule->values[i]; err = -EINVAL; switch(f->type) { default: goto exit_free; case AUDIT_PID: case AUDIT_UID: case AUDIT_EUID: case AUDIT_SUID: case AUDIT_FSUID: case AUDIT_GID: case AUDIT_EGID: case AUDIT_SGID: case AUDIT_FSGID: case AUDIT_LOGINUID: case AUDIT_PERS: case AUDIT_MSGTYPE: case AUDIT_PPID: case AUDIT_DEVMAJOR: case AUDIT_DEVMINOR: case AUDIT_EXIT: case AUDIT_SUCCESS: case AUDIT_ARG0: case AUDIT_ARG1: case AUDIT_ARG2: case AUDIT_ARG3: break; /* arch is only allowed to be = or != */ case AUDIT_ARCH: if ((f->op != AUDIT_NOT_EQUAL) && (f->op != AUDIT_EQUAL) && (f->op != AUDIT_NEGATE) && (f->op)) { err = -EINVAL; goto exit_free; } entry->rule.arch_f = f; break; case AUDIT_PERM: if (f->val & ~15) goto exit_free; break; case AUDIT_INODE: err = audit_to_inode(&entry->rule, f); if (err) goto exit_free; break; } entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1; /* Support for legacy operators where * AUDIT_NEGATE bit signifies != and otherwise assumes == */ if (f->op & AUDIT_NEGATE) f->op = AUDIT_NOT_EQUAL; else if (!f->op) f->op = AUDIT_EQUAL; else if (f->op == AUDIT_OPERATORS) { err = -EINVAL; goto exit_free; } } f = entry->rule.inode_f; if (f) { switch(f->op) { case AUDIT_NOT_EQUAL: entry->rule.inode_f = NULL; case AUDIT_EQUAL: break; default: err = -EINVAL; goto exit_free; } }exit_nofree: return entry;exit_free: audit_free_rule(entry); return ERR_PTR(err);}/* Translate struct audit_rule_data to kernel's rule respresentation. */static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, size_t datasz){ int err = 0; struct audit_entry *entry; struct audit_field *f; void *bufp; size_t remain = datasz - sizeof(struct audit_rule_data); int i; char *str; entry = audit_to_entry_common((struct audit_rule *)data); if (IS_ERR(entry)) goto exit_nofree; bufp = data->buf; entry->rule.vers_ops = 2; for (i = 0; i < data->field_count; i++) { struct audit_field *f = &entry->rule.fields[i]; err = -EINVAL; if (!(data->fieldflags[i] & AUDIT_OPERATORS) || data->fieldflags[i] & ~AUDIT_OPERATORS) goto exit_free; f->op = data->fieldflags[i] & AUDIT_OPERATORS; f->type = data->fields[i]; f->val = data->values[i]; f->se_str = NULL; f->se_rule = NULL; switch(f->type) { case AUDIT_PID: case AUDIT_UID: case AUDIT_EUID: case AUDIT_SUID: case AUDIT_FSUID: case AUDIT_GID: case AUDIT_EGID: case AUDIT_SGID: case AUDIT_FSGID: case AUDIT_LOGINUID: case AUDIT_PERS: case AUDIT_MSGTYPE: case AUDIT_PPID: case AUDIT_DEVMAJOR: case AUDIT_DEVMINOR: case AUDIT_EXIT: case AUDIT_SUCCESS: case AUDIT_ARG0: case AUDIT_ARG1: case AUDIT_ARG2: case AUDIT_ARG3: break; case AUDIT_ARCH: entry->rule.arch_f = f; break; case AUDIT_SUBJ_USER: case AUDIT_SUBJ_ROLE: case AUDIT_SUBJ_TYPE: case AUDIT_SUBJ_SEN: case AUDIT_SUBJ_CLR: case AUDIT_OBJ_USER: case AUDIT_OBJ_ROLE: case AUDIT_OBJ_TYPE: case AUDIT_OBJ_LEV_LOW: case AUDIT_OBJ_LEV_HIGH: str = audit_unpack_string(&bufp, &remain, f->val); if (IS_ERR(str)) goto exit_free; entry->rule.buflen += f->val; err = selinux_audit_rule_init(f->type, f->op, str, &f->se_rule); /* Keep currently invalid fields around in case they * become valid after a policy reload. */ if (err == -EINVAL) { printk(KERN_WARNING "audit rule for selinux " "\'%s\' is invalid\n", str); err = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -