Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5adc8a6a authored by Amy Griffis's avatar Amy Griffis Committed by Al Viro
Browse files

[PATCH] add rule filterkey



Add support for a rule key, which can be used to tie audit records to audit
rules.  This is useful when a watched file is accessed through a link or
symlink, as well as for general audit log analysis.

Because this patch uses a string key instead of an integer key, there is a bit
of extra overhead to do the kstrdup() when a rule fires.  However, we're also
allocating memory for the audit record buffer, so it's probably not that
significant.  I went ahead with a string key because it seems more
user-friendly.

Note that the user must ensure that filterkeys are unique.  The kernel only
checks for duplicate rules.

Signed-off-by: default avatarAmy Griffis <amy.griffis@hpd.com>
parent 9262e914
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -122,6 +122,7 @@
/* Rule structure sizes -- if these change, different AUDIT_ADD and
/* Rule structure sizes -- if these change, different AUDIT_ADD and
 * AUDIT_LIST commands must be implemented. */
 * AUDIT_LIST commands must be implemented. */
#define AUDIT_MAX_FIELDS   64
#define AUDIT_MAX_FIELDS   64
#define AUDIT_MAX_KEY_LEN  32
#define AUDIT_BITMASK_SIZE 64
#define AUDIT_BITMASK_SIZE 64
#define AUDIT_WORD(nr) ((__u32)((nr)/32))
#define AUDIT_WORD(nr) ((__u32)((nr)/32))
#define AUDIT_BIT(nr)  (1 << ((nr) - AUDIT_WORD(nr)*32))
#define AUDIT_BIT(nr)  (1 << ((nr) - AUDIT_WORD(nr)*32))
@@ -171,6 +172,8 @@
#define AUDIT_ARG2      (AUDIT_ARG0+2)
#define AUDIT_ARG2      (AUDIT_ARG0+2)
#define AUDIT_ARG3      (AUDIT_ARG0+3)
#define AUDIT_ARG3      (AUDIT_ARG0+3)


#define AUDIT_FILTERKEY	210

#define AUDIT_NEGATE			0x80000000
#define AUDIT_NEGATE			0x80000000


/* These are the supported operators.
/* These are the supported operators.
+1 −0
Original line number Original line Diff line number Diff line
@@ -81,6 +81,7 @@ struct audit_krule {
	u32			mask[AUDIT_BITMASK_SIZE];
	u32			mask[AUDIT_BITMASK_SIZE];
	u32			buflen; /* for data alloc on list rules */
	u32			buflen; /* for data alloc on list rules */
	u32			field_count;
	u32			field_count;
	char			*filterkey; /* ties events to rules */
	struct audit_field	*fields;
	struct audit_field	*fields;
	struct audit_field	*inode_f; /* quick access to an inode field */
	struct audit_field	*inode_f; /* quick access to an inode field */
	struct audit_watch	*watch;	/* associated watch */
	struct audit_watch	*watch;	/* associated watch */
+59 −36
Original line number Original line Diff line number Diff line
@@ -141,6 +141,7 @@ static inline void audit_free_rule(struct audit_entry *e)
			selinux_audit_rule_free(f->se_rule);
			selinux_audit_rule_free(f->se_rule);
		}
		}
	kfree(e->rule.fields);
	kfree(e->rule.fields);
	kfree(e->rule.filterkey);
	kfree(e);
	kfree(e);
}
}


@@ -511,6 +512,16 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
			if (err)
			if (err)
				goto exit_free;
				goto exit_free;
			break;
			break;
		case AUDIT_FILTERKEY:
			err = -EINVAL;
			if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN)
				goto exit_free;
			str = audit_unpack_string(&bufp, &remain, f->val);
			if (IS_ERR(str))
				goto exit_free;
			entry->rule.buflen += f->val;
			entry->rule.filterkey = str;
			break;
		default:
		default:
			goto exit_free;
			goto exit_free;
		}
		}
@@ -612,6 +623,10 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
			data->buflen += data->values[i] =
			data->buflen += data->values[i] =
				audit_pack_string(&bufp, krule->watch->path);
				audit_pack_string(&bufp, krule->watch->path);
			break;
			break;
		case AUDIT_FILTERKEY:
			data->buflen += data->values[i] =
				audit_pack_string(&bufp, krule->filterkey);
			break;
		default:
		default:
			data->values[i] = f->val;
			data->values[i] = f->val;
		}
		}
@@ -651,6 +666,11 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
			if (strcmp(a->watch->path, b->watch->path))
			if (strcmp(a->watch->path, b->watch->path))
				return 1;
				return 1;
			break;
			break;
		case AUDIT_FILTERKEY:
			/* both filterkeys exist based on above type compare */
			if (strcmp(a->filterkey, b->filterkey))
				return 1;
			break;
		default:
		default:
			if (a->fields[i].val != b->fields[i].val)
			if (a->fields[i].val != b->fields[i].val)
				return 1;
				return 1;
@@ -730,6 +750,7 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
	u32 fcount = old->field_count;
	u32 fcount = old->field_count;
	struct audit_entry *entry;
	struct audit_entry *entry;
	struct audit_krule *new;
	struct audit_krule *new;
	char *fk;
	int i, err = 0;
	int i, err = 0;


	entry = audit_init_entry(fcount);
	entry = audit_init_entry(fcount);
@@ -760,6 +781,13 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
		case AUDIT_SE_CLR:
		case AUDIT_SE_CLR:
			err = audit_dupe_selinux_field(&new->fields[i],
			err = audit_dupe_selinux_field(&new->fields[i],
						       &old->fields[i]);
						       &old->fields[i]);
			break;
		case AUDIT_FILTERKEY:
			fk = kstrdup(old->filterkey, GFP_KERNEL);
			if (unlikely(!fk))
				err = -ENOMEM;
			else
				new->filterkey = fk;
		}
		}
		if (err) {
		if (err) {
			audit_free_rule(entry);
			audit_free_rule(entry);
@@ -1245,6 +1273,34 @@ static void audit_list_rules(int pid, int seq, struct sk_buff_head *q)
		skb_queue_tail(q, skb);
		skb_queue_tail(q, skb);
}
}


/* Log rule additions and removals */
static void audit_log_rule_change(uid_t loginuid, u32 sid, char *action,
				  struct audit_krule *rule, int res)
{
	struct audit_buffer *ab;

	ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
	if (!ab)
		return;
	audit_log_format(ab, "auid=%u", loginuid);
	if (sid) {
		char *ctx = NULL;
		u32 len;
		if (selinux_ctxid_to_string(sid, &ctx, &len))
			audit_log_format(ab, " ssid=%u", sid);
		else
			audit_log_format(ab, " subj=%s", ctx);
		kfree(ctx);
	}
	audit_log_format(ab, " %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
 * audit_receive_filter - apply all rules to the specified message type
 * @type: audit message type
 * @type: audit message type
@@ -1304,24 +1360,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,


		err = audit_add_rule(entry,
		err = audit_add_rule(entry,
				     &audit_filter_list[entry->rule.listnr]);
				     &audit_filter_list[entry->rule.listnr]);

		audit_log_rule_change(loginuid, sid, "add", &entry->rule, !err);
		if (sid) {
			char *ctx = NULL;
			u32 len;
			if (selinux_ctxid_to_string(sid, &ctx, &len)) {
				/* Maybe call audit_panic? */
				audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
				 "auid=%u ssid=%u add rule to list=%d res=%d",
				 loginuid, sid, entry->rule.listnr, !err);
			} else
				audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
				 "auid=%u subj=%s add rule to list=%d res=%d",
				 loginuid, ctx, entry->rule.listnr, !err);
			kfree(ctx);
		} else
			audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
				"auid=%u add rule to list=%d res=%d",
				loginuid, entry->rule.listnr, !err);


		if (err)
		if (err)
			audit_free_rule(entry);
			audit_free_rule(entry);
@@ -1337,24 +1376,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,


		err = audit_del_rule(entry,
		err = audit_del_rule(entry,
				     &audit_filter_list[entry->rule.listnr]);
				     &audit_filter_list[entry->rule.listnr]);

		audit_log_rule_change(loginuid, sid, "remove", &entry->rule,
		if (sid) {
				      !err);
			char *ctx = NULL;
			u32 len;
			if (selinux_ctxid_to_string(sid, &ctx, &len)) {
				/* Maybe call audit_panic? */
				audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
					"auid=%u ssid=%u remove rule from list=%d res=%d",
					 loginuid, sid, entry->rule.listnr, !err);
			} else
				audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
					"auid=%u subj=%s remove rule from list=%d res=%d",
					 loginuid, ctx, entry->rule.listnr, !err);
			kfree(ctx);
		} else
			audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
				"auid=%u remove rule from list=%d res=%d",
				loginuid, entry->rule.listnr, !err);


		audit_free_rule(entry);
		audit_free_rule(entry);
		break;
		break;
+15 −0
Original line number Original line Diff line number Diff line
@@ -186,6 +186,7 @@ struct audit_context {
	int		    auditable;  /* 1 if record should be written */
	int		    auditable;  /* 1 if record should be written */
	int		    name_count;
	int		    name_count;
	struct audit_names  names[AUDIT_NAMES];
	struct audit_names  names[AUDIT_NAMES];
	char *		    filterkey;	/* key for rule that triggered record */
	struct dentry *	    pwd;
	struct dentry *	    pwd;
	struct vfsmount *   pwdmnt;
	struct vfsmount *   pwdmnt;
	struct audit_context *previous; /* For nested syscalls */
	struct audit_context *previous; /* For nested syscalls */
@@ -348,11 +349,17 @@ static int audit_filter_rules(struct task_struct *tsk,
			if (ctx)
			if (ctx)
				result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
				result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
			break;
			break;
		case AUDIT_FILTERKEY:
			/* ignore this field for filtering */
			result = 1;
			break;
		}
		}


		if (!result)
		if (!result)
			return 0;
			return 0;
	}
	}
	if (rule->filterkey)
		ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
	switch (rule->action) {
	switch (rule->action) {
	case AUDIT_NEVER:    *state = AUDIT_DISABLED;	    break;
	case AUDIT_NEVER:    *state = AUDIT_DISABLED;	    break;
	case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
	case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
@@ -627,6 +634,7 @@ static inline void audit_free_context(struct audit_context *context)
		}
		}
		audit_free_names(context);
		audit_free_names(context);
		audit_free_aux(context);
		audit_free_aux(context);
		kfree(context->filterkey);
		kfree(context);
		kfree(context);
		context  = previous;
		context  = previous;
	} while (context);
	} while (context);
@@ -735,6 +743,11 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
		  context->euid, context->suid, context->fsuid,
		  context->euid, context->suid, context->fsuid,
		  context->egid, context->sgid, context->fsgid, tty);
		  context->egid, context->sgid, context->fsgid, tty);
	audit_log_task_info(ab, tsk);
	audit_log_task_info(ab, tsk);
	if (context->filterkey) {
		audit_log_format(ab, " key=");
		audit_log_untrustedstring(ab, context->filterkey);
	} else
		audit_log_format(ab, " key=(null)");
	audit_log_end(ab);
	audit_log_end(ab);


	for (aux = context->aux; aux; aux = aux->next) {
	for (aux = context->aux; aux; aux = aux->next) {
@@ -1060,6 +1073,8 @@ void audit_syscall_exit(int valid, long return_code)
	} else {
	} else {
		audit_free_names(context);
		audit_free_names(context);
		audit_free_aux(context);
		audit_free_aux(context);
		kfree(context->filterkey);
		context->filterkey = NULL;
		tsk->audit_context = context;
		tsk->audit_context = context;
	}
	}
}
}