auditfilter.c

来自「Kernel code of linux kernel」· C语言 代码 · 共 1,851 行 · 第 1/4 页

C
1,851
字号
				 * list.  Grab a reference before releasing				 * audit_filter_mutex, to be released in				 * audit_inotify_unregister(). */				list_add(&parent->ilist, &inotify_list);				get_inotify_watch(&parent->wdata);			}		}	}	if (e->rule.tree)		audit_remove_tree_rule(&e->rule);	list_del_rcu(&e->list);	call_rcu(&e->rcu, audit_free_rule_rcu);#ifdef CONFIG_AUDITSYSCALL	if (!dont_count)		audit_n_rules--;	if (!audit_match_signal(entry))		audit_signals--;#endif	mutex_unlock(&audit_filter_mutex);	if (!list_empty(&inotify_list))		audit_inotify_unregister(&inotify_list);out:	if (tmp_watch)		audit_put_watch(tmp_watch); /* match initial get */	if (tree)		audit_put_tree(tree);	/* that's the temporary one */	return ret;}/* List rules using struct audit_rule.  Exists for backward * compatibility with userspace. */static void audit_list(int pid, int seq, struct sk_buff_head *q){	struct sk_buff *skb;	struct audit_entry *entry;	int i;	/* This is a blocking read, so use audit_filter_mutex instead of rcu	 * iterator to sync with list writers. */	for (i=0; i<AUDIT_NR_FILTERS; i++) {		list_for_each_entry(entry, &audit_filter_list[i], list) {			struct audit_rule *rule;			rule = audit_krule_to_rule(&entry->rule);			if (unlikely(!rule))				break;			skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,					 rule, sizeof(*rule));			if (skb)				skb_queue_tail(q, skb);			kfree(rule);		}	}	for (i = 0; i < AUDIT_INODE_BUCKETS; i++) {		list_for_each_entry(entry, &audit_inode_hash[i], list) {			struct audit_rule *rule;			rule = audit_krule_to_rule(&entry->rule);			if (unlikely(!rule))				break;			skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,					 rule, sizeof(*rule));			if (skb)				skb_queue_tail(q, skb);			kfree(rule);		}	}	skb = audit_make_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);	if (skb)		skb_queue_tail(q, skb);}/* List rules using struct audit_rule_data. */static void audit_list_rules(int pid, int seq, struct sk_buff_head *q){	struct sk_buff *skb;	struct audit_entry *e;	int i;	/* This is a blocking read, so use audit_filter_mutex instead of rcu	 * iterator to sync with list writers. */	for (i=0; i<AUDIT_NR_FILTERS; i++) {		list_for_each_entry(e, &audit_filter_list[i], list) {			struct audit_rule_data *data;			data = audit_krule_to_data(&e->rule);			if (unlikely(!data))				break;			skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,					 data, sizeof(*data) + data->buflen);			if (skb)				skb_queue_tail(q, skb);			kfree(data);		}	}	for (i=0; i< AUDIT_INODE_BUCKETS; i++) {		list_for_each_entry(e, &audit_inode_hash[i], list) {			struct audit_rule_data *data;			data = audit_krule_to_data(&e->rule);			if (unlikely(!data))				break;			skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,					 data, sizeof(*data) + data->buflen);			if (skb)				skb_queue_tail(q, skb);			kfree(data);		}	}	skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);	if (skb)		skb_queue_tail(q, skb);}/* Log rule additions and removals */static void audit_log_rule_change(uid_t loginuid, u32 sessionid, u32 sid,				  char *action, struct audit_krule *rule,				  int res){	struct audit_buffer *ab;	if (!audit_enabled)		return;	ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);	if (!ab)		return;	audit_log_format(ab, "auid=%u ses=%u", loginuid, sessionid);	if (sid) {		char *ctx = NULL;		u32 len;		if (security_secid_to_secctx(sid, &ctx, &len))			audit_log_format(ab, " ssid=%u", sid);		else {			audit_log_format(ab, " subj=%s", ctx);			security_release_secctx(ctx, len);		}	}	audit_log_format(ab, " op=%s rule key=", action);	if (rule->filterkey)		audit_log_untrustedstring(ab, rule->filterkey);	else		audit_log_format(ab, "(null)");	audit_log_format(ab, " list=%d res=%d", rule->listnr, res);	audit_log_end(ab);}/** * audit_receive_filter - apply all rules to the specified message type * @type: audit message type * @pid: target pid for netlink audit messages * @uid: target uid for netlink audit messages * @seq: netlink audit message sequence (serial) number * @data: payload data * @datasz: size of payload data * @loginuid: loginuid of sender * @sessionid: sessionid for netlink audit message * @sid: SE Linux Security ID of sender */int audit_receive_filter(int type, int pid, int uid, int seq, void *data,			 size_t datasz, uid_t loginuid, u32 sessionid, u32 sid){	struct task_struct *tsk;	struct audit_netlink_list *dest;	int err = 0;	struct audit_entry *entry;	switch (type) {	case AUDIT_LIST:	case AUDIT_LIST_RULES:		/* We can't just spew out the rules here because we might fill		 * the available socket buffer space and deadlock waiting for		 * auditctl to read from it... which isn't ever going to		 * happen if we're actually running in the context of auditctl		 * trying to _send_ the stuff */		dest = kmalloc(sizeof(struct audit_netlink_list), GFP_KERNEL);		if (!dest)			return -ENOMEM;		dest->pid = pid;		skb_queue_head_init(&dest->q);		mutex_lock(&audit_filter_mutex);		if (type == AUDIT_LIST)			audit_list(pid, seq, &dest->q);		else			audit_list_rules(pid, seq, &dest->q);		mutex_unlock(&audit_filter_mutex);		tsk = kthread_run(audit_send_list, dest, "audit_send_list");		if (IS_ERR(tsk)) {			skb_queue_purge(&dest->q);			kfree(dest);			err = PTR_ERR(tsk);		}		break;	case AUDIT_ADD:	case AUDIT_ADD_RULE:		if (type == AUDIT_ADD)			entry = audit_rule_to_entry(data);		else			entry = audit_data_to_entry(data, datasz);		if (IS_ERR(entry))			return PTR_ERR(entry);		err = audit_add_rule(entry,				     &audit_filter_list[entry->rule.listnr]);		audit_log_rule_change(loginuid, sessionid, sid, "add",				      &entry->rule, !err);		if (err)			audit_free_rule(entry);		break;	case AUDIT_DEL:	case AUDIT_DEL_RULE:		if (type == AUDIT_DEL)			entry = audit_rule_to_entry(data);		else			entry = audit_data_to_entry(data, datasz);		if (IS_ERR(entry))			return PTR_ERR(entry);		err = audit_del_rule(entry,				     &audit_filter_list[entry->rule.listnr]);		audit_log_rule_change(loginuid, sessionid, sid, "remove",				      &entry->rule, !err);		audit_free_rule(entry);		break;	default:		return -EINVAL;	}	return err;}int audit_comparator(const u32 left, const u32 op, const u32 right){	switch (op) {	case AUDIT_EQUAL:		return (left == right);	case AUDIT_NOT_EQUAL:		return (left != right);	case AUDIT_LESS_THAN:		return (left < right);	case AUDIT_LESS_THAN_OR_EQUAL:		return (left <= right);	case AUDIT_GREATER_THAN:		return (left > right);	case AUDIT_GREATER_THAN_OR_EQUAL:		return (left >= right);	case AUDIT_BIT_MASK:		return (left & right);	case AUDIT_BIT_TEST:		return ((left & right) == right);	}	BUG();	return 0;}/* Compare given dentry name with last component in given path, * return of 0 indicates a match. */int audit_compare_dname_path(const char *dname, const char *path,			     int *dirlen){	int dlen, plen;	const char *p;	if (!dname || !path)		return 1;	dlen = strlen(dname);	plen = strlen(path);	if (plen < dlen)		return 1;	/* disregard trailing slashes */	p = path + plen - 1;	while ((*p == '/') && (p > path))		p--;	/* find last path component */	p = p - dlen + 1;	if (p < path)		return 1;	else if (p > path) {		if (*--p != '/')			return 1;		else			p++;	}	/* return length of path's directory component */	if (dirlen)		*dirlen = p - path;	return strncmp(p, dname, dlen);}static int audit_filter_user_rules(struct netlink_skb_parms *cb,				   struct audit_krule *rule,				   enum audit_state *state){	int i;	for (i = 0; i < rule->field_count; i++) {		struct audit_field *f = &rule->fields[i];		int result = 0;		switch (f->type) {		case AUDIT_PID:			result = audit_comparator(cb->creds.pid, f->op, f->val);			break;		case AUDIT_UID:			result = audit_comparator(cb->creds.uid, f->op, f->val);			break;		case AUDIT_GID:			result = audit_comparator(cb->creds.gid, f->op, f->val);			break;		case AUDIT_LOGINUID:			result = audit_comparator(cb->loginuid, f->op, f->val);			break;		}		if (!result)			return 0;	}	switch (rule->action) {	case AUDIT_NEVER:    *state = AUDIT_DISABLED;	    break;	case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;	}	return 1;}int audit_filter_user(struct netlink_skb_parms *cb){	enum audit_state state = AUDIT_DISABLED;	struct audit_entry *e;	int ret = 1;	rcu_read_lock();	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {		if (audit_filter_user_rules(cb, &e->rule, &state)) {			if (state == AUDIT_DISABLED)				ret = 0;			break;		}	}	rcu_read_unlock();	return ret; /* Audit by default */}int audit_filter_type(int type){	struct audit_entry *e;	int result = 0;	rcu_read_lock();	if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE]))		goto unlock_and_return;	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE],				list) {		int i;		for (i = 0; i < e->rule.field_count; i++) {			struct audit_field *f = &e->rule.fields[i];			if (f->type == AUDIT_MSGTYPE) {				result = audit_comparator(type, f->op, f->val);				if (!result)					break;			}		}		if (result)			goto unlock_and_return;	}unlock_and_return:	rcu_read_unlock();	return result;}/* This function will re-initialize the lsm_rule field of all applicable rules. * It will traverse the filter lists serarching for rules that contain LSM * specific filter fields.  When such a rule is found, it is copied, the * LSM field is re-initialized, and the old rule is replaced with the * updated rule. */int audit_update_lsm_rules(void){	struct audit_entry *entry, *n, *nentry;	struct audit_watch *watch;	struct audit_tree *tree;	int i, err = 0;	/* audit_filter_mutex synchronizes the writers */	mutex_lock(&audit_filter_mutex);	for (i = 0; i < AUDIT_NR_FILTERS; i++) {		list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) {			if (!security_audit_rule_known(&entry->rule))				continue;			watch = entry->rule.watch;			tree = entry->rule.tree;			nentry = audit_dupe_rule(&entry->rule, watch);			if (IS_ERR(nentry)) {				/* save the first error encountered for the				 * return value */				if (!err)					err = PTR_ERR(nentry);				audit_panic("error updating LSM filters");				if (watch)					list_del(&entry->rule.rlist);				list_del_rcu(&entry->list);			} else {				if (watch) {					list_add(&nentry->rule.rlist,						 &watch->rules);					list_del(&entry->rule.rlist);				} else if (tree)					list_replace_init(&entry->rule.rlist,						     &nentry->rule.rlist);				list_replace_rcu(&entry->list, &nentry->list);			}			call_rcu(&entry->rcu, audit_free_rule_rcu);		}	}	mutex_unlock(&audit_filter_mutex);	return err;}/* Update watch data in audit rules based on inotify events. */void audit_handle_ievent(struct inotify_watch *i_watch, u32 wd, u32 mask,			 u32 cookie, const char *dname, struct inode *inode){	struct audit_parent *parent;	parent = container_of(i_watch, struct audit_parent, wdata);	if (mask & (IN_CREATE|IN_MOVED_TO) && inode)		audit_update_watch(parent, dname, inode->i_sb->s_dev,				   inode->i_ino, 0);	else if (mask & (IN_DELETE|IN_MOVED_FROM))		audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1);	/* inotify automatically removes the watch and sends IN_IGNORED */	else if (mask & (IN_DELETE_SELF|IN_UNMOUNT))		audit_remove_parent_watches(parent);	/* inotify does not remove the watch, so remove it manually */	else if(mask & IN_MOVE_SELF) {		audit_remove_parent_watches(parent);		inotify_remove_watch_locked(audit_ih, i_watch);	} else if (mask & IN_IGNORED)		put_inotify_watch(i_watch);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?