11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2fe7752baSDavid Woodhouse /* auditfilter.c -- filtering of audit events
3fe7752baSDavid Woodhouse *
4fe7752baSDavid Woodhouse * Copyright 2003-2004 Red Hat, Inc.
5fe7752baSDavid Woodhouse * Copyright 2005 Hewlett-Packard Development Company, L.P.
6fe7752baSDavid Woodhouse * Copyright 2005 IBM Corporation
7fe7752baSDavid Woodhouse */
8fe7752baSDavid Woodhouse
9f952d10fSRichard Guy Briggs #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10f952d10fSRichard Guy Briggs
11fe7752baSDavid Woodhouse #include <linux/kernel.h>
12fe7752baSDavid Woodhouse #include <linux/audit.h>
13fe7752baSDavid Woodhouse #include <linux/kthread.h>
14f368c07dSAmy Griffis #include <linux/mutex.h>
15f368c07dSAmy Griffis #include <linux/fs.h>
16f368c07dSAmy Griffis #include <linux/namei.h>
17fe7752baSDavid Woodhouse #include <linux/netlink.h>
18f368c07dSAmy Griffis #include <linux/sched.h>
195a0e3ad6STejun Heo #include <linux/slab.h>
202a862b32SAhmed S. Darwish #include <linux/security.h>
2148095d99SEric W. Biederman #include <net/net_namespace.h>
226f285b19SEric W. Biederman #include <net/sock.h>
23fe7752baSDavid Woodhouse #include "audit.h"
24fe7752baSDavid Woodhouse
25f368c07dSAmy Griffis /*
26f368c07dSAmy Griffis * Locking model:
27f368c07dSAmy Griffis *
28f368c07dSAmy Griffis * audit_filter_mutex:
29f368c07dSAmy Griffis * Synchronizes writes and blocking reads of audit's filterlist
30f368c07dSAmy Griffis * data. Rcu is used to traverse the filterlist and access
31f368c07dSAmy Griffis * contents of structs audit_entry, audit_watch and opaque
32d7a96f3aSAhmed S. Darwish * LSM rules during filtering. If modified, these structures
33f368c07dSAmy Griffis * must be copied and replace their counterparts in the filterlist.
34f368c07dSAmy Griffis * An audit_parent struct is not accessed during filtering, so may
35f368c07dSAmy Griffis * be written directly provided audit_filter_mutex is held.
36f368c07dSAmy Griffis */
37f368c07dSAmy Griffis
38f368c07dSAmy Griffis /* Audit filter lists, defined in <linux/audit.h> */
39fe7752baSDavid Woodhouse struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
40fe7752baSDavid Woodhouse LIST_HEAD_INIT(audit_filter_list[0]),
41fe7752baSDavid Woodhouse LIST_HEAD_INIT(audit_filter_list[1]),
42fe7752baSDavid Woodhouse LIST_HEAD_INIT(audit_filter_list[2]),
43fe7752baSDavid Woodhouse LIST_HEAD_INIT(audit_filter_list[3]),
44fe7752baSDavid Woodhouse LIST_HEAD_INIT(audit_filter_list[4]),
45fe7752baSDavid Woodhouse LIST_HEAD_INIT(audit_filter_list[5]),
4642d5e376SRichard Guy Briggs LIST_HEAD_INIT(audit_filter_list[6]),
4767daf270SPaul Moore LIST_HEAD_INIT(audit_filter_list[7]),
4867daf270SPaul Moore #if AUDIT_NR_FILTERS != 8
49fe7752baSDavid Woodhouse #error Fix audit_filter_list initialiser
50fe7752baSDavid Woodhouse #endif
51fe7752baSDavid Woodhouse };
52e45aa212SAl Viro static struct list_head audit_rules_list[AUDIT_NR_FILTERS] = {
53e45aa212SAl Viro LIST_HEAD_INIT(audit_rules_list[0]),
54e45aa212SAl Viro LIST_HEAD_INIT(audit_rules_list[1]),
55e45aa212SAl Viro LIST_HEAD_INIT(audit_rules_list[2]),
56e45aa212SAl Viro LIST_HEAD_INIT(audit_rules_list[3]),
57e45aa212SAl Viro LIST_HEAD_INIT(audit_rules_list[4]),
58e45aa212SAl Viro LIST_HEAD_INIT(audit_rules_list[5]),
5942d5e376SRichard Guy Briggs LIST_HEAD_INIT(audit_rules_list[6]),
6067daf270SPaul Moore LIST_HEAD_INIT(audit_rules_list[7]),
61e45aa212SAl Viro };
62fe7752baSDavid Woodhouse
6374c3cbe3SAl Viro DEFINE_MUTEX(audit_filter_mutex);
64f368c07dSAmy Griffis
audit_free_lsm_field(struct audit_field * f)65219ca394SRichard Guy Briggs static void audit_free_lsm_field(struct audit_field *f)
66219ca394SRichard Guy Briggs {
67219ca394SRichard Guy Briggs switch (f->type) {
68219ca394SRichard Guy Briggs case AUDIT_SUBJ_USER:
69219ca394SRichard Guy Briggs case AUDIT_SUBJ_ROLE:
70219ca394SRichard Guy Briggs case AUDIT_SUBJ_TYPE:
71219ca394SRichard Guy Briggs case AUDIT_SUBJ_SEN:
72219ca394SRichard Guy Briggs case AUDIT_SUBJ_CLR:
73219ca394SRichard Guy Briggs case AUDIT_OBJ_USER:
74219ca394SRichard Guy Briggs case AUDIT_OBJ_ROLE:
75219ca394SRichard Guy Briggs case AUDIT_OBJ_TYPE:
76219ca394SRichard Guy Briggs case AUDIT_OBJ_LEV_LOW:
77219ca394SRichard Guy Briggs case AUDIT_OBJ_LEV_HIGH:
78219ca394SRichard Guy Briggs kfree(f->lsm_str);
79219ca394SRichard Guy Briggs security_audit_rule_free(f->lsm_rule);
80219ca394SRichard Guy Briggs }
81219ca394SRichard Guy Briggs }
82219ca394SRichard Guy Briggs
audit_free_rule(struct audit_entry * e)8393315ed6SAmy Griffis static inline void audit_free_rule(struct audit_entry *e)
8493315ed6SAmy Griffis {
853dc7e315SDarrel Goeddel int i;
86c28bb7daSZhenwen Xu struct audit_krule *erule = &e->rule;
87ae7b8f41SEric Paris
88f368c07dSAmy Griffis /* some rules don't have associated watches */
89c28bb7daSZhenwen Xu if (erule->watch)
90c28bb7daSZhenwen Xu audit_put_watch(erule->watch);
91c28bb7daSZhenwen Xu if (erule->fields)
92219ca394SRichard Guy Briggs for (i = 0; i < erule->field_count; i++)
93219ca394SRichard Guy Briggs audit_free_lsm_field(&erule->fields[i]);
94c28bb7daSZhenwen Xu kfree(erule->fields);
95c28bb7daSZhenwen Xu kfree(erule->filterkey);
9693315ed6SAmy Griffis kfree(e);
9793315ed6SAmy Griffis }
9893315ed6SAmy Griffis
audit_free_rule_rcu(struct rcu_head * head)9974c3cbe3SAl Viro void audit_free_rule_rcu(struct rcu_head *head)
10093315ed6SAmy Griffis {
10193315ed6SAmy Griffis struct audit_entry *e = container_of(head, struct audit_entry, rcu);
10293315ed6SAmy Griffis audit_free_rule(e);
10393315ed6SAmy Griffis }
10493315ed6SAmy Griffis
1053dc7e315SDarrel Goeddel /* Initialize an audit filterlist entry. */
audit_init_entry(u32 field_count)1063dc7e315SDarrel Goeddel static inline struct audit_entry *audit_init_entry(u32 field_count)
1073dc7e315SDarrel Goeddel {
1083dc7e315SDarrel Goeddel struct audit_entry *entry;
1093dc7e315SDarrel Goeddel struct audit_field *fields;
1103dc7e315SDarrel Goeddel
1113dc7e315SDarrel Goeddel entry = kzalloc(sizeof(*entry), GFP_KERNEL);
1123dc7e315SDarrel Goeddel if (unlikely(!entry))
1133dc7e315SDarrel Goeddel return NULL;
1143dc7e315SDarrel Goeddel
115bab5e2d6SFabian Frederick fields = kcalloc(field_count, sizeof(*fields), GFP_KERNEL);
1163dc7e315SDarrel Goeddel if (unlikely(!fields)) {
1173dc7e315SDarrel Goeddel kfree(entry);
1183dc7e315SDarrel Goeddel return NULL;
1193dc7e315SDarrel Goeddel }
1203dc7e315SDarrel Goeddel entry->rule.fields = fields;
1213dc7e315SDarrel Goeddel
1223dc7e315SDarrel Goeddel return entry;
1233dc7e315SDarrel Goeddel }
1243dc7e315SDarrel Goeddel
12593315ed6SAmy Griffis /* Unpack a filter field's string representation from user-space
12693315ed6SAmy Griffis * buffer. */
audit_unpack_string(void ** bufp,size_t * remain,size_t len)12774c3cbe3SAl Viro char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
12893315ed6SAmy Griffis {
12993315ed6SAmy Griffis char *str;
13093315ed6SAmy Griffis
13193315ed6SAmy Griffis if (!*bufp || (len == 0) || (len > *remain))
13293315ed6SAmy Griffis return ERR_PTR(-EINVAL);
13393315ed6SAmy Griffis
13493315ed6SAmy Griffis /* Of the currently implemented string fields, PATH_MAX
13593315ed6SAmy Griffis * defines the longest valid length.
13693315ed6SAmy Griffis */
13793315ed6SAmy Griffis if (len > PATH_MAX)
13893315ed6SAmy Griffis return ERR_PTR(-ENAMETOOLONG);
13993315ed6SAmy Griffis
14093315ed6SAmy Griffis str = kmalloc(len + 1, GFP_KERNEL);
14193315ed6SAmy Griffis if (unlikely(!str))
14293315ed6SAmy Griffis return ERR_PTR(-ENOMEM);
14393315ed6SAmy Griffis
14493315ed6SAmy Griffis memcpy(str, *bufp, len);
14593315ed6SAmy Griffis str[len] = 0;
14693315ed6SAmy Griffis *bufp += len;
14793315ed6SAmy Griffis *remain -= len;
14893315ed6SAmy Griffis
14993315ed6SAmy Griffis return str;
15093315ed6SAmy Griffis }
15193315ed6SAmy Griffis
152fd97646bSWei Yuan /* Translate an inode field to kernel representation. */
audit_to_inode(struct audit_krule * krule,struct audit_field * f)153f368c07dSAmy Griffis static inline int audit_to_inode(struct audit_krule *krule,
154f368c07dSAmy Griffis struct audit_field *f)
155f368c07dSAmy Griffis {
15667daf270SPaul Moore if ((krule->listnr != AUDIT_FILTER_EXIT &&
15767daf270SPaul Moore krule->listnr != AUDIT_FILTER_URING_EXIT) ||
1583639f170SRichard Guy Briggs krule->inode_f || krule->watch || krule->tree ||
1595af75d8dSAl Viro (f->op != Audit_equal && f->op != Audit_not_equal))
160f368c07dSAmy Griffis return -EINVAL;
161f368c07dSAmy Griffis
162f368c07dSAmy Griffis krule->inode_f = f;
163f368c07dSAmy Griffis return 0;
164f368c07dSAmy Griffis }
165f368c07dSAmy Griffis
166b915543bSAl Viro static __u32 *classes[AUDIT_SYSCALL_CLASSES];
167b915543bSAl Viro
audit_register_class(int class,unsigned * list)168b915543bSAl Viro int __init audit_register_class(int class, unsigned *list)
169b915543bSAl Viro {
170bab5e2d6SFabian Frederick __u32 *p = kcalloc(AUDIT_BITMASK_SIZE, sizeof(__u32), GFP_KERNEL);
171b915543bSAl Viro if (!p)
172b915543bSAl Viro return -ENOMEM;
173b915543bSAl Viro while (*list != ~0U) {
174b915543bSAl Viro unsigned n = *list++;
175b915543bSAl Viro if (n >= AUDIT_BITMASK_SIZE * 32 - AUDIT_SYSCALL_CLASSES) {
176b915543bSAl Viro kfree(p);
177b915543bSAl Viro return -EINVAL;
178b915543bSAl Viro }
179b915543bSAl Viro p[AUDIT_WORD(n)] |= AUDIT_BIT(n);
180b915543bSAl Viro }
181b915543bSAl Viro if (class >= AUDIT_SYSCALL_CLASSES || classes[class]) {
182b915543bSAl Viro kfree(p);
183b915543bSAl Viro return -EINVAL;
184b915543bSAl Viro }
185b915543bSAl Viro classes[class] = p;
186b915543bSAl Viro return 0;
187b915543bSAl Viro }
188b915543bSAl Viro
audit_match_class(int class,unsigned syscall)18955669bfaSAl Viro int audit_match_class(int class, unsigned syscall)
19055669bfaSAl Viro {
191c926e4f4SKlaus Weidner if (unlikely(syscall >= AUDIT_BITMASK_SIZE * 32))
19255669bfaSAl Viro return 0;
19355669bfaSAl Viro if (unlikely(class >= AUDIT_SYSCALL_CLASSES || !classes[class]))
19455669bfaSAl Viro return 0;
19555669bfaSAl Viro return classes[class][AUDIT_WORD(syscall)] & AUDIT_BIT(syscall);
19655669bfaSAl Viro }
19755669bfaSAl Viro
198327b9eebSAl Viro #ifdef CONFIG_AUDITSYSCALL
audit_match_class_bits(int class,u32 * mask)199e54dc243SAmy Griffis static inline int audit_match_class_bits(int class, u32 *mask)
200e54dc243SAmy Griffis {
201e54dc243SAmy Griffis int i;
202e54dc243SAmy Griffis
203e54dc243SAmy Griffis if (classes[class]) {
204e54dc243SAmy Griffis for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
205e54dc243SAmy Griffis if (mask[i] & classes[class][i])
206e54dc243SAmy Griffis return 0;
207e54dc243SAmy Griffis }
208e54dc243SAmy Griffis return 1;
209e54dc243SAmy Griffis }
210e54dc243SAmy Griffis
audit_match_signal(struct audit_entry * entry)211e54dc243SAmy Griffis static int audit_match_signal(struct audit_entry *entry)
212e54dc243SAmy Griffis {
213e54dc243SAmy Griffis struct audit_field *arch = entry->rule.arch_f;
214e54dc243SAmy Griffis
215e54dc243SAmy Griffis if (!arch) {
216e54dc243SAmy Griffis /* When arch is unspecified, we must check both masks on biarch
217e54dc243SAmy Griffis * as syscall number alone is ambiguous. */
218e54dc243SAmy Griffis return (audit_match_class_bits(AUDIT_CLASS_SIGNAL,
219e54dc243SAmy Griffis entry->rule.mask) &&
220e54dc243SAmy Griffis audit_match_class_bits(AUDIT_CLASS_SIGNAL_32,
221e54dc243SAmy Griffis entry->rule.mask));
222e54dc243SAmy Griffis }
223e54dc243SAmy Griffis
224e54dc243SAmy Griffis switch (audit_classify_arch(arch->val)) {
225e54dc243SAmy Griffis case 0: /* native */
226e54dc243SAmy Griffis return (audit_match_class_bits(AUDIT_CLASS_SIGNAL,
227e54dc243SAmy Griffis entry->rule.mask));
228e54dc243SAmy Griffis case 1: /* 32bit on biarch */
229e54dc243SAmy Griffis return (audit_match_class_bits(AUDIT_CLASS_SIGNAL_32,
230e54dc243SAmy Griffis entry->rule.mask));
231e54dc243SAmy Griffis default:
232e54dc243SAmy Griffis return 1;
233e54dc243SAmy Griffis }
234e54dc243SAmy Griffis }
235327b9eebSAl Viro #endif
236e54dc243SAmy Griffis
23793315ed6SAmy Griffis /* Common user-space to kernel rule translation. */
audit_to_entry_common(struct audit_rule_data * rule)23856c4911aSEric Paris static inline struct audit_entry *audit_to_entry_common(struct audit_rule_data *rule)
23993315ed6SAmy Griffis {
24093315ed6SAmy Griffis unsigned listnr;
24193315ed6SAmy Griffis struct audit_entry *entry;
24293315ed6SAmy Griffis int i, err;
24393315ed6SAmy Griffis
24493315ed6SAmy Griffis err = -EINVAL;
24593315ed6SAmy Griffis listnr = rule->flags & ~AUDIT_FILTER_PREPEND;
24693315ed6SAmy Griffis switch (listnr) {
24793315ed6SAmy Griffis default:
24893315ed6SAmy Griffis goto exit_err;
24993315ed6SAmy Griffis #ifdef CONFIG_AUDITSYSCALL
25093315ed6SAmy Griffis case AUDIT_FILTER_ENTRY:
2515260ecc2SRichard Guy Briggs pr_err("AUDIT_FILTER_ENTRY is deprecated\n");
2527ff68e53SEric Paris goto exit_err;
25393315ed6SAmy Griffis case AUDIT_FILTER_EXIT:
25467daf270SPaul Moore case AUDIT_FILTER_URING_EXIT:
25593315ed6SAmy Griffis case AUDIT_FILTER_TASK:
25693315ed6SAmy Griffis #endif
2577ff68e53SEric Paris case AUDIT_FILTER_USER:
258d904ac03SRichard Guy Briggs case AUDIT_FILTER_EXCLUDE:
25942d5e376SRichard Guy Briggs case AUDIT_FILTER_FS:
26093315ed6SAmy Griffis ;
26193315ed6SAmy Griffis }
262014149ccSAl Viro if (unlikely(rule->action == AUDIT_POSSIBLE)) {
263f952d10fSRichard Guy Briggs pr_err("AUDIT_POSSIBLE is deprecated\n");
264014149ccSAl Viro goto exit_err;
265014149ccSAl Viro }
266014149ccSAl Viro if (rule->action != AUDIT_NEVER && rule->action != AUDIT_ALWAYS)
26793315ed6SAmy Griffis goto exit_err;
26893315ed6SAmy Griffis if (rule->field_count > AUDIT_MAX_FIELDS)
26993315ed6SAmy Griffis goto exit_err;
27093315ed6SAmy Griffis
27193315ed6SAmy Griffis err = -ENOMEM;
2723dc7e315SDarrel Goeddel entry = audit_init_entry(rule->field_count);
2733dc7e315SDarrel Goeddel if (!entry)
27493315ed6SAmy Griffis goto exit_err;
27593315ed6SAmy Griffis
27693315ed6SAmy Griffis entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
27793315ed6SAmy Griffis entry->rule.listnr = listnr;
27893315ed6SAmy Griffis entry->rule.action = rule->action;
27993315ed6SAmy Griffis entry->rule.field_count = rule->field_count;
28093315ed6SAmy Griffis
28193315ed6SAmy Griffis for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
28293315ed6SAmy Griffis entry->rule.mask[i] = rule->mask[i];
28393315ed6SAmy Griffis
284b915543bSAl Viro for (i = 0; i < AUDIT_SYSCALL_CLASSES; i++) {
285b915543bSAl Viro int bit = AUDIT_BITMASK_SIZE * 32 - i - 1;
286b915543bSAl Viro __u32 *p = &entry->rule.mask[AUDIT_WORD(bit)];
287b915543bSAl Viro __u32 *class;
288b915543bSAl Viro
289b915543bSAl Viro if (!(*p & AUDIT_BIT(bit)))
290b915543bSAl Viro continue;
291b915543bSAl Viro *p &= ~AUDIT_BIT(bit);
292b915543bSAl Viro class = classes[i];
293b915543bSAl Viro if (class) {
294b915543bSAl Viro int j;
295b915543bSAl Viro for (j = 0; j < AUDIT_BITMASK_SIZE; j++)
296b915543bSAl Viro entry->rule.mask[j] |= class[j];
297b915543bSAl Viro }
298b915543bSAl Viro }
299b915543bSAl Viro
30093315ed6SAmy Griffis return entry;
30193315ed6SAmy Griffis
30293315ed6SAmy Griffis exit_err:
30393315ed6SAmy Griffis return ERR_PTR(err);
30493315ed6SAmy Griffis }
30593315ed6SAmy Griffis
3065af75d8dSAl Viro static u32 audit_ops[] =
3075af75d8dSAl Viro {
3085af75d8dSAl Viro [Audit_equal] = AUDIT_EQUAL,
3095af75d8dSAl Viro [Audit_not_equal] = AUDIT_NOT_EQUAL,
3105af75d8dSAl Viro [Audit_bitmask] = AUDIT_BIT_MASK,
3115af75d8dSAl Viro [Audit_bittest] = AUDIT_BIT_TEST,
3125af75d8dSAl Viro [Audit_lt] = AUDIT_LESS_THAN,
3135af75d8dSAl Viro [Audit_gt] = AUDIT_GREATER_THAN,
3145af75d8dSAl Viro [Audit_le] = AUDIT_LESS_THAN_OR_EQUAL,
3155af75d8dSAl Viro [Audit_ge] = AUDIT_GREATER_THAN_OR_EQUAL,
3165af75d8dSAl Viro };
3175af75d8dSAl Viro
audit_to_op(u32 op)3185af75d8dSAl Viro static u32 audit_to_op(u32 op)
3195af75d8dSAl Viro {
3205af75d8dSAl Viro u32 n;
3215af75d8dSAl Viro for (n = Audit_equal; n < Audit_bad && audit_ops[n] != op; n++)
3225af75d8dSAl Viro ;
3235af75d8dSAl Viro return n;
3245af75d8dSAl Viro }
3255af75d8dSAl Viro
326ab61d38eSEric Paris /* check if an audit field is valid */
audit_field_valid(struct audit_entry * entry,struct audit_field * f)32762062cf8SEric Paris static int audit_field_valid(struct audit_entry *entry, struct audit_field *f)
32893315ed6SAmy Griffis {
32962062cf8SEric Paris switch (f->type) {
33062062cf8SEric Paris case AUDIT_MSGTYPE:
331d904ac03SRichard Guy Briggs if (entry->rule.listnr != AUDIT_FILTER_EXCLUDE &&
33262062cf8SEric Paris entry->rule.listnr != AUDIT_FILTER_USER)
33362062cf8SEric Paris return -EINVAL;
33462062cf8SEric Paris break;
33542d5e376SRichard Guy Briggs case AUDIT_FSTYPE:
33642d5e376SRichard Guy Briggs if (entry->rule.listnr != AUDIT_FILTER_FS)
33742d5e376SRichard Guy Briggs return -EINVAL;
33842d5e376SRichard Guy Briggs break;
33967daf270SPaul Moore case AUDIT_PERM:
34067daf270SPaul Moore if (entry->rule.listnr == AUDIT_FILTER_URING_EXIT)
34167daf270SPaul Moore return -EINVAL;
34267daf270SPaul Moore break;
34342d5e376SRichard Guy Briggs }
34442d5e376SRichard Guy Briggs
34542d5e376SRichard Guy Briggs switch (entry->rule.listnr) {
34642d5e376SRichard Guy Briggs case AUDIT_FILTER_FS:
34742d5e376SRichard Guy Briggs switch (f->type) {
34842d5e376SRichard Guy Briggs case AUDIT_FSTYPE:
34942d5e376SRichard Guy Briggs case AUDIT_FILTERKEY:
35042d5e376SRichard Guy Briggs break;
35142d5e376SRichard Guy Briggs default:
35242d5e376SRichard Guy Briggs return -EINVAL;
35342d5e376SRichard Guy Briggs }
354b7a84deaSNicholas Mc Guire }
3555af75d8dSAl Viro
356ecc68904SRichard Guy Briggs /* Check for valid field type and op */
357f368c07dSAmy Griffis switch (f->type) {
358ecc68904SRichard Guy Briggs case AUDIT_ARG0:
359ecc68904SRichard Guy Briggs case AUDIT_ARG1:
360ecc68904SRichard Guy Briggs case AUDIT_ARG2:
361ecc68904SRichard Guy Briggs case AUDIT_ARG3:
362ecc68904SRichard Guy Briggs case AUDIT_PERS: /* <uapi/linux/personality.h> */
363ecc68904SRichard Guy Briggs case AUDIT_DEVMINOR:
364ecc68904SRichard Guy Briggs /* all ops are valid */
365ecc68904SRichard Guy Briggs break;
3660a73dcccSAl Viro case AUDIT_UID:
3670a73dcccSAl Viro case AUDIT_EUID:
3680a73dcccSAl Viro case AUDIT_SUID:
3690a73dcccSAl Viro case AUDIT_FSUID:
370ca57ec0fSEric W. Biederman case AUDIT_LOGINUID:
371ab61d38eSEric Paris case AUDIT_OBJ_UID:
3720a73dcccSAl Viro case AUDIT_GID:
3730a73dcccSAl Viro case AUDIT_EGID:
3740a73dcccSAl Viro case AUDIT_SGID:
3750a73dcccSAl Viro case AUDIT_FSGID:
376ab61d38eSEric Paris case AUDIT_OBJ_GID:
377ca57ec0fSEric W. Biederman case AUDIT_PID:
3780a73dcccSAl Viro case AUDIT_MSGTYPE:
3793b33ac31SSteve Grubb case AUDIT_PPID:
3800a73dcccSAl Viro case AUDIT_DEVMAJOR:
3810a73dcccSAl Viro case AUDIT_EXIT:
3820a73dcccSAl Viro case AUDIT_SUCCESS:
38378122037SEric Paris case AUDIT_INODE:
3848fae4770SRichard Guy Briggs case AUDIT_SESSIONID:
385ecc68904SRichard Guy Briggs case AUDIT_SUBJ_SEN:
386ecc68904SRichard Guy Briggs case AUDIT_SUBJ_CLR:
387ecc68904SRichard Guy Briggs case AUDIT_OBJ_LEV_LOW:
388ecc68904SRichard Guy Briggs case AUDIT_OBJ_LEV_HIGH:
389bf361231SRichard Guy Briggs case AUDIT_SADDR_FAM:
39074f2345bSEric Paris /* bit ops are only useful on syscall args */
3915af75d8dSAl Viro if (f->op == Audit_bitmask || f->op == Audit_bittest)
392ab61d38eSEric Paris return -EINVAL;
39374f2345bSEric Paris break;
394ab61d38eSEric Paris case AUDIT_SUBJ_USER:
395ab61d38eSEric Paris case AUDIT_SUBJ_ROLE:
396ab61d38eSEric Paris case AUDIT_SUBJ_TYPE:
397ab61d38eSEric Paris case AUDIT_OBJ_USER:
398ab61d38eSEric Paris case AUDIT_OBJ_ROLE:
399ab61d38eSEric Paris case AUDIT_OBJ_TYPE:
400ab61d38eSEric Paris case AUDIT_WATCH:
401ab61d38eSEric Paris case AUDIT_DIR:
402ab61d38eSEric Paris case AUDIT_FILTERKEY:
403780a7654SEric W. Biederman case AUDIT_LOGINUID_SET:
4044b8a311bSEric Paris case AUDIT_ARCH:
40542d5e376SRichard Guy Briggs case AUDIT_FSTYPE:
406ecc68904SRichard Guy Briggs case AUDIT_PERM:
407ecc68904SRichard Guy Briggs case AUDIT_FILETYPE:
408ecc68904SRichard Guy Briggs case AUDIT_FIELD_COMPARE:
409ecc68904SRichard Guy Briggs case AUDIT_EXE:
410ecc68904SRichard Guy Briggs /* only equal and not equal valid ops */
4115af75d8dSAl Viro if (f->op != Audit_not_equal && f->op != Audit_equal)
412ab61d38eSEric Paris return -EINVAL;
4134b8a311bSEric Paris break;
414ecc68904SRichard Guy Briggs default:
415ecc68904SRichard Guy Briggs /* field not recognized */
416ecc68904SRichard Guy Briggs return -EINVAL;
417ecc68904SRichard Guy Briggs }
418ecc68904SRichard Guy Briggs
419ecc68904SRichard Guy Briggs /* Check for select valid field values */
420ecc68904SRichard Guy Briggs switch (f->type) {
421ecc68904SRichard Guy Briggs case AUDIT_LOGINUID_SET:
422ecc68904SRichard Guy Briggs if ((f->val != 0) && (f->val != 1))
423ecc68904SRichard Guy Briggs return -EINVAL;
424ecc68904SRichard Guy Briggs break;
42555669bfaSAl Viro case AUDIT_PERM:
42655669bfaSAl Viro if (f->val & ~15)
427ab61d38eSEric Paris return -EINVAL;
42855669bfaSAl Viro break;
4298b67dca9SAl Viro case AUDIT_FILETYPE:
4305ef30ee5SEric Paris if (f->val & ~S_IFMT)
431ab61d38eSEric Paris return -EINVAL;
4328b67dca9SAl Viro break;
433ab61d38eSEric Paris case AUDIT_FIELD_COMPARE:
434ab61d38eSEric Paris if (f->val > AUDIT_MAX_FIELD_COMPARE)
435ab61d38eSEric Paris return -EINVAL;
436f368c07dSAmy Griffis break;
437bf361231SRichard Guy Briggs case AUDIT_SADDR_FAM:
438bf361231SRichard Guy Briggs if (f->val >= AF_MAX)
43934d99af5SRichard Guy Briggs return -EINVAL;
44034d99af5SRichard Guy Briggs break;
441ecc68904SRichard Guy Briggs default:
4423dc7e315SDarrel Goeddel break;
443b7a84deaSNicholas Mc Guire }
444ecc68904SRichard Guy Briggs
44562062cf8SEric Paris return 0;
44693315ed6SAmy Griffis }
44793315ed6SAmy Griffis
448fd97646bSWei Yuan /* Translate struct audit_rule_data to kernel's rule representation. */
audit_data_to_entry(struct audit_rule_data * data,size_t datasz)44993315ed6SAmy Griffis static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
45093315ed6SAmy Griffis size_t datasz)
45193315ed6SAmy Griffis {
45293315ed6SAmy Griffis int err = 0;
45393315ed6SAmy Griffis struct audit_entry *entry;
45493315ed6SAmy Griffis void *bufp;
4553dc7e315SDarrel Goeddel size_t remain = datasz - sizeof(struct audit_rule_data);
45693315ed6SAmy Griffis int i;
4573dc7e315SDarrel Goeddel char *str;
45834d99af5SRichard Guy Briggs struct audit_fsnotify_mark *audit_mark;
45993315ed6SAmy Griffis
46056c4911aSEric Paris entry = audit_to_entry_common(data);
46193315ed6SAmy Griffis if (IS_ERR(entry))
46293315ed6SAmy Griffis goto exit_nofree;
46393315ed6SAmy Griffis
46493315ed6SAmy Griffis bufp = data->buf;
46593315ed6SAmy Griffis for (i = 0; i < data->field_count; i++) {
46693315ed6SAmy Griffis struct audit_field *f = &entry->rule.fields[i];
4672ad3e17eSPaul Moore u32 f_val;
46893315ed6SAmy Griffis
46993315ed6SAmy Griffis err = -EINVAL;
4705af75d8dSAl Viro
4715af75d8dSAl Viro f->op = audit_to_op(data->fieldflags[i]);
4725af75d8dSAl Viro if (f->op == Audit_bad)
47393315ed6SAmy Griffis goto exit_free;
47493315ed6SAmy Griffis
47593315ed6SAmy Griffis f->type = data->fields[i];
4762ad3e17eSPaul Moore f_val = data->values[i];
47762062cf8SEric Paris
478780a7654SEric W. Biederman /* Support legacy tests for a valid loginuid */
4792ad3e17eSPaul Moore if ((f->type == AUDIT_LOGINUID) && (f_val == AUDIT_UID_UNSET)) {
480780a7654SEric W. Biederman f->type = AUDIT_LOGINUID_SET;
4812ad3e17eSPaul Moore f_val = 0;
482041d7b98SRichard Guy Briggs entry->rule.pflags |= AUDIT_LOGINUID_LEGACY;
483f1dc4867SRichard Guy Briggs }
484f1dc4867SRichard Guy Briggs
48562062cf8SEric Paris err = audit_field_valid(entry, f);
48662062cf8SEric Paris if (err)
48762062cf8SEric Paris goto exit_free;
48862062cf8SEric Paris
48962062cf8SEric Paris err = -EINVAL;
4903dc7e315SDarrel Goeddel switch (f->type) {
491780a7654SEric W. Biederman case AUDIT_LOGINUID:
4920a73dcccSAl Viro case AUDIT_UID:
4930a73dcccSAl Viro case AUDIT_EUID:
4940a73dcccSAl Viro case AUDIT_SUID:
4950a73dcccSAl Viro case AUDIT_FSUID:
496ca57ec0fSEric W. Biederman case AUDIT_OBJ_UID:
4972ad3e17eSPaul Moore f->uid = make_kuid(current_user_ns(), f_val);
498ca57ec0fSEric W. Biederman if (!uid_valid(f->uid))
499ca57ec0fSEric W. Biederman goto exit_free;
500ca57ec0fSEric W. Biederman break;
5010a73dcccSAl Viro case AUDIT_GID:
5020a73dcccSAl Viro case AUDIT_EGID:
5030a73dcccSAl Viro case AUDIT_SGID:
5040a73dcccSAl Viro case AUDIT_FSGID:
505ca57ec0fSEric W. Biederman case AUDIT_OBJ_GID:
5062ad3e17eSPaul Moore f->gid = make_kgid(current_user_ns(), f_val);
507ca57ec0fSEric W. Biederman if (!gid_valid(f->gid))
508ca57ec0fSEric W. Biederman goto exit_free;
509ca57ec0fSEric W. Biederman break;
510e54dc243SAmy Griffis case AUDIT_ARCH:
5112ad3e17eSPaul Moore f->val = f_val;
512e54dc243SAmy Griffis entry->rule.arch_f = f;
513e54dc243SAmy Griffis break;
5143a6b9f85SDarrel Goeddel case AUDIT_SUBJ_USER:
5153a6b9f85SDarrel Goeddel case AUDIT_SUBJ_ROLE:
5163a6b9f85SDarrel Goeddel case AUDIT_SUBJ_TYPE:
5173a6b9f85SDarrel Goeddel case AUDIT_SUBJ_SEN:
5183a6b9f85SDarrel Goeddel case AUDIT_SUBJ_CLR:
5196e5a2d1dSDarrel Goeddel case AUDIT_OBJ_USER:
5206e5a2d1dSDarrel Goeddel case AUDIT_OBJ_ROLE:
5216e5a2d1dSDarrel Goeddel case AUDIT_OBJ_TYPE:
5226e5a2d1dSDarrel Goeddel case AUDIT_OBJ_LEV_LOW:
5236e5a2d1dSDarrel Goeddel case AUDIT_OBJ_LEV_HIGH:
5242ad3e17eSPaul Moore str = audit_unpack_string(&bufp, &remain, f_val);
5252ad3e17eSPaul Moore if (IS_ERR(str)) {
5262ad3e17eSPaul Moore err = PTR_ERR(str);
5273dc7e315SDarrel Goeddel goto exit_free;
5282ad3e17eSPaul Moore }
5292ad3e17eSPaul Moore entry->rule.buflen += f_val;
5302ad3e17eSPaul Moore f->lsm_str = str;
531d7a96f3aSAhmed S. Darwish err = security_audit_rule_init(f->type, f->op, str,
532*28d0ecc5SGUO Zihua (void **)&f->lsm_rule,
533*28d0ecc5SGUO Zihua GFP_KERNEL);
5343dc7e315SDarrel Goeddel /* Keep currently invalid fields around in case they
5353dc7e315SDarrel Goeddel * become valid after a policy reload. */
5363dc7e315SDarrel Goeddel if (err == -EINVAL) {
537f952d10fSRichard Guy Briggs pr_warn("audit rule for LSM \'%s\' is invalid\n",
538f952d10fSRichard Guy Briggs str);
5393dc7e315SDarrel Goeddel err = 0;
5402ad3e17eSPaul Moore } else if (err)
5413dc7e315SDarrel Goeddel goto exit_free;
5423dc7e315SDarrel Goeddel break;
543f368c07dSAmy Griffis case AUDIT_WATCH:
5442ad3e17eSPaul Moore str = audit_unpack_string(&bufp, &remain, f_val);
5452ad3e17eSPaul Moore if (IS_ERR(str)) {
5462ad3e17eSPaul Moore err = PTR_ERR(str);
547f368c07dSAmy Griffis goto exit_free;
5482ad3e17eSPaul Moore }
5492ad3e17eSPaul Moore err = audit_to_watch(&entry->rule, str, f_val, f->op);
550f368c07dSAmy Griffis if (err) {
551f368c07dSAmy Griffis kfree(str);
552f368c07dSAmy Griffis goto exit_free;
553f368c07dSAmy Griffis }
5542ad3e17eSPaul Moore entry->rule.buflen += f_val;
555f368c07dSAmy Griffis break;
55674c3cbe3SAl Viro case AUDIT_DIR:
5572ad3e17eSPaul Moore str = audit_unpack_string(&bufp, &remain, f_val);
5582ad3e17eSPaul Moore if (IS_ERR(str)) {
5592ad3e17eSPaul Moore err = PTR_ERR(str);
56074c3cbe3SAl Viro goto exit_free;
5612ad3e17eSPaul Moore }
56274c3cbe3SAl Viro err = audit_make_tree(&entry->rule, str, f->op);
56374c3cbe3SAl Viro kfree(str);
56474c3cbe3SAl Viro if (err)
56574c3cbe3SAl Viro goto exit_free;
5662ad3e17eSPaul Moore entry->rule.buflen += f_val;
56774c3cbe3SAl Viro break;
568f368c07dSAmy Griffis case AUDIT_INODE:
5692ad3e17eSPaul Moore f->val = f_val;
570f368c07dSAmy Griffis err = audit_to_inode(&entry->rule, f);
571f368c07dSAmy Griffis if (err)
572f368c07dSAmy Griffis goto exit_free;
573f368c07dSAmy Griffis break;
5745adc8a6aSAmy Griffis case AUDIT_FILTERKEY:
5752ad3e17eSPaul Moore if (entry->rule.filterkey || f_val > AUDIT_MAX_KEY_LEN)
5765adc8a6aSAmy Griffis goto exit_free;
5772ad3e17eSPaul Moore str = audit_unpack_string(&bufp, &remain, f_val);
57834d99af5SRichard Guy Briggs if (IS_ERR(str)) {
57934d99af5SRichard Guy Briggs err = PTR_ERR(str);
58034d99af5SRichard Guy Briggs goto exit_free;
58134d99af5SRichard Guy Briggs }
5822ad3e17eSPaul Moore entry->rule.buflen += f_val;
5832ad3e17eSPaul Moore entry->rule.filterkey = str;
5842ad3e17eSPaul Moore break;
5852ad3e17eSPaul Moore case AUDIT_EXE:
5862ad3e17eSPaul Moore if (entry->rule.exe || f_val > PATH_MAX)
5872ad3e17eSPaul Moore goto exit_free;
5882ad3e17eSPaul Moore str = audit_unpack_string(&bufp, &remain, f_val);
5892ad3e17eSPaul Moore if (IS_ERR(str)) {
5902ad3e17eSPaul Moore err = PTR_ERR(str);
5912ad3e17eSPaul Moore goto exit_free;
5922ad3e17eSPaul Moore }
5932ad3e17eSPaul Moore audit_mark = audit_alloc_mark(&entry->rule, str, f_val);
59434d99af5SRichard Guy Briggs if (IS_ERR(audit_mark)) {
59534d99af5SRichard Guy Briggs kfree(str);
59634d99af5SRichard Guy Briggs err = PTR_ERR(audit_mark);
59734d99af5SRichard Guy Briggs goto exit_free;
59834d99af5SRichard Guy Briggs }
5992ad3e17eSPaul Moore entry->rule.buflen += f_val;
60034d99af5SRichard Guy Briggs entry->rule.exe = audit_mark;
60134d99af5SRichard Guy Briggs break;
6022ad3e17eSPaul Moore default:
6032ad3e17eSPaul Moore f->val = f_val;
6042ad3e17eSPaul Moore break;
605f368c07dSAmy Griffis }
606f368c07dSAmy Griffis }
607f368c07dSAmy Griffis
6085af75d8dSAl Viro if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal)
609f368c07dSAmy Griffis entry->rule.inode_f = NULL;
61093315ed6SAmy Griffis
61193315ed6SAmy Griffis exit_nofree:
61293315ed6SAmy Griffis return entry;
61393315ed6SAmy Griffis
61493315ed6SAmy Griffis exit_free:
615373e0f34SChen Gang if (entry->rule.tree)
616373e0f34SChen Gang audit_put_tree(entry->rule.tree); /* that's the temporary one */
61734d99af5SRichard Guy Briggs if (entry->rule.exe)
61834d99af5SRichard Guy Briggs audit_remove_mark(entry->rule.exe); /* that's the template one */
61993315ed6SAmy Griffis audit_free_rule(entry);
62093315ed6SAmy Griffis return ERR_PTR(err);
62193315ed6SAmy Griffis }
62293315ed6SAmy Griffis
62393315ed6SAmy Griffis /* Pack a filter field's string representation into data block. */
audit_pack_string(void ** bufp,const char * str)62474c3cbe3SAl Viro static inline size_t audit_pack_string(void **bufp, const char *str)
62593315ed6SAmy Griffis {
62693315ed6SAmy Griffis size_t len = strlen(str);
62793315ed6SAmy Griffis
62893315ed6SAmy Griffis memcpy(*bufp, str, len);
62993315ed6SAmy Griffis *bufp += len;
63093315ed6SAmy Griffis
63193315ed6SAmy Griffis return len;
63293315ed6SAmy Griffis }
63393315ed6SAmy Griffis
634fd97646bSWei Yuan /* Translate kernel rule representation to struct audit_rule_data. */
audit_krule_to_data(struct audit_krule * krule)63593315ed6SAmy Griffis static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
63693315ed6SAmy Griffis {
63793315ed6SAmy Griffis struct audit_rule_data *data;
63893315ed6SAmy Griffis void *bufp;
63993315ed6SAmy Griffis int i;
64093315ed6SAmy Griffis
641bc6e60a4SXiu Jianfeng data = kmalloc(struct_size(data, buf, krule->buflen), GFP_KERNEL);
64293315ed6SAmy Griffis if (unlikely(!data))
6430a3b483eSAmy Griffis return NULL;
64493315ed6SAmy Griffis memset(data, 0, sizeof(*data));
64593315ed6SAmy Griffis
64693315ed6SAmy Griffis data->flags = krule->flags | krule->listnr;
64793315ed6SAmy Griffis data->action = krule->action;
64893315ed6SAmy Griffis data->field_count = krule->field_count;
64993315ed6SAmy Griffis bufp = data->buf;
65093315ed6SAmy Griffis for (i = 0; i < data->field_count; i++) {
65193315ed6SAmy Griffis struct audit_field *f = &krule->fields[i];
65293315ed6SAmy Griffis
65393315ed6SAmy Griffis data->fields[i] = f->type;
6545af75d8dSAl Viro data->fieldflags[i] = audit_ops[f->op];
65593315ed6SAmy Griffis switch (f->type) {
6563a6b9f85SDarrel Goeddel case AUDIT_SUBJ_USER:
6573a6b9f85SDarrel Goeddel case AUDIT_SUBJ_ROLE:
6583a6b9f85SDarrel Goeddel case AUDIT_SUBJ_TYPE:
6593a6b9f85SDarrel Goeddel case AUDIT_SUBJ_SEN:
6603a6b9f85SDarrel Goeddel case AUDIT_SUBJ_CLR:
6616e5a2d1dSDarrel Goeddel case AUDIT_OBJ_USER:
6626e5a2d1dSDarrel Goeddel case AUDIT_OBJ_ROLE:
6636e5a2d1dSDarrel Goeddel case AUDIT_OBJ_TYPE:
6646e5a2d1dSDarrel Goeddel case AUDIT_OBJ_LEV_LOW:
6656e5a2d1dSDarrel Goeddel case AUDIT_OBJ_LEV_HIGH:
6663dc7e315SDarrel Goeddel data->buflen += data->values[i] =
66704305e4aSAhmed S. Darwish audit_pack_string(&bufp, f->lsm_str);
6683dc7e315SDarrel Goeddel break;
669f368c07dSAmy Griffis case AUDIT_WATCH:
670f368c07dSAmy Griffis data->buflen += data->values[i] =
671cfcad62cSEric Paris audit_pack_string(&bufp,
672cfcad62cSEric Paris audit_watch_path(krule->watch));
673f368c07dSAmy Griffis break;
67474c3cbe3SAl Viro case AUDIT_DIR:
67574c3cbe3SAl Viro data->buflen += data->values[i] =
67674c3cbe3SAl Viro audit_pack_string(&bufp,
67774c3cbe3SAl Viro audit_tree_path(krule->tree));
67874c3cbe3SAl Viro break;
6795adc8a6aSAmy Griffis case AUDIT_FILTERKEY:
6805adc8a6aSAmy Griffis data->buflen += data->values[i] =
6815adc8a6aSAmy Griffis audit_pack_string(&bufp, krule->filterkey);
6825adc8a6aSAmy Griffis break;
68334d99af5SRichard Guy Briggs case AUDIT_EXE:
68434d99af5SRichard Guy Briggs data->buflen += data->values[i] =
68534d99af5SRichard Guy Briggs audit_pack_string(&bufp, audit_mark_path(krule->exe));
68634d99af5SRichard Guy Briggs break;
687041d7b98SRichard Guy Briggs case AUDIT_LOGINUID_SET:
688041d7b98SRichard Guy Briggs if (krule->pflags & AUDIT_LOGINUID_LEGACY && !f->val) {
689041d7b98SRichard Guy Briggs data->fields[i] = AUDIT_LOGINUID;
690041d7b98SRichard Guy Briggs data->values[i] = AUDIT_UID_UNSET;
691041d7b98SRichard Guy Briggs break;
692041d7b98SRichard Guy Briggs }
693df561f66SGustavo A. R. Silva fallthrough; /* if set */
69493315ed6SAmy Griffis default:
69593315ed6SAmy Griffis data->values[i] = f->val;
69693315ed6SAmy Griffis }
69793315ed6SAmy Griffis }
698b1a0f64cSAtul Kumar Pant for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
699b1a0f64cSAtul Kumar Pant data->mask[i] = krule->mask[i];
70093315ed6SAmy Griffis
70193315ed6SAmy Griffis return data;
70293315ed6SAmy Griffis }
70393315ed6SAmy Griffis
70493315ed6SAmy Griffis /* Compare two rules in kernel format. Considered success if rules
70593315ed6SAmy Griffis * don't match. */
audit_compare_rule(struct audit_krule * a,struct audit_krule * b)70693315ed6SAmy Griffis static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
707fe7752baSDavid Woodhouse {
708fe7752baSDavid Woodhouse int i;
709fe7752baSDavid Woodhouse
71093315ed6SAmy Griffis if (a->flags != b->flags ||
711041d7b98SRichard Guy Briggs a->pflags != b->pflags ||
71293315ed6SAmy Griffis a->listnr != b->listnr ||
71393315ed6SAmy Griffis a->action != b->action ||
71493315ed6SAmy Griffis a->field_count != b->field_count)
715fe7752baSDavid Woodhouse return 1;
716fe7752baSDavid Woodhouse
717fe7752baSDavid Woodhouse for (i = 0; i < a->field_count; i++) {
71893315ed6SAmy Griffis if (a->fields[i].type != b->fields[i].type ||
71993315ed6SAmy Griffis a->fields[i].op != b->fields[i].op)
720fe7752baSDavid Woodhouse return 1;
72193315ed6SAmy Griffis
72293315ed6SAmy Griffis switch (a->fields[i].type) {
7233a6b9f85SDarrel Goeddel case AUDIT_SUBJ_USER:
7243a6b9f85SDarrel Goeddel case AUDIT_SUBJ_ROLE:
7253a6b9f85SDarrel Goeddel case AUDIT_SUBJ_TYPE:
7263a6b9f85SDarrel Goeddel case AUDIT_SUBJ_SEN:
7273a6b9f85SDarrel Goeddel case AUDIT_SUBJ_CLR:
7286e5a2d1dSDarrel Goeddel case AUDIT_OBJ_USER:
7296e5a2d1dSDarrel Goeddel case AUDIT_OBJ_ROLE:
7306e5a2d1dSDarrel Goeddel case AUDIT_OBJ_TYPE:
7316e5a2d1dSDarrel Goeddel case AUDIT_OBJ_LEV_LOW:
7326e5a2d1dSDarrel Goeddel case AUDIT_OBJ_LEV_HIGH:
73304305e4aSAhmed S. Darwish if (strcmp(a->fields[i].lsm_str, b->fields[i].lsm_str))
7343dc7e315SDarrel Goeddel return 1;
7353dc7e315SDarrel Goeddel break;
736f368c07dSAmy Griffis case AUDIT_WATCH:
737cfcad62cSEric Paris if (strcmp(audit_watch_path(a->watch),
738cfcad62cSEric Paris audit_watch_path(b->watch)))
739f368c07dSAmy Griffis return 1;
740f368c07dSAmy Griffis break;
74174c3cbe3SAl Viro case AUDIT_DIR:
74274c3cbe3SAl Viro if (strcmp(audit_tree_path(a->tree),
74374c3cbe3SAl Viro audit_tree_path(b->tree)))
74474c3cbe3SAl Viro return 1;
74574c3cbe3SAl Viro break;
7465adc8a6aSAmy Griffis case AUDIT_FILTERKEY:
7475adc8a6aSAmy Griffis /* both filterkeys exist based on above type compare */
7485adc8a6aSAmy Griffis if (strcmp(a->filterkey, b->filterkey))
7495adc8a6aSAmy Griffis return 1;
7505adc8a6aSAmy Griffis break;
75134d99af5SRichard Guy Briggs case AUDIT_EXE:
75234d99af5SRichard Guy Briggs /* both paths exist based on above type compare */
75334d99af5SRichard Guy Briggs if (strcmp(audit_mark_path(a->exe),
75434d99af5SRichard Guy Briggs audit_mark_path(b->exe)))
75534d99af5SRichard Guy Briggs return 1;
75634d99af5SRichard Guy Briggs break;
757ca57ec0fSEric W. Biederman case AUDIT_UID:
758ca57ec0fSEric W. Biederman case AUDIT_EUID:
759ca57ec0fSEric W. Biederman case AUDIT_SUID:
760ca57ec0fSEric W. Biederman case AUDIT_FSUID:
761ca57ec0fSEric W. Biederman case AUDIT_LOGINUID:
762ca57ec0fSEric W. Biederman case AUDIT_OBJ_UID:
763ca57ec0fSEric W. Biederman if (!uid_eq(a->fields[i].uid, b->fields[i].uid))
764ca57ec0fSEric W. Biederman return 1;
765ca57ec0fSEric W. Biederman break;
766ca57ec0fSEric W. Biederman case AUDIT_GID:
767ca57ec0fSEric W. Biederman case AUDIT_EGID:
768ca57ec0fSEric W. Biederman case AUDIT_SGID:
769ca57ec0fSEric W. Biederman case AUDIT_FSGID:
770ca57ec0fSEric W. Biederman case AUDIT_OBJ_GID:
771ca57ec0fSEric W. Biederman if (!gid_eq(a->fields[i].gid, b->fields[i].gid))
772ca57ec0fSEric W. Biederman return 1;
773ca57ec0fSEric W. Biederman break;
77493315ed6SAmy Griffis default:
77593315ed6SAmy Griffis if (a->fields[i].val != b->fields[i].val)
77693315ed6SAmy Griffis return 1;
77793315ed6SAmy Griffis }
778fe7752baSDavid Woodhouse }
779fe7752baSDavid Woodhouse
780fe7752baSDavid Woodhouse for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
781fe7752baSDavid Woodhouse if (a->mask[i] != b->mask[i])
782fe7752baSDavid Woodhouse return 1;
783fe7752baSDavid Woodhouse
784fe7752baSDavid Woodhouse return 0;
785fe7752baSDavid Woodhouse }
786fe7752baSDavid Woodhouse
78704305e4aSAhmed S. Darwish /* Duplicate LSM field information. The lsm_rule is opaque, so must be
7883dc7e315SDarrel Goeddel * re-initialized. */
audit_dupe_lsm_field(struct audit_field * df,struct audit_field * sf)789d7a96f3aSAhmed S. Darwish static inline int audit_dupe_lsm_field(struct audit_field *df,
7903dc7e315SDarrel Goeddel struct audit_field *sf)
7913dc7e315SDarrel Goeddel {
7923dc7e315SDarrel Goeddel int ret = 0;
79304305e4aSAhmed S. Darwish char *lsm_str;
7943dc7e315SDarrel Goeddel
79504305e4aSAhmed S. Darwish /* our own copy of lsm_str */
79604305e4aSAhmed S. Darwish lsm_str = kstrdup(sf->lsm_str, GFP_KERNEL);
79704305e4aSAhmed S. Darwish if (unlikely(!lsm_str))
7983dc7e315SDarrel Goeddel return -ENOMEM;
79904305e4aSAhmed S. Darwish df->lsm_str = lsm_str;
8003dc7e315SDarrel Goeddel
80104305e4aSAhmed S. Darwish /* our own (refreshed) copy of lsm_rule */
80204305e4aSAhmed S. Darwish ret = security_audit_rule_init(df->type, df->op, df->lsm_str,
803*28d0ecc5SGUO Zihua (void **)&df->lsm_rule, GFP_KERNEL);
8043dc7e315SDarrel Goeddel /* Keep currently invalid fields around in case they
8053dc7e315SDarrel Goeddel * become valid after a policy reload. */
8063dc7e315SDarrel Goeddel if (ret == -EINVAL) {
807f952d10fSRichard Guy Briggs pr_warn("audit rule for LSM \'%s\' is invalid\n",
808f952d10fSRichard Guy Briggs df->lsm_str);
8093dc7e315SDarrel Goeddel ret = 0;
8103dc7e315SDarrel Goeddel }
8113dc7e315SDarrel Goeddel
8123dc7e315SDarrel Goeddel return ret;
8133dc7e315SDarrel Goeddel }
8143dc7e315SDarrel Goeddel
8153dc7e315SDarrel Goeddel /* Duplicate an audit rule. This will be a deep copy with the exception
816d7a96f3aSAhmed S. Darwish * of the watch - that pointer is carried over. The LSM specific fields
8173dc7e315SDarrel Goeddel * will be updated in the copy. The point is to be able to replace the old
818f368c07dSAmy Griffis * rule with the new rule in the filterlist, then free the old rule.
819f368c07dSAmy Griffis * The rlist element is undefined; list manipulations are handled apart from
820f368c07dSAmy Griffis * the initial copy. */
audit_dupe_rule(struct audit_krule * old)821ae7b8f41SEric Paris struct audit_entry *audit_dupe_rule(struct audit_krule *old)
8223dc7e315SDarrel Goeddel {
8233dc7e315SDarrel Goeddel u32 fcount = old->field_count;
8243dc7e315SDarrel Goeddel struct audit_entry *entry;
8253dc7e315SDarrel Goeddel struct audit_krule *new;
8265adc8a6aSAmy Griffis char *fk;
8273dc7e315SDarrel Goeddel int i, err = 0;
8283dc7e315SDarrel Goeddel
8293dc7e315SDarrel Goeddel entry = audit_init_entry(fcount);
8303dc7e315SDarrel Goeddel if (unlikely(!entry))
8313dc7e315SDarrel Goeddel return ERR_PTR(-ENOMEM);
8323dc7e315SDarrel Goeddel
8333dc7e315SDarrel Goeddel new = &entry->rule;
8343dc7e315SDarrel Goeddel new->flags = old->flags;
835041d7b98SRichard Guy Briggs new->pflags = old->pflags;
8363dc7e315SDarrel Goeddel new->listnr = old->listnr;
8373dc7e315SDarrel Goeddel new->action = old->action;
8383dc7e315SDarrel Goeddel for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
8393dc7e315SDarrel Goeddel new->mask[i] = old->mask[i];
8400590b933SAl Viro new->prio = old->prio;
8413dc7e315SDarrel Goeddel new->buflen = old->buflen;
842f368c07dSAmy Griffis new->inode_f = old->inode_f;
8433dc7e315SDarrel Goeddel new->field_count = old->field_count;
844ae7b8f41SEric Paris
84574c3cbe3SAl Viro /*
84674c3cbe3SAl Viro * note that we are OK with not refcounting here; audit_match_tree()
84774c3cbe3SAl Viro * never dereferences tree and we can't get false positives there
84874c3cbe3SAl Viro * since we'd have to have rule gone from the list *and* removed
84974c3cbe3SAl Viro * before the chunks found by lookup had been allocated, i.e. before
85074c3cbe3SAl Viro * the beginning of list scan.
85174c3cbe3SAl Viro */
85274c3cbe3SAl Viro new->tree = old->tree;
8533dc7e315SDarrel Goeddel memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount);
8543dc7e315SDarrel Goeddel
85504305e4aSAhmed S. Darwish /* deep copy this information, updating the lsm_rule fields, because
8563dc7e315SDarrel Goeddel * the originals will all be freed when the old rule is freed. */
8573dc7e315SDarrel Goeddel for (i = 0; i < fcount; i++) {
8583dc7e315SDarrel Goeddel switch (new->fields[i].type) {
8593a6b9f85SDarrel Goeddel case AUDIT_SUBJ_USER:
8603a6b9f85SDarrel Goeddel case AUDIT_SUBJ_ROLE:
8613a6b9f85SDarrel Goeddel case AUDIT_SUBJ_TYPE:
8623a6b9f85SDarrel Goeddel case AUDIT_SUBJ_SEN:
8633a6b9f85SDarrel Goeddel case AUDIT_SUBJ_CLR:
8646e5a2d1dSDarrel Goeddel case AUDIT_OBJ_USER:
8656e5a2d1dSDarrel Goeddel case AUDIT_OBJ_ROLE:
8666e5a2d1dSDarrel Goeddel case AUDIT_OBJ_TYPE:
8676e5a2d1dSDarrel Goeddel case AUDIT_OBJ_LEV_LOW:
8686e5a2d1dSDarrel Goeddel case AUDIT_OBJ_LEV_HIGH:
869d7a96f3aSAhmed S. Darwish err = audit_dupe_lsm_field(&new->fields[i],
8703dc7e315SDarrel Goeddel &old->fields[i]);
8715adc8a6aSAmy Griffis break;
8725adc8a6aSAmy Griffis case AUDIT_FILTERKEY:
8735adc8a6aSAmy Griffis fk = kstrdup(old->filterkey, GFP_KERNEL);
8745adc8a6aSAmy Griffis if (unlikely(!fk))
8755adc8a6aSAmy Griffis err = -ENOMEM;
8765adc8a6aSAmy Griffis else
8775adc8a6aSAmy Griffis new->filterkey = fk;
87834d99af5SRichard Guy Briggs break;
87934d99af5SRichard Guy Briggs case AUDIT_EXE:
88034d99af5SRichard Guy Briggs err = audit_dupe_exe(new, old);
88134d99af5SRichard Guy Briggs break;
8823dc7e315SDarrel Goeddel }
8833dc7e315SDarrel Goeddel if (err) {
88434d99af5SRichard Guy Briggs if (new->exe)
88534d99af5SRichard Guy Briggs audit_remove_mark(new->exe);
8863dc7e315SDarrel Goeddel audit_free_rule(entry);
8873dc7e315SDarrel Goeddel return ERR_PTR(err);
8883dc7e315SDarrel Goeddel }
8893dc7e315SDarrel Goeddel }
8903dc7e315SDarrel Goeddel
891ae7b8f41SEric Paris if (old->watch) {
892ae7b8f41SEric Paris audit_get_watch(old->watch);
893ae7b8f41SEric Paris new->watch = old->watch;
894f368c07dSAmy Griffis }
895f368c07dSAmy Griffis
8963dc7e315SDarrel Goeddel return entry;
8973dc7e315SDarrel Goeddel }
8983dc7e315SDarrel Goeddel
899f368c07dSAmy Griffis /* Find an existing audit rule.
900f368c07dSAmy Griffis * Caller must hold audit_filter_mutex to prevent stale rule data. */
audit_find_rule(struct audit_entry * entry,struct list_head ** p)901f368c07dSAmy Griffis static struct audit_entry *audit_find_rule(struct audit_entry *entry,
90236c4f1b1SAl Viro struct list_head **p)
903f368c07dSAmy Griffis {
904f368c07dSAmy Griffis struct audit_entry *e, *found = NULL;
90536c4f1b1SAl Viro struct list_head *list;
906f368c07dSAmy Griffis int h;
907f368c07dSAmy Griffis
90836c4f1b1SAl Viro if (entry->rule.inode_f) {
90936c4f1b1SAl Viro h = audit_hash_ino(entry->rule.inode_f->val);
91036c4f1b1SAl Viro *p = list = &audit_inode_hash[h];
91136c4f1b1SAl Viro } else if (entry->rule.watch) {
912f368c07dSAmy Griffis /* we don't know the inode number, so must walk entire hash */
913f368c07dSAmy Griffis for (h = 0; h < AUDIT_INODE_BUCKETS; h++) {
914f368c07dSAmy Griffis list = &audit_inode_hash[h];
915f368c07dSAmy Griffis list_for_each_entry(e, list, list)
916f368c07dSAmy Griffis if (!audit_compare_rule(&entry->rule, &e->rule)) {
917f368c07dSAmy Griffis found = e;
918f368c07dSAmy Griffis goto out;
919f368c07dSAmy Griffis }
920f368c07dSAmy Griffis }
921f368c07dSAmy Griffis goto out;
92236c4f1b1SAl Viro } else {
92336c4f1b1SAl Viro *p = list = &audit_filter_list[entry->rule.listnr];
924f368c07dSAmy Griffis }
925f368c07dSAmy Griffis
926f368c07dSAmy Griffis list_for_each_entry(e, list, list)
927f368c07dSAmy Griffis if (!audit_compare_rule(&entry->rule, &e->rule)) {
928f368c07dSAmy Griffis found = e;
929f368c07dSAmy Griffis goto out;
930f368c07dSAmy Griffis }
931f368c07dSAmy Griffis
932f368c07dSAmy Griffis out:
933f368c07dSAmy Griffis return found;
934f368c07dSAmy Griffis }
935f368c07dSAmy Griffis
9360590b933SAl Viro static u64 prio_low = ~0ULL/2;
9370590b933SAl Viro static u64 prio_high = ~0ULL/2 - 1;
9380590b933SAl Viro
939f368c07dSAmy Griffis /* Add rule to given filterlist if not a duplicate. */
audit_add_rule(struct audit_entry * entry)94036c4f1b1SAl Viro static inline int audit_add_rule(struct audit_entry *entry)
941fe7752baSDavid Woodhouse {
94293315ed6SAmy Griffis struct audit_entry *e;
943f368c07dSAmy Griffis struct audit_watch *watch = entry->rule.watch;
94474c3cbe3SAl Viro struct audit_tree *tree = entry->rule.tree;
94536c4f1b1SAl Viro struct list_head *list;
946ae9d2fb4SPaul Moore int err = 0;
947471a5c7cSAl Viro #ifdef CONFIG_AUDITSYSCALL
948471a5c7cSAl Viro int dont_count = 0;
949471a5c7cSAl Viro
95042d5e376SRichard Guy Briggs /* If any of these, don't count towards total */
95142d5e376SRichard Guy Briggs switch (entry->rule.listnr) {
95242d5e376SRichard Guy Briggs case AUDIT_FILTER_USER:
953d904ac03SRichard Guy Briggs case AUDIT_FILTER_EXCLUDE:
95442d5e376SRichard Guy Briggs case AUDIT_FILTER_FS:
955471a5c7cSAl Viro dont_count = 1;
95642d5e376SRichard Guy Briggs }
957471a5c7cSAl Viro #endif
958fe7752baSDavid Woodhouse
959f368c07dSAmy Griffis mutex_lock(&audit_filter_mutex);
96036c4f1b1SAl Viro e = audit_find_rule(entry, &list);
961f368c07dSAmy Griffis if (e) {
96235fe4d0bSEric Paris mutex_unlock(&audit_filter_mutex);
963f368c07dSAmy Griffis err = -EEXIST;
96474c3cbe3SAl Viro /* normally audit_add_tree_rule() will free it on failure */
96574c3cbe3SAl Viro if (tree)
96674c3cbe3SAl Viro audit_put_tree(tree);
967f8259b26SRichard Guy Briggs return err;
968f368c07dSAmy Griffis }
969f368c07dSAmy Griffis
970f368c07dSAmy Griffis if (watch) {
971f368c07dSAmy Griffis /* audit_filter_mutex is dropped and re-taken during this call */
972ae7b8f41SEric Paris err = audit_add_watch(&entry->rule, &list);
973f368c07dSAmy Griffis if (err) {
974f368c07dSAmy Griffis mutex_unlock(&audit_filter_mutex);
9752f992ee8SChen Gang /*
9762f992ee8SChen Gang * normally audit_add_tree_rule() will free it
9772f992ee8SChen Gang * on failure
9782f992ee8SChen Gang */
9792f992ee8SChen Gang if (tree)
9802f992ee8SChen Gang audit_put_tree(tree);
981f8259b26SRichard Guy Briggs return err;
982f368c07dSAmy Griffis }
983fe7752baSDavid Woodhouse }
98474c3cbe3SAl Viro if (tree) {
98574c3cbe3SAl Viro err = audit_add_tree_rule(&entry->rule);
98674c3cbe3SAl Viro if (err) {
98774c3cbe3SAl Viro mutex_unlock(&audit_filter_mutex);
988f8259b26SRichard Guy Briggs return err;
98974c3cbe3SAl Viro }
99074c3cbe3SAl Viro }
991fe7752baSDavid Woodhouse
9920590b933SAl Viro entry->rule.prio = ~0ULL;
99367daf270SPaul Moore if (entry->rule.listnr == AUDIT_FILTER_EXIT ||
99467daf270SPaul Moore entry->rule.listnr == AUDIT_FILTER_URING_EXIT) {
9950590b933SAl Viro if (entry->rule.flags & AUDIT_FILTER_PREPEND)
9960590b933SAl Viro entry->rule.prio = ++prio_high;
9970590b933SAl Viro else
9980590b933SAl Viro entry->rule.prio = --prio_low;
9990590b933SAl Viro }
10000590b933SAl Viro
1001fe7752baSDavid Woodhouse if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
1002e45aa212SAl Viro list_add(&entry->rule.list,
1003e45aa212SAl Viro &audit_rules_list[entry->rule.listnr]);
1004fe7752baSDavid Woodhouse list_add_rcu(&entry->list, list);
10056a2bceecSAmy Griffis entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
1006fe7752baSDavid Woodhouse } else {
1007e45aa212SAl Viro list_add_tail(&entry->rule.list,
1008e45aa212SAl Viro &audit_rules_list[entry->rule.listnr]);
1009fe7752baSDavid Woodhouse list_add_tail_rcu(&entry->list, list);
1010fe7752baSDavid Woodhouse }
1011471a5c7cSAl Viro #ifdef CONFIG_AUDITSYSCALL
1012471a5c7cSAl Viro if (!dont_count)
1013471a5c7cSAl Viro audit_n_rules++;
1014e54dc243SAmy Griffis
1015e54dc243SAmy Griffis if (!audit_match_signal(entry))
1016e54dc243SAmy Griffis audit_signals++;
1017471a5c7cSAl Viro #endif
1018f368c07dSAmy Griffis mutex_unlock(&audit_filter_mutex);
1019f368c07dSAmy Griffis
1020f368c07dSAmy Griffis return err;
1021fe7752baSDavid Woodhouse }
1022fe7752baSDavid Woodhouse
1023f368c07dSAmy Griffis /* Remove an existing rule from filterlist. */
audit_del_rule(struct audit_entry * entry)10247f492942SRichard Guy Briggs int audit_del_rule(struct audit_entry *entry)
1025fe7752baSDavid Woodhouse {
1026fe7752baSDavid Woodhouse struct audit_entry *e;
102774c3cbe3SAl Viro struct audit_tree *tree = entry->rule.tree;
102836c4f1b1SAl Viro struct list_head *list;
102936c4f1b1SAl Viro int ret = 0;
1030471a5c7cSAl Viro #ifdef CONFIG_AUDITSYSCALL
1031471a5c7cSAl Viro int dont_count = 0;
1032471a5c7cSAl Viro
103342d5e376SRichard Guy Briggs /* If any of these, don't count towards total */
103442d5e376SRichard Guy Briggs switch (entry->rule.listnr) {
103542d5e376SRichard Guy Briggs case AUDIT_FILTER_USER:
1036d904ac03SRichard Guy Briggs case AUDIT_FILTER_EXCLUDE:
103742d5e376SRichard Guy Briggs case AUDIT_FILTER_FS:
1038471a5c7cSAl Viro dont_count = 1;
103942d5e376SRichard Guy Briggs }
1040471a5c7cSAl Viro #endif
1041fe7752baSDavid Woodhouse
1042f368c07dSAmy Griffis mutex_lock(&audit_filter_mutex);
104336c4f1b1SAl Viro e = audit_find_rule(entry, &list);
1044f368c07dSAmy Griffis if (!e) {
1045f368c07dSAmy Griffis ret = -ENOENT;
1046f368c07dSAmy Griffis goto out;
1047f368c07dSAmy Griffis }
1048f368c07dSAmy Griffis
1049cfcad62cSEric Paris if (e->rule.watch)
1050a05fb6ccSEric Paris audit_remove_watch_rule(&e->rule);
1051f368c07dSAmy Griffis
105274c3cbe3SAl Viro if (e->rule.tree)
105374c3cbe3SAl Viro audit_remove_tree_rule(&e->rule);
105474c3cbe3SAl Viro
105534d99af5SRichard Guy Briggs if (e->rule.exe)
105634d99af5SRichard Guy Briggs audit_remove_mark_rule(&e->rule);
105734d99af5SRichard Guy Briggs
1058471a5c7cSAl Viro #ifdef CONFIG_AUDITSYSCALL
1059471a5c7cSAl Viro if (!dont_count)
1060471a5c7cSAl Viro audit_n_rules--;
1061e54dc243SAmy Griffis
1062e54dc243SAmy Griffis if (!audit_match_signal(entry))
1063e54dc243SAmy Griffis audit_signals--;
1064471a5c7cSAl Viro #endif
10658c85fc9aSRichard Guy Briggs
10668c85fc9aSRichard Guy Briggs list_del_rcu(&e->list);
10678c85fc9aSRichard Guy Briggs list_del(&e->rule.list);
10688c85fc9aSRichard Guy Briggs call_rcu(&e->rcu, audit_free_rule_rcu);
1069f368c07dSAmy Griffis
1070f368c07dSAmy Griffis out:
10718c85fc9aSRichard Guy Briggs mutex_unlock(&audit_filter_mutex);
10728c85fc9aSRichard Guy Briggs
107374c3cbe3SAl Viro if (tree)
107474c3cbe3SAl Viro audit_put_tree(tree); /* that's the temporary one */
1075f368c07dSAmy Griffis
1076f368c07dSAmy Griffis return ret;
1077fe7752baSDavid Woodhouse }
1078fe7752baSDavid Woodhouse
107993315ed6SAmy Griffis /* List rules using struct audit_rule_data. */
audit_list_rules(int seq,struct sk_buff_head * q)108045a0642bSPaul Moore static void audit_list_rules(int seq, struct sk_buff_head *q)
108193315ed6SAmy Griffis {
10829044e6bcSAl Viro struct sk_buff *skb;
1083e45aa212SAl Viro struct audit_krule *r;
108493315ed6SAmy Griffis int i;
108593315ed6SAmy Griffis
1086f368c07dSAmy Griffis /* This is a blocking read, so use audit_filter_mutex instead of rcu
1087f368c07dSAmy Griffis * iterator to sync with list writers. */
108893315ed6SAmy Griffis for (i = 0; i < AUDIT_NR_FILTERS; i++) {
1089e45aa212SAl Viro list_for_each_entry(r, &audit_rules_list[i], list) {
109093315ed6SAmy Griffis struct audit_rule_data *data;
109193315ed6SAmy Griffis
1092e45aa212SAl Viro data = audit_krule_to_data(r);
1093f368c07dSAmy Griffis if (unlikely(!data))
1094f368c07dSAmy Griffis break;
109545a0642bSPaul Moore skb = audit_make_reply(seq, AUDIT_LIST_RULES, 0, 1,
109645a0642bSPaul Moore data,
109730561b51SXiu Jianfeng struct_size(data, buf, data->buflen));
10989044e6bcSAl Viro if (skb)
10999044e6bcSAl Viro skb_queue_tail(q, skb);
110093315ed6SAmy Griffis kfree(data);
110193315ed6SAmy Griffis }
110293315ed6SAmy Griffis }
110345a0642bSPaul Moore skb = audit_make_reply(seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
11049044e6bcSAl Viro if (skb)
11059044e6bcSAl Viro skb_queue_tail(q, skb);
110693315ed6SAmy Griffis }
110793315ed6SAmy Griffis
11085adc8a6aSAmy Griffis /* Log rule additions and removals */
audit_log_rule_change(char * action,struct audit_krule * rule,int res)1109dc9eb698SEric Paris static void audit_log_rule_change(char *action, struct audit_krule *rule, int res)
11105adc8a6aSAmy Griffis {
11115adc8a6aSAmy Griffis struct audit_buffer *ab;
11125adc8a6aSAmy Griffis
11131a6b9f23SEric Paris if (!audit_enabled)
11141a6b9f23SEric Paris return;
11151a6b9f23SEric Paris
1116626abcd1SRichard Guy Briggs ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONFIG_CHANGE);
11175adc8a6aSAmy Griffis if (!ab)
11185adc8a6aSAmy Griffis return;
11195c5b8d8bSRichard Guy Briggs audit_log_session_info(ab);
1120b122c376SEric Paris audit_log_task_context(ab);
1121c1e8f06dSSteve Grubb audit_log_format(ab, " op=%s", action);
11229d960985SEric Paris audit_log_key(ab, rule->filterkey);
11235adc8a6aSAmy Griffis audit_log_format(ab, " list=%d res=%d", rule->listnr, res);
11245adc8a6aSAmy Griffis audit_log_end(ab);
11255adc8a6aSAmy Griffis }
11265adc8a6aSAmy Griffis
1127fe7752baSDavid Woodhouse /**
1128ce0d9f04SRichard Guy Briggs * audit_rule_change - apply all rules to the specified message type
1129fe7752baSDavid Woodhouse * @type: audit message type
1130fe7752baSDavid Woodhouse * @seq: netlink audit message sequence (serial) number
1131fe7752baSDavid Woodhouse * @data: payload data
113293315ed6SAmy Griffis * @datasz: size of payload data
1133fe7752baSDavid Woodhouse */
audit_rule_change(int type,int seq,void * data,size_t datasz)113445a0642bSPaul Moore int audit_rule_change(int type, int seq, void *data, size_t datasz)
1135fe7752baSDavid Woodhouse {
1136fe7752baSDavid Woodhouse int err = 0;
113793315ed6SAmy Griffis struct audit_entry *entry;
1138fe7752baSDavid Woodhouse
113970c4cf17SWenwen Wang switch (type) {
114070c4cf17SWenwen Wang case AUDIT_ADD_RULE:
1141e85322d2SRichard Guy Briggs entry = audit_data_to_entry(data, datasz);
1142e85322d2SRichard Guy Briggs if (IS_ERR(entry))
1143e85322d2SRichard Guy Briggs return PTR_ERR(entry);
1144ce0d9f04SRichard Guy Briggs err = audit_add_rule(entry);
1145e7df61f4SBurn Alting audit_log_rule_change("add_rule", &entry->rule, !err);
1146ce0d9f04SRichard Guy Briggs break;
1147ce0d9f04SRichard Guy Briggs case AUDIT_DEL_RULE:
114870c4cf17SWenwen Wang entry = audit_data_to_entry(data, datasz);
114970c4cf17SWenwen Wang if (IS_ERR(entry))
115070c4cf17SWenwen Wang return PTR_ERR(entry);
1151ce0d9f04SRichard Guy Briggs err = audit_del_rule(entry);
1152e7df61f4SBurn Alting audit_log_rule_change("remove_rule", &entry->rule, !err);
1153ce0d9f04SRichard Guy Briggs break;
1154ce0d9f04SRichard Guy Briggs default:
1155739c9503SEric Paris WARN_ON(1);
115670c4cf17SWenwen Wang return -EINVAL;
1157ce0d9f04SRichard Guy Briggs }
1158ce0d9f04SRichard Guy Briggs
115934d99af5SRichard Guy Briggs if (err || type == AUDIT_DEL_RULE) {
116034d99af5SRichard Guy Briggs if (entry->rule.exe)
116134d99af5SRichard Guy Briggs audit_remove_mark(entry->rule.exe);
1162e85322d2SRichard Guy Briggs audit_free_rule(entry);
116334d99af5SRichard Guy Briggs }
1164e85322d2SRichard Guy Briggs
1165ce0d9f04SRichard Guy Briggs return err;
1166ce0d9f04SRichard Guy Briggs }
1167ce0d9f04SRichard Guy Briggs
1168ce0d9f04SRichard Guy Briggs /**
1169ce0d9f04SRichard Guy Briggs * audit_list_rules_send - list the audit rules
1170d211f177SEric W. Biederman * @request_skb: skb of request we are replying to (used to target the reply)
1171ce0d9f04SRichard Guy Briggs * @seq: netlink audit message sequence (serial) number
1172ce0d9f04SRichard Guy Briggs */
audit_list_rules_send(struct sk_buff * request_skb,int seq)11736f285b19SEric W. Biederman int audit_list_rules_send(struct sk_buff *request_skb, int seq)
1174ce0d9f04SRichard Guy Briggs {
1175ce0d9f04SRichard Guy Briggs struct task_struct *tsk;
1176ce0d9f04SRichard Guy Briggs struct audit_netlink_list *dest;
1177ce0d9f04SRichard Guy Briggs
1178fe7752baSDavid Woodhouse /* We can't just spew out the rules here because we might fill
1179fe7752baSDavid Woodhouse * the available socket buffer space and deadlock waiting for
1180fe7752baSDavid Woodhouse * auditctl to read from it... which isn't ever going to
1181fe7752baSDavid Woodhouse * happen if we're actually running in the context of auditctl
1182fe7752baSDavid Woodhouse * trying to _send_ the stuff */
1183fe7752baSDavid Woodhouse
11843054d067SPaul Moore dest = kmalloc(sizeof(*dest), GFP_KERNEL);
1185fe7752baSDavid Woodhouse if (!dest)
1186fe7752baSDavid Woodhouse return -ENOMEM;
11873054d067SPaul Moore dest->net = get_net(sock_net(NETLINK_CB(request_skb).sk));
11883054d067SPaul Moore dest->portid = NETLINK_CB(request_skb).portid;
11899044e6bcSAl Viro skb_queue_head_init(&dest->q);
1190fe7752baSDavid Woodhouse
1191f368c07dSAmy Griffis mutex_lock(&audit_filter_mutex);
119245a0642bSPaul Moore audit_list_rules(seq, &dest->q);
1193f368c07dSAmy Griffis mutex_unlock(&audit_filter_mutex);
11949044e6bcSAl Viro
11953054d067SPaul Moore tsk = kthread_run(audit_send_list_thread, dest, "audit_send_list");
1196fe7752baSDavid Woodhouse if (IS_ERR(tsk)) {
11979044e6bcSAl Viro skb_queue_purge(&dest->q);
11983054d067SPaul Moore put_net(dest->net);
1199fe7752baSDavid Woodhouse kfree(dest);
12003054d067SPaul Moore return PTR_ERR(tsk);
1201fe7752baSDavid Woodhouse }
1202fe7752baSDavid Woodhouse
12033054d067SPaul Moore return 0;
1204fe7752baSDavid Woodhouse }
1205fe7752baSDavid Woodhouse
audit_comparator(u32 left,u32 op,u32 right)12065af75d8dSAl Viro int audit_comparator(u32 left, u32 op, u32 right)
1207fe7752baSDavid Woodhouse {
1208fe7752baSDavid Woodhouse switch (op) {
12095af75d8dSAl Viro case Audit_equal:
1210fe7752baSDavid Woodhouse return (left == right);
12115af75d8dSAl Viro case Audit_not_equal:
1212fe7752baSDavid Woodhouse return (left != right);
12135af75d8dSAl Viro case Audit_lt:
1214fe7752baSDavid Woodhouse return (left < right);
12155af75d8dSAl Viro case Audit_le:
1216fe7752baSDavid Woodhouse return (left <= right);
12175af75d8dSAl Viro case Audit_gt:
1218fe7752baSDavid Woodhouse return (left > right);
12195af75d8dSAl Viro case Audit_ge:
1220fe7752baSDavid Woodhouse return (left >= right);
12215af75d8dSAl Viro case Audit_bitmask:
122274f2345bSEric Paris return (left & right);
12235af75d8dSAl Viro case Audit_bittest:
122474f2345bSEric Paris return ((left & right) == right);
12255af75d8dSAl Viro default:
1226d9d9ec6eSDustin Kirkland return 0;
1227fe7752baSDavid Woodhouse }
12285af75d8dSAl Viro }
1229fe7752baSDavid Woodhouse
audit_uid_comparator(kuid_t left,u32 op,kuid_t right)1230ca57ec0fSEric W. Biederman int audit_uid_comparator(kuid_t left, u32 op, kuid_t right)
1231ca57ec0fSEric W. Biederman {
1232ca57ec0fSEric W. Biederman switch (op) {
1233ca57ec0fSEric W. Biederman case Audit_equal:
1234ca57ec0fSEric W. Biederman return uid_eq(left, right);
1235ca57ec0fSEric W. Biederman case Audit_not_equal:
1236ca57ec0fSEric W. Biederman return !uid_eq(left, right);
1237ca57ec0fSEric W. Biederman case Audit_lt:
1238ca57ec0fSEric W. Biederman return uid_lt(left, right);
1239ca57ec0fSEric W. Biederman case Audit_le:
1240ca57ec0fSEric W. Biederman return uid_lte(left, right);
1241ca57ec0fSEric W. Biederman case Audit_gt:
1242ca57ec0fSEric W. Biederman return uid_gt(left, right);
1243ca57ec0fSEric W. Biederman case Audit_ge:
1244ca57ec0fSEric W. Biederman return uid_gte(left, right);
1245ca57ec0fSEric W. Biederman case Audit_bitmask:
1246ca57ec0fSEric W. Biederman case Audit_bittest:
1247ca57ec0fSEric W. Biederman default:
1248ca57ec0fSEric W. Biederman return 0;
1249ca57ec0fSEric W. Biederman }
1250ca57ec0fSEric W. Biederman }
1251ca57ec0fSEric W. Biederman
audit_gid_comparator(kgid_t left,u32 op,kgid_t right)1252ca57ec0fSEric W. Biederman int audit_gid_comparator(kgid_t left, u32 op, kgid_t right)
1253ca57ec0fSEric W. Biederman {
1254ca57ec0fSEric W. Biederman switch (op) {
1255ca57ec0fSEric W. Biederman case Audit_equal:
1256ca57ec0fSEric W. Biederman return gid_eq(left, right);
1257ca57ec0fSEric W. Biederman case Audit_not_equal:
1258ca57ec0fSEric W. Biederman return !gid_eq(left, right);
1259ca57ec0fSEric W. Biederman case Audit_lt:
1260ca57ec0fSEric W. Biederman return gid_lt(left, right);
1261ca57ec0fSEric W. Biederman case Audit_le:
1262ca57ec0fSEric W. Biederman return gid_lte(left, right);
1263ca57ec0fSEric W. Biederman case Audit_gt:
1264ca57ec0fSEric W. Biederman return gid_gt(left, right);
1265ca57ec0fSEric W. Biederman case Audit_ge:
1266ca57ec0fSEric W. Biederman return gid_gte(left, right);
1267ca57ec0fSEric W. Biederman case Audit_bitmask:
1268ca57ec0fSEric W. Biederman case Audit_bittest:
1269ca57ec0fSEric W. Biederman default:
1270ca57ec0fSEric W. Biederman return 0;
1271ca57ec0fSEric W. Biederman }
1272ca57ec0fSEric W. Biederman }
1273ca57ec0fSEric W. Biederman
1274bfcec708SJeff Layton /**
1275bfcec708SJeff Layton * parent_len - find the length of the parent portion of a pathname
1276bfcec708SJeff Layton * @path: pathname of which to determine length
1277bfcec708SJeff Layton */
parent_len(const char * path)1278bfcec708SJeff Layton int parent_len(const char *path)
1279bfcec708SJeff Layton {
1280bfcec708SJeff Layton int plen;
1281bfcec708SJeff Layton const char *p;
1282bfcec708SJeff Layton
1283bfcec708SJeff Layton plen = strlen(path);
1284bfcec708SJeff Layton
1285bfcec708SJeff Layton if (plen == 0)
1286bfcec708SJeff Layton return plen;
1287bfcec708SJeff Layton
1288bfcec708SJeff Layton /* disregard trailing slashes */
1289bfcec708SJeff Layton p = path + plen - 1;
1290bfcec708SJeff Layton while ((*p == '/') && (p > path))
1291bfcec708SJeff Layton p--;
1292bfcec708SJeff Layton
1293bfcec708SJeff Layton /* walk backward until we find the next slash or hit beginning */
1294bfcec708SJeff Layton while ((*p != '/') && (p > path))
1295bfcec708SJeff Layton p--;
1296bfcec708SJeff Layton
1297bfcec708SJeff Layton /* did we find a slash? Then increment to include it in path */
1298bfcec708SJeff Layton if (*p == '/')
1299bfcec708SJeff Layton p++;
1300bfcec708SJeff Layton
1301bfcec708SJeff Layton return p - path;
1302bfcec708SJeff Layton }
1303bfcec708SJeff Layton
1304e3d6b07bSJeff Layton /**
1305e3d6b07bSJeff Layton * audit_compare_dname_path - compare given dentry name with last component in
1306e3d6b07bSJeff Layton * given path. Return of 0 indicates a match.
1307e3d6b07bSJeff Layton * @dname: dentry name that we're comparing
1308e3d6b07bSJeff Layton * @path: full pathname that we're comparing
1309e3d6b07bSJeff Layton * @parentlen: length of the parent if known. Passing in AUDIT_NAME_FULL
1310e3d6b07bSJeff Layton * here indicates that we must compute this value.
1311e3d6b07bSJeff Layton */
audit_compare_dname_path(const struct qstr * dname,const char * path,int parentlen)1312795d673aSAl Viro int audit_compare_dname_path(const struct qstr *dname, const char *path, int parentlen)
1313f368c07dSAmy Griffis {
1314e3d6b07bSJeff Layton int dlen, pathlen;
1315f368c07dSAmy Griffis const char *p;
1316fe7752baSDavid Woodhouse
1317795d673aSAl Viro dlen = dname->len;
131829e9a346SEric Paris pathlen = strlen(path);
131929e9a346SEric Paris if (pathlen < dlen)
1320f368c07dSAmy Griffis return 1;
1321f368c07dSAmy Griffis
1322e3d6b07bSJeff Layton parentlen = parentlen == AUDIT_NAME_FULL ? parent_len(path) : parentlen;
132329e9a346SEric Paris if (pathlen - parentlen != dlen)
132429e9a346SEric Paris return 1;
1325f368c07dSAmy Griffis
132629e9a346SEric Paris p = path + parentlen;
1327f368c07dSAmy Griffis
1328795d673aSAl Viro return strncmp(p, dname->name, dlen);
1329f368c07dSAmy Griffis }
1330fe7752baSDavid Woodhouse
audit_filter(int msgtype,unsigned int listtype)133186b2efbeSRichard Guy Briggs int audit_filter(int msgtype, unsigned int listtype)
1332fe7752baSDavid Woodhouse {
133386b2efbeSRichard Guy Briggs struct audit_entry *e;
133486b2efbeSRichard Guy Briggs int ret = 1; /* Audit by default */
1335fe7752baSDavid Woodhouse
133686b2efbeSRichard Guy Briggs rcu_read_lock();
133786b2efbeSRichard Guy Briggs list_for_each_entry_rcu(e, &audit_filter_list[listtype], list) {
133886b2efbeSRichard Guy Briggs int i, result = 0;
133986b2efbeSRichard Guy Briggs
134086b2efbeSRichard Guy Briggs for (i = 0; i < e->rule.field_count; i++) {
134186b2efbeSRichard Guy Briggs struct audit_field *f = &e->rule.fields[i];
1342f1dc4867SRichard Guy Briggs pid_t pid;
1343c53fa1edSPatrick McHardy u32 sid;
1344fe7752baSDavid Woodhouse
134593315ed6SAmy Griffis switch (f->type) {
1346fe7752baSDavid Woodhouse case AUDIT_PID:
1347f1dc4867SRichard Guy Briggs pid = task_pid_nr(current);
1348f1dc4867SRichard Guy Briggs result = audit_comparator(pid, f->op, f->val);
1349fe7752baSDavid Woodhouse break;
1350fe7752baSDavid Woodhouse case AUDIT_UID:
1351ca57ec0fSEric W. Biederman result = audit_uid_comparator(current_uid(), f->op, f->uid);
1352fe7752baSDavid Woodhouse break;
1353fe7752baSDavid Woodhouse case AUDIT_GID:
1354ca57ec0fSEric W. Biederman result = audit_gid_comparator(current_gid(), f->op, f->gid);
1355fe7752baSDavid Woodhouse break;
1356fe7752baSDavid Woodhouse case AUDIT_LOGINUID:
1357ca57ec0fSEric W. Biederman result = audit_uid_comparator(audit_get_loginuid(current),
1358ca57ec0fSEric W. Biederman f->op, f->uid);
1359fe7752baSDavid Woodhouse break;
1360780a7654SEric W. Biederman case AUDIT_LOGINUID_SET:
1361780a7654SEric W. Biederman result = audit_comparator(audit_loginuid_set(current),
1362780a7654SEric W. Biederman f->op, f->val);
1363780a7654SEric W. Biederman break;
136462062cf8SEric Paris case AUDIT_MSGTYPE:
136586b2efbeSRichard Guy Briggs result = audit_comparator(msgtype, f->op, f->val);
136662062cf8SEric Paris break;
1367d29be158SMiloslav Trmac case AUDIT_SUBJ_USER:
1368d29be158SMiloslav Trmac case AUDIT_SUBJ_ROLE:
1369d29be158SMiloslav Trmac case AUDIT_SUBJ_TYPE:
1370d29be158SMiloslav Trmac case AUDIT_SUBJ_SEN:
1371d29be158SMiloslav Trmac case AUDIT_SUBJ_CLR:
1372c53fa1edSPatrick McHardy if (f->lsm_rule) {
13736326948fSPaul Moore security_current_getsecid_subj(&sid);
1374c53fa1edSPatrick McHardy result = security_audit_rule_match(sid,
137590462a5bSRichard Guy Briggs f->type, f->op, f->lsm_rule);
1376c53fa1edSPatrick McHardy }
1377d29be158SMiloslav Trmac break;
137829c1372dSOndrej Mosnáček case AUDIT_EXE:
137929c1372dSOndrej Mosnáček result = audit_exe_compare(current, e->rule.exe);
138029c1372dSOndrej Mosnáček if (f->op == Audit_not_equal)
138129c1372dSOndrej Mosnáček result = !result;
138229c1372dSOndrej Mosnáček break;
138386b2efbeSRichard Guy Briggs default:
138486b2efbeSRichard Guy Briggs goto unlock_and_return;
1385fe7752baSDavid Woodhouse }
138686b2efbeSRichard Guy Briggs if (result < 0) /* error */
138786b2efbeSRichard Guy Briggs goto unlock_and_return;
138886b2efbeSRichard Guy Briggs if (!result)
138966b12abcSPaul Moore break;
1390fe7752baSDavid Woodhouse }
139186b2efbeSRichard Guy Briggs if (result > 0) {
1392d904ac03SRichard Guy Briggs if (e->rule.action == AUDIT_NEVER || listtype == AUDIT_FILTER_EXCLUDE)
1393fe7752baSDavid Woodhouse ret = 0;
1394fe7752baSDavid Woodhouse break;
1395fe7752baSDavid Woodhouse }
1396fe7752baSDavid Woodhouse }
1397fe7752baSDavid Woodhouse unlock_and_return:
1398fe7752baSDavid Woodhouse rcu_read_unlock();
139986b2efbeSRichard Guy Briggs return ret;
1400fe7752baSDavid Woodhouse }
14013dc7e315SDarrel Goeddel
update_lsm_rule(struct audit_krule * r)1402e45aa212SAl Viro static int update_lsm_rule(struct audit_krule *r)
14033dc7e315SDarrel Goeddel {
1404e45aa212SAl Viro struct audit_entry *entry = container_of(r, struct audit_entry, rule);
14051a9d0797SAl Viro struct audit_entry *nentry;
14061a9d0797SAl Viro int err = 0;
14073dc7e315SDarrel Goeddel
1408e45aa212SAl Viro if (!security_audit_rule_known(r))
14091a9d0797SAl Viro return 0;
14103dc7e315SDarrel Goeddel
1411ae7b8f41SEric Paris nentry = audit_dupe_rule(r);
141234d99af5SRichard Guy Briggs if (entry->rule.exe)
141334d99af5SRichard Guy Briggs audit_remove_mark(entry->rule.exe);
1414801678c5SHirofumi Nakagawa if (IS_ERR(nentry)) {
14153dc7e315SDarrel Goeddel /* save the first error encountered for the
14163dc7e315SDarrel Goeddel * return value */
14173dc7e315SDarrel Goeddel err = PTR_ERR(nentry);
1418d7a96f3aSAhmed S. Darwish audit_panic("error updating LSM filters");
1419ae7b8f41SEric Paris if (r->watch)
1420e45aa212SAl Viro list_del(&r->rlist);
14213dc7e315SDarrel Goeddel list_del_rcu(&entry->list);
1422e45aa212SAl Viro list_del(&r->list);
14233dc7e315SDarrel Goeddel } else {
1424ae7b8f41SEric Paris if (r->watch || r->tree)
1425e45aa212SAl Viro list_replace_init(&r->rlist, &nentry->rule.rlist);
14263dc7e315SDarrel Goeddel list_replace_rcu(&entry->list, &nentry->list);
1427e45aa212SAl Viro list_replace(&r->list, &nentry->rule.list);
14283dc7e315SDarrel Goeddel }
14293dc7e315SDarrel Goeddel call_rcu(&entry->rcu, audit_free_rule_rcu);
14301a9d0797SAl Viro
14311a9d0797SAl Viro return err;
14321a9d0797SAl Viro }
14331a9d0797SAl Viro
14341a9d0797SAl Viro /* This function will re-initialize the lsm_rule field of all applicable rules.
14351a9d0797SAl Viro * It will traverse the filter lists serarching for rules that contain LSM
14361a9d0797SAl Viro * specific filter fields. When such a rule is found, it is copied, the
14371a9d0797SAl Viro * LSM field is re-initialized, and the old rule is replaced with the
14381a9d0797SAl Viro * updated rule. */
audit_update_lsm_rules(void)14391a9d0797SAl Viro int audit_update_lsm_rules(void)
14401a9d0797SAl Viro {
1441e45aa212SAl Viro struct audit_krule *r, *n;
14421a9d0797SAl Viro int i, err = 0;
14431a9d0797SAl Viro
14441a9d0797SAl Viro /* audit_filter_mutex synchronizes the writers */
14451a9d0797SAl Viro mutex_lock(&audit_filter_mutex);
14461a9d0797SAl Viro
14471a9d0797SAl Viro for (i = 0; i < AUDIT_NR_FILTERS; i++) {
1448e45aa212SAl Viro list_for_each_entry_safe(r, n, &audit_rules_list[i], list) {
1449e45aa212SAl Viro int res = update_lsm_rule(r);
14501a9d0797SAl Viro if (!err)
14511a9d0797SAl Viro err = res;
14521a9d0797SAl Viro }
14531a9d0797SAl Viro }
1454f368c07dSAmy Griffis mutex_unlock(&audit_filter_mutex);
14553dc7e315SDarrel Goeddel
14563dc7e315SDarrel Goeddel return err;
14573dc7e315SDarrel Goeddel }
1458