1458 lines · 34.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* auditfilter.c -- filtering of audit events * * Copyright 2003-2004 Red Hat, Inc. * Copyright 2005 Hewlett-Packard Development Company, L.P. * Copyright 2005 IBM Corporation */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #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/slab.h>#include <linux/security.h>#include <net/net_namespace.h>#include <net/sock.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 *		LSM 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. */ /* 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]),	LIST_HEAD_INIT(audit_filter_list[6]),	LIST_HEAD_INIT(audit_filter_list[7]),#if AUDIT_NR_FILTERS != 8#error Fix audit_filter_list initialiser#endif};static struct list_head audit_rules_list[AUDIT_NR_FILTERS] = {	LIST_HEAD_INIT(audit_rules_list[0]),	LIST_HEAD_INIT(audit_rules_list[1]),	LIST_HEAD_INIT(audit_rules_list[2]),	LIST_HEAD_INIT(audit_rules_list[3]),	LIST_HEAD_INIT(audit_rules_list[4]),	LIST_HEAD_INIT(audit_rules_list[5]),	LIST_HEAD_INIT(audit_rules_list[6]),	LIST_HEAD_INIT(audit_rules_list[7]),}; DEFINE_MUTEX(audit_filter_mutex); static void audit_free_lsm_field(struct audit_field *f){	switch (f->type) {	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:		kfree(f->lsm_str);		security_audit_rule_free(f->lsm_rule);	}} static inline void audit_free_rule(struct audit_entry *e){	int i;	struct audit_krule *erule = &e->rule; 	/* some rules don't have associated watches */	if (erule->watch)		audit_put_watch(erule->watch);	if (erule->fields)		for (i = 0; i < erule->field_count; i++)			audit_free_lsm_field(&erule->fields[i]);	kfree(erule->fields);	kfree(erule->filterkey);	kfree(e);} 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 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 = kcalloc(field_count, sizeof(*fields), 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. */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 representation. */static inline int audit_to_inode(struct audit_krule *krule,				 struct audit_field *f){	if ((krule->listnr != AUDIT_FILTER_EXIT &&	     krule->listnr != AUDIT_FILTER_URING_EXIT) ||	    krule->inode_f || krule->watch || krule->tree ||	    (f->op != Audit_equal && f->op != Audit_not_equal))		return -EINVAL; 	krule->inode_f = f;	return 0;} static __u32 *classes[AUDIT_SYSCALL_CLASSES]; int __init audit_register_class(int class, unsigned *list){	__u32 *p = kcalloc(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 * 32))		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_data *rule){	unsigned listnr;	struct audit_entry *entry;	int i, err; 	err = -EINVAL;	listnr = rule->flags & ~AUDIT_FILTER_PREPEND;	switch (listnr) {	default:		goto exit_err;#ifdef CONFIG_AUDITSYSCALL	case AUDIT_FILTER_ENTRY:		pr_err("AUDIT_FILTER_ENTRY is deprecated\n");		goto exit_err;	case AUDIT_FILTER_EXIT:	case AUDIT_FILTER_URING_EXIT:	case AUDIT_FILTER_TASK:#endif	case AUDIT_FILTER_USER:	case AUDIT_FILTER_EXCLUDE:	case AUDIT_FILTER_FS:		;	}	if (unlikely(rule->action == AUDIT_POSSIBLE)) {		pr_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);} static u32 audit_ops[] ={	[Audit_equal] = AUDIT_EQUAL,	[Audit_not_equal] = AUDIT_NOT_EQUAL,	[Audit_bitmask] = AUDIT_BIT_MASK,	[Audit_bittest] = AUDIT_BIT_TEST,	[Audit_lt] = AUDIT_LESS_THAN,	[Audit_gt] = AUDIT_GREATER_THAN,	[Audit_le] = AUDIT_LESS_THAN_OR_EQUAL,	[Audit_ge] = AUDIT_GREATER_THAN_OR_EQUAL,}; static u32 audit_to_op(u32 op){	u32 n;	for (n = Audit_equal; n < Audit_bad && audit_ops[n] != op; n++)		;	return n;} /* check if an audit field is valid */static int audit_field_valid(struct audit_entry *entry, struct audit_field *f){	switch (f->type) {	case AUDIT_MSGTYPE:		if (entry->rule.listnr != AUDIT_FILTER_EXCLUDE &&		    entry->rule.listnr != AUDIT_FILTER_USER)			return -EINVAL;		break;	case AUDIT_FSTYPE:		if (entry->rule.listnr != AUDIT_FILTER_FS)			return -EINVAL;		break;	case AUDIT_PERM:		if (entry->rule.listnr == AUDIT_FILTER_URING_EXIT)			return -EINVAL;		break;	} 	switch (entry->rule.listnr) {	case AUDIT_FILTER_FS:		switch (f->type) {		case AUDIT_FSTYPE:		case AUDIT_FILTERKEY:			break;		default:			return -EINVAL;		}	} 	/* Check for valid field type and op */	switch (f->type) {	case AUDIT_ARG0:	case AUDIT_ARG1:	case AUDIT_ARG2:	case AUDIT_ARG3:	case AUDIT_PERS: /* <uapi/linux/personality.h> */	case AUDIT_DEVMINOR:		/* all ops are valid */		break;	case AUDIT_UID:	case AUDIT_EUID:	case AUDIT_SUID:	case AUDIT_FSUID:	case AUDIT_LOGINUID:	case AUDIT_OBJ_UID:	case AUDIT_GID:	case AUDIT_EGID:	case AUDIT_SGID:	case AUDIT_FSGID:	case AUDIT_OBJ_GID:	case AUDIT_PID:	case AUDIT_MSGTYPE:	case AUDIT_PPID:	case AUDIT_DEVMAJOR:	case AUDIT_EXIT:	case AUDIT_SUCCESS:	case AUDIT_INODE:	case AUDIT_SESSIONID:	case AUDIT_SUBJ_SEN:	case AUDIT_SUBJ_CLR:	case AUDIT_OBJ_LEV_LOW:	case AUDIT_OBJ_LEV_HIGH:	case AUDIT_SADDR_FAM:		/* bit ops are only useful on syscall args */		if (f->op == Audit_bitmask || f->op == Audit_bittest)			return -EINVAL;		break;	case AUDIT_SUBJ_USER:	case AUDIT_SUBJ_ROLE:	case AUDIT_SUBJ_TYPE:	case AUDIT_OBJ_USER:	case AUDIT_OBJ_ROLE:	case AUDIT_OBJ_TYPE:	case AUDIT_WATCH:	case AUDIT_DIR:	case AUDIT_FILTERKEY:	case AUDIT_LOGINUID_SET:	case AUDIT_ARCH:	case AUDIT_FSTYPE:	case AUDIT_PERM:	case AUDIT_FILETYPE:	case AUDIT_FIELD_COMPARE:	case AUDIT_EXE:		/* only equal and not equal valid ops */		if (f->op != Audit_not_equal && f->op != Audit_equal)			return -EINVAL;		break;	default:		/* field not recognized */		return -EINVAL;	} 	/* Check for select valid field values */	switch (f->type) {	case AUDIT_LOGINUID_SET:		if ((f->val != 0) && (f->val != 1))			return -EINVAL;		break;	case AUDIT_PERM:		if (f->val & ~15)			return -EINVAL;		break;	case AUDIT_FILETYPE:		if (f->val & ~S_IFMT)			return -EINVAL;		break;	case AUDIT_FIELD_COMPARE:		if (f->val > AUDIT_MAX_FIELD_COMPARE)			return -EINVAL;		break;	case AUDIT_SADDR_FAM:		if (f->val >= AF_MAX)			return -EINVAL;		break;	default:		break;	} 	return 0;} /* Translate struct audit_rule_data to kernel's rule representation. */static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,					       size_t datasz){	int err = 0;	struct audit_entry *entry;	void *bufp;	size_t remain = datasz - sizeof(struct audit_rule_data);	int i;	char *str;	struct audit_fsnotify_mark *audit_mark; 	entry = audit_to_entry_common(data);	if (IS_ERR(entry))		goto exit_nofree; 	bufp = data->buf;	for (i = 0; i < data->field_count; i++) {		struct audit_field *f = &entry->rule.fields[i];		u32 f_val; 		err = -EINVAL; 		f->op = audit_to_op(data->fieldflags[i]);		if (f->op == Audit_bad)			goto exit_free; 		f->type = data->fields[i];		f_val = data->values[i]; 		/* Support legacy tests for a valid loginuid */		if ((f->type == AUDIT_LOGINUID) && (f_val == AUDIT_UID_UNSET)) {			f->type = AUDIT_LOGINUID_SET;			f_val = 0;			entry->rule.pflags |= AUDIT_LOGINUID_LEGACY;		} 		err = audit_field_valid(entry, f);		if (err)			goto exit_free; 		err = -EINVAL;		switch (f->type) {		case AUDIT_LOGINUID:		case AUDIT_UID:		case AUDIT_EUID:		case AUDIT_SUID:		case AUDIT_FSUID:		case AUDIT_OBJ_UID:			f->uid = make_kuid(current_user_ns(), f_val);			if (!uid_valid(f->uid))				goto exit_free;			break;

Lines 1–500 of 1458.