diff options
Diffstat (limited to 'kernel/auditfilter.c')
-rw-r--r-- | kernel/auditfilter.c | 467 |
1 files changed, 357 insertions, 110 deletions
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index a3a32752f973..686d514a3518 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c | |||
@@ -40,52 +40,279 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { | |||
40 | #endif | 40 | #endif |
41 | }; | 41 | }; |
42 | 42 | ||
43 | /* Copy rule from user-space to kernel-space. Called from | 43 | static inline void audit_free_rule(struct audit_entry *e) |
44 | * audit_add_rule during AUDIT_ADD. */ | ||
45 | static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) | ||
46 | { | 44 | { |
45 | kfree(e->rule.fields); | ||
46 | kfree(e); | ||
47 | } | ||
48 | |||
49 | static inline void audit_free_rule_rcu(struct rcu_head *head) | ||
50 | { | ||
51 | struct audit_entry *e = container_of(head, struct audit_entry, rcu); | ||
52 | audit_free_rule(e); | ||
53 | } | ||
54 | |||
55 | /* Unpack a filter field's string representation from user-space | ||
56 | * buffer. */ | ||
57 | static __attribute__((unused)) char *audit_unpack_string(void **bufp, size_t *remain, size_t len) | ||
58 | { | ||
59 | char *str; | ||
60 | |||
61 | if (!*bufp || (len == 0) || (len > *remain)) | ||
62 | return ERR_PTR(-EINVAL); | ||
63 | |||
64 | /* Of the currently implemented string fields, PATH_MAX | ||
65 | * defines the longest valid length. | ||
66 | */ | ||
67 | if (len > PATH_MAX) | ||
68 | return ERR_PTR(-ENAMETOOLONG); | ||
69 | |||
70 | str = kmalloc(len + 1, GFP_KERNEL); | ||
71 | if (unlikely(!str)) | ||
72 | return ERR_PTR(-ENOMEM); | ||
73 | |||
74 | memcpy(str, *bufp, len); | ||
75 | str[len] = 0; | ||
76 | *bufp += len; | ||
77 | *remain -= len; | ||
78 | |||
79 | return str; | ||
80 | } | ||
81 | |||
82 | /* Common user-space to kernel rule translation. */ | ||
83 | static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule) | ||
84 | { | ||
85 | unsigned listnr; | ||
86 | struct audit_entry *entry; | ||
87 | struct audit_field *fields; | ||
88 | int i, err; | ||
89 | |||
90 | err = -EINVAL; | ||
91 | listnr = rule->flags & ~AUDIT_FILTER_PREPEND; | ||
92 | switch(listnr) { | ||
93 | default: | ||
94 | goto exit_err; | ||
95 | case AUDIT_FILTER_USER: | ||
96 | case AUDIT_FILTER_TYPE: | ||
97 | #ifdef CONFIG_AUDITSYSCALL | ||
98 | case AUDIT_FILTER_ENTRY: | ||
99 | case AUDIT_FILTER_EXIT: | ||
100 | case AUDIT_FILTER_TASK: | ||
101 | #endif | ||
102 | ; | ||
103 | } | ||
104 | if (rule->action != AUDIT_NEVER && rule->action != AUDIT_POSSIBLE && | ||
105 | rule->action != AUDIT_ALWAYS) | ||
106 | goto exit_err; | ||
107 | if (rule->field_count > AUDIT_MAX_FIELDS) | ||
108 | goto exit_err; | ||
109 | |||
110 | err = -ENOMEM; | ||
111 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | ||
112 | if (unlikely(!entry)) | ||
113 | goto exit_err; | ||
114 | fields = kmalloc(sizeof(*fields) * rule->field_count, GFP_KERNEL); | ||
115 | if (unlikely(!fields)) { | ||
116 | kfree(entry); | ||
117 | goto exit_err; | ||
118 | } | ||
119 | |||
120 | memset(&entry->rule, 0, sizeof(struct audit_krule)); | ||
121 | memset(fields, 0, sizeof(struct audit_field)); | ||
122 | |||
123 | entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND; | ||
124 | entry->rule.listnr = listnr; | ||
125 | entry->rule.action = rule->action; | ||
126 | entry->rule.field_count = rule->field_count; | ||
127 | entry->rule.fields = fields; | ||
128 | |||
129 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) | ||
130 | entry->rule.mask[i] = rule->mask[i]; | ||
131 | |||
132 | return entry; | ||
133 | |||
134 | exit_err: | ||
135 | return ERR_PTR(err); | ||
136 | } | ||
137 | |||
138 | /* Translate struct audit_rule to kernel's rule respresentation. | ||
139 | * Exists for backward compatibility with userspace. */ | ||
140 | static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) | ||
141 | { | ||
142 | struct audit_entry *entry; | ||
143 | int err = 0; | ||
47 | int i; | 144 | int i; |
48 | 145 | ||
49 | if (s->action != AUDIT_NEVER | 146 | entry = audit_to_entry_common(rule); |
50 | && s->action != AUDIT_POSSIBLE | 147 | if (IS_ERR(entry)) |
51 | && s->action != AUDIT_ALWAYS) | 148 | goto exit_nofree; |
52 | return -1; | 149 | |
53 | if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS) | 150 | for (i = 0; i < rule->field_count; i++) { |
54 | return -1; | 151 | struct audit_field *f = &entry->rule.fields[i]; |
55 | if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS) | 152 | |
56 | return -1; | 153 | if (rule->fields[i] & AUDIT_UNUSED_BITS) { |
57 | 154 | err = -EINVAL; | |
58 | d->flags = s->flags; | 155 | goto exit_free; |
59 | d->action = s->action; | 156 | } |
60 | d->field_count = s->field_count; | 157 | |
61 | for (i = 0; i < d->field_count; i++) { | 158 | f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS); |
62 | d->fields[i] = s->fields[i]; | 159 | f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); |
63 | d->values[i] = s->values[i]; | 160 | f->val = rule->values[i]; |
161 | |||
162 | entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1; | ||
163 | if (f->op & AUDIT_NEGATE) | ||
164 | f->op |= AUDIT_NOT_EQUAL; | ||
165 | else if (!(f->op & AUDIT_OPERATORS)) | ||
166 | f->op |= AUDIT_EQUAL; | ||
167 | f->op &= ~AUDIT_NEGATE; | ||
64 | } | 168 | } |
65 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i]; | 169 | |
66 | return 0; | 170 | exit_nofree: |
171 | return entry; | ||
172 | |||
173 | exit_free: | ||
174 | audit_free_rule(entry); | ||
175 | return ERR_PTR(err); | ||
67 | } | 176 | } |
68 | 177 | ||
69 | /* Check to see if two rules are identical. It is called from | 178 | /* Translate struct audit_rule_data to kernel's rule respresentation. */ |
70 | * audit_add_rule during AUDIT_ADD and | 179 | static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, |
71 | * audit_del_rule during AUDIT_DEL. */ | 180 | size_t datasz) |
72 | static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) | ||
73 | { | 181 | { |
182 | int err = 0; | ||
183 | struct audit_entry *entry; | ||
184 | void *bufp; | ||
185 | /* size_t remain = datasz - sizeof(struct audit_rule_data); */ | ||
74 | int i; | 186 | int i; |
75 | 187 | ||
76 | if (a->flags != b->flags) | 188 | entry = audit_to_entry_common((struct audit_rule *)data); |
77 | return 1; | 189 | if (IS_ERR(entry)) |
190 | goto exit_nofree; | ||
78 | 191 | ||
79 | if (a->action != b->action) | 192 | bufp = data->buf; |
80 | return 1; | 193 | entry->rule.vers_ops = 2; |
194 | for (i = 0; i < data->field_count; i++) { | ||
195 | struct audit_field *f = &entry->rule.fields[i]; | ||
196 | |||
197 | err = -EINVAL; | ||
198 | if (!(data->fieldflags[i] & AUDIT_OPERATORS) || | ||
199 | data->fieldflags[i] & ~AUDIT_OPERATORS) | ||
200 | goto exit_free; | ||
201 | |||
202 | f->op = data->fieldflags[i] & AUDIT_OPERATORS; | ||
203 | f->type = data->fields[i]; | ||
204 | switch(f->type) { | ||
205 | /* call type-specific conversion routines here */ | ||
206 | default: | ||
207 | f->val = data->values[i]; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | exit_nofree: | ||
212 | return entry; | ||
213 | |||
214 | exit_free: | ||
215 | audit_free_rule(entry); | ||
216 | return ERR_PTR(err); | ||
217 | } | ||
218 | |||
219 | /* Pack a filter field's string representation into data block. */ | ||
220 | static inline size_t audit_pack_string(void **bufp, char *str) | ||
221 | { | ||
222 | size_t len = strlen(str); | ||
223 | |||
224 | memcpy(*bufp, str, len); | ||
225 | *bufp += len; | ||
226 | |||
227 | return len; | ||
228 | } | ||
229 | |||
230 | /* Translate kernel rule respresentation to struct audit_rule. | ||
231 | * Exists for backward compatibility with userspace. */ | ||
232 | static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule) | ||
233 | { | ||
234 | struct audit_rule *rule; | ||
235 | int i; | ||
236 | |||
237 | rule = kmalloc(sizeof(*rule), GFP_KERNEL); | ||
238 | if (unlikely(!rule)) | ||
239 | return ERR_PTR(-ENOMEM); | ||
240 | memset(rule, 0, sizeof(*rule)); | ||
241 | |||
242 | rule->flags = krule->flags | krule->listnr; | ||
243 | rule->action = krule->action; | ||
244 | rule->field_count = krule->field_count; | ||
245 | for (i = 0; i < rule->field_count; i++) { | ||
246 | rule->values[i] = krule->fields[i].val; | ||
247 | rule->fields[i] = krule->fields[i].type; | ||
248 | |||
249 | if (krule->vers_ops == 1) { | ||
250 | if (krule->fields[i].op & AUDIT_NOT_EQUAL) | ||
251 | rule->fields[i] |= AUDIT_NEGATE; | ||
252 | } else { | ||
253 | rule->fields[i] |= krule->fields[i].op; | ||
254 | } | ||
255 | } | ||
256 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i]; | ||
257 | |||
258 | return rule; | ||
259 | } | ||
81 | 260 | ||
82 | if (a->field_count != b->field_count) | 261 | /* Translate kernel rule respresentation to struct audit_rule_data. */ |
262 | static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) | ||
263 | { | ||
264 | struct audit_rule_data *data; | ||
265 | void *bufp; | ||
266 | int i; | ||
267 | |||
268 | data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL); | ||
269 | if (unlikely(!data)) | ||
270 | return ERR_PTR(-ENOMEM); | ||
271 | memset(data, 0, sizeof(*data)); | ||
272 | |||
273 | data->flags = krule->flags | krule->listnr; | ||
274 | data->action = krule->action; | ||
275 | data->field_count = krule->field_count; | ||
276 | bufp = data->buf; | ||
277 | for (i = 0; i < data->field_count; i++) { | ||
278 | struct audit_field *f = &krule->fields[i]; | ||
279 | |||
280 | data->fields[i] = f->type; | ||
281 | data->fieldflags[i] = f->op; | ||
282 | switch(f->type) { | ||
283 | /* call type-specific conversion routines here */ | ||
284 | default: | ||
285 | data->values[i] = f->val; | ||
286 | } | ||
287 | } | ||
288 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) data->mask[i] = krule->mask[i]; | ||
289 | |||
290 | return data; | ||
291 | } | ||
292 | |||
293 | /* Compare two rules in kernel format. Considered success if rules | ||
294 | * don't match. */ | ||
295 | static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) | ||
296 | { | ||
297 | int i; | ||
298 | |||
299 | if (a->flags != b->flags || | ||
300 | a->listnr != b->listnr || | ||
301 | a->action != b->action || | ||
302 | a->field_count != b->field_count) | ||
83 | return 1; | 303 | return 1; |
84 | 304 | ||
85 | for (i = 0; i < a->field_count; i++) { | 305 | for (i = 0; i < a->field_count; i++) { |
86 | if (a->fields[i] != b->fields[i] | 306 | if (a->fields[i].type != b->fields[i].type || |
87 | || a->values[i] != b->values[i]) | 307 | a->fields[i].op != b->fields[i].op) |
88 | return 1; | 308 | return 1; |
309 | |||
310 | switch(a->fields[i].type) { | ||
311 | /* call type-specific comparison routines here */ | ||
312 | default: | ||
313 | if (a->fields[i].val != b->fields[i].val) | ||
314 | return 1; | ||
315 | } | ||
89 | } | 316 | } |
90 | 317 | ||
91 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) | 318 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) |
@@ -95,41 +322,21 @@ static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) | |||
95 | return 0; | 322 | return 0; |
96 | } | 323 | } |
97 | 324 | ||
98 | /* Note that audit_add_rule and audit_del_rule are called via | 325 | /* Add rule to given filterlist if not a duplicate. Protected by |
99 | * audit_receive() in audit.c, and are protected by | ||
100 | * audit_netlink_sem. */ | 326 | * audit_netlink_sem. */ |
101 | static inline int audit_add_rule(struct audit_rule *rule, | 327 | static inline int audit_add_rule(struct audit_entry *entry, |
102 | struct list_head *list) | 328 | struct list_head *list) |
103 | { | 329 | { |
104 | struct audit_entry *entry; | 330 | struct audit_entry *e; |
105 | int i; | ||
106 | 331 | ||
107 | /* Do not use the _rcu iterator here, since this is the only | 332 | /* Do not use the _rcu iterator here, since this is the only |
108 | * addition routine. */ | 333 | * addition routine. */ |
109 | list_for_each_entry(entry, list, list) { | 334 | list_for_each_entry(e, list, list) { |
110 | if (!audit_compare_rule(rule, &entry->rule)) | 335 | if (!audit_compare_rule(&entry->rule, &e->rule)) |
111 | return -EEXIST; | 336 | return -EEXIST; |
112 | } | 337 | } |
113 | 338 | ||
114 | for (i = 0; i < rule->field_count; i++) { | ||
115 | if (rule->fields[i] & AUDIT_UNUSED_BITS) | ||
116 | return -EINVAL; | ||
117 | if ( rule->fields[i] & AUDIT_NEGATE) | ||
118 | rule->fields[i] |= AUDIT_NOT_EQUAL; | ||
119 | else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 ) | ||
120 | rule->fields[i] |= AUDIT_EQUAL; | ||
121 | rule->fields[i] &= ~AUDIT_NEGATE; | ||
122 | } | ||
123 | |||
124 | if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) | ||
125 | return -ENOMEM; | ||
126 | if (audit_copy_rule(&entry->rule, rule)) { | ||
127 | kfree(entry); | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | |||
131 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { | 339 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { |
132 | entry->rule.flags &= ~AUDIT_FILTER_PREPEND; | ||
133 | list_add_rcu(&entry->list, list); | 340 | list_add_rcu(&entry->list, list); |
134 | } else { | 341 | } else { |
135 | list_add_tail_rcu(&entry->list, list); | 342 | list_add_tail_rcu(&entry->list, list); |
@@ -138,16 +345,9 @@ static inline int audit_add_rule(struct audit_rule *rule, | |||
138 | return 0; | 345 | return 0; |
139 | } | 346 | } |
140 | 347 | ||
141 | static inline void audit_free_rule(struct rcu_head *head) | 348 | /* Remove an existing rule from filterlist. Protected by |
142 | { | ||
143 | struct audit_entry *e = container_of(head, struct audit_entry, rcu); | ||
144 | kfree(e); | ||
145 | } | ||
146 | |||
147 | /* Note that audit_add_rule and audit_del_rule are called via | ||
148 | * audit_receive() in audit.c, and are protected by | ||
149 | * audit_netlink_sem. */ | 349 | * audit_netlink_sem. */ |
150 | static inline int audit_del_rule(struct audit_rule *rule, | 350 | static inline int audit_del_rule(struct audit_entry *entry, |
151 | struct list_head *list) | 351 | struct list_head *list) |
152 | { | 352 | { |
153 | struct audit_entry *e; | 353 | struct audit_entry *e; |
@@ -155,16 +355,18 @@ static inline int audit_del_rule(struct audit_rule *rule, | |||
155 | /* Do not use the _rcu iterator here, since this is the only | 355 | /* Do not use the _rcu iterator here, since this is the only |
156 | * deletion routine. */ | 356 | * deletion routine. */ |
157 | list_for_each_entry(e, list, list) { | 357 | list_for_each_entry(e, list, list) { |
158 | if (!audit_compare_rule(rule, &e->rule)) { | 358 | if (!audit_compare_rule(&entry->rule, &e->rule)) { |
159 | list_del_rcu(&e->list); | 359 | list_del_rcu(&e->list); |
160 | call_rcu(&e->rcu, audit_free_rule); | 360 | call_rcu(&e->rcu, audit_free_rule_rcu); |
161 | return 0; | 361 | return 0; |
162 | } | 362 | } |
163 | } | 363 | } |
164 | return -ENOENT; /* No matching rule */ | 364 | return -ENOENT; /* No matching rule */ |
165 | } | 365 | } |
166 | 366 | ||
167 | static int audit_list_rules(void *_dest) | 367 | /* List rules using struct audit_rule. Exists for backward |
368 | * compatibility with userspace. */ | ||
369 | static int audit_list(void *_dest) | ||
168 | { | 370 | { |
169 | int pid, seq; | 371 | int pid, seq; |
170 | int *dest = _dest; | 372 | int *dest = _dest; |
@@ -180,9 +382,16 @@ static int audit_list_rules(void *_dest) | |||
180 | /* The *_rcu iterators not needed here because we are | 382 | /* The *_rcu iterators not needed here because we are |
181 | always called with audit_netlink_sem held. */ | 383 | always called with audit_netlink_sem held. */ |
182 | for (i=0; i<AUDIT_NR_FILTERS; i++) { | 384 | for (i=0; i<AUDIT_NR_FILTERS; i++) { |
183 | list_for_each_entry(entry, &audit_filter_list[i], list) | 385 | list_for_each_entry(entry, &audit_filter_list[i], list) { |
386 | struct audit_rule *rule; | ||
387 | |||
388 | rule = audit_krule_to_rule(&entry->rule); | ||
389 | if (unlikely(!rule)) | ||
390 | break; | ||
184 | audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, | 391 | audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, |
185 | &entry->rule, sizeof(entry->rule)); | 392 | rule, sizeof(*rule)); |
393 | kfree(rule); | ||
394 | } | ||
186 | } | 395 | } |
187 | audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); | 396 | audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); |
188 | 397 | ||
@@ -190,6 +399,40 @@ static int audit_list_rules(void *_dest) | |||
190 | return 0; | 399 | return 0; |
191 | } | 400 | } |
192 | 401 | ||
402 | /* List rules using struct audit_rule_data. */ | ||
403 | static int audit_list_rules(void *_dest) | ||
404 | { | ||
405 | int pid, seq; | ||
406 | int *dest = _dest; | ||
407 | struct audit_entry *e; | ||
408 | int i; | ||
409 | |||
410 | pid = dest[0]; | ||
411 | seq = dest[1]; | ||
412 | kfree(dest); | ||
413 | |||
414 | down(&audit_netlink_sem); | ||
415 | |||
416 | /* The *_rcu iterators not needed here because we are | ||
417 | always called with audit_netlink_sem held. */ | ||
418 | for (i=0; i<AUDIT_NR_FILTERS; i++) { | ||
419 | list_for_each_entry(e, &audit_filter_list[i], list) { | ||
420 | struct audit_rule_data *data; | ||
421 | |||
422 | data = audit_krule_to_data(&e->rule); | ||
423 | if (unlikely(!data)) | ||
424 | break; | ||
425 | audit_send_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, | ||
426 | data, sizeof(*data)); | ||
427 | kfree(data); | ||
428 | } | ||
429 | } | ||
430 | audit_send_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0); | ||
431 | |||
432 | up(&audit_netlink_sem); | ||
433 | return 0; | ||
434 | } | ||
435 | |||
193 | /** | 436 | /** |
194 | * audit_receive_filter - apply all rules to the specified message type | 437 | * audit_receive_filter - apply all rules to the specified message type |
195 | * @type: audit message type | 438 | * @type: audit message type |
@@ -197,18 +440,20 @@ static int audit_list_rules(void *_dest) | |||
197 | * @uid: target uid for netlink audit messages | 440 | * @uid: target uid for netlink audit messages |
198 | * @seq: netlink audit message sequence (serial) number | 441 | * @seq: netlink audit message sequence (serial) number |
199 | * @data: payload data | 442 | * @data: payload data |
443 | * @datasz: size of payload data | ||
200 | * @loginuid: loginuid of sender | 444 | * @loginuid: loginuid of sender |
201 | */ | 445 | */ |
202 | int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | 446 | int audit_receive_filter(int type, int pid, int uid, int seq, void *data, |
203 | uid_t loginuid) | 447 | size_t datasz, uid_t loginuid) |
204 | { | 448 | { |
205 | struct task_struct *tsk; | 449 | struct task_struct *tsk; |
206 | int *dest; | 450 | int *dest; |
207 | int err = 0; | 451 | int err = 0; |
208 | unsigned listnr; | 452 | struct audit_entry *entry; |
209 | 453 | ||
210 | switch (type) { | 454 | switch (type) { |
211 | case AUDIT_LIST: | 455 | case AUDIT_LIST: |
456 | case AUDIT_LIST_RULES: | ||
212 | /* We can't just spew out the rules here because we might fill | 457 | /* We can't just spew out the rules here because we might fill |
213 | * the available socket buffer space and deadlock waiting for | 458 | * the available socket buffer space and deadlock waiting for |
214 | * auditctl to read from it... which isn't ever going to | 459 | * auditctl to read from it... which isn't ever going to |
@@ -221,41 +466,48 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
221 | dest[0] = pid; | 466 | dest[0] = pid; |
222 | dest[1] = seq; | 467 | dest[1] = seq; |
223 | 468 | ||
224 | tsk = kthread_run(audit_list_rules, dest, "audit_list_rules"); | 469 | if (type == AUDIT_LIST) |
470 | tsk = kthread_run(audit_list, dest, "audit_list"); | ||
471 | else | ||
472 | tsk = kthread_run(audit_list_rules, dest, | ||
473 | "audit_list_rules"); | ||
225 | if (IS_ERR(tsk)) { | 474 | if (IS_ERR(tsk)) { |
226 | kfree(dest); | 475 | kfree(dest); |
227 | err = PTR_ERR(tsk); | 476 | err = PTR_ERR(tsk); |
228 | } | 477 | } |
229 | break; | 478 | break; |
230 | case AUDIT_ADD: | 479 | case AUDIT_ADD: |
231 | listnr = ((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; | 480 | case AUDIT_ADD_RULE: |
232 | switch(listnr) { | 481 | if (type == AUDIT_ADD) |
233 | default: | 482 | entry = audit_rule_to_entry(data); |
234 | return -EINVAL; | 483 | else |
235 | 484 | entry = audit_data_to_entry(data, datasz); | |
236 | case AUDIT_FILTER_USER: | 485 | if (IS_ERR(entry)) |
237 | case AUDIT_FILTER_TYPE: | 486 | return PTR_ERR(entry); |
238 | #ifdef CONFIG_AUDITSYSCALL | 487 | |
239 | case AUDIT_FILTER_ENTRY: | 488 | err = audit_add_rule(entry, |
240 | case AUDIT_FILTER_EXIT: | 489 | &audit_filter_list[entry->rule.listnr]); |
241 | case AUDIT_FILTER_TASK: | ||
242 | #endif | ||
243 | ; | ||
244 | } | ||
245 | err = audit_add_rule(data, &audit_filter_list[listnr]); | ||
246 | if (!err) | 490 | if (!err) |
247 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 491 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
248 | "auid=%u added an audit rule\n", loginuid); | 492 | "auid=%u added an audit rule\n", loginuid); |
493 | else | ||
494 | audit_free_rule(entry); | ||
249 | break; | 495 | break; |
250 | case AUDIT_DEL: | 496 | case AUDIT_DEL: |
251 | listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; | 497 | case AUDIT_DEL_RULE: |
252 | if (listnr >= AUDIT_NR_FILTERS) | 498 | if (type == AUDIT_DEL) |
253 | return -EINVAL; | 499 | entry = audit_rule_to_entry(data); |
254 | 500 | else | |
255 | err = audit_del_rule(data, &audit_filter_list[listnr]); | 501 | entry = audit_data_to_entry(data, datasz); |
502 | if (IS_ERR(entry)) | ||
503 | return PTR_ERR(entry); | ||
504 | |||
505 | err = audit_del_rule(entry, | ||
506 | &audit_filter_list[entry->rule.listnr]); | ||
256 | if (!err) | 507 | if (!err) |
257 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 508 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
258 | "auid=%u removed an audit rule\n", loginuid); | 509 | "auid=%u removed an audit rule\n", loginuid); |
510 | audit_free_rule(entry); | ||
259 | break; | 511 | break; |
260 | default: | 512 | default: |
261 | return -EINVAL; | 513 | return -EINVAL; |
@@ -287,29 +539,27 @@ int audit_comparator(const u32 left, const u32 op, const u32 right) | |||
287 | 539 | ||
288 | 540 | ||
289 | static int audit_filter_user_rules(struct netlink_skb_parms *cb, | 541 | static int audit_filter_user_rules(struct netlink_skb_parms *cb, |
290 | struct audit_rule *rule, | 542 | struct audit_krule *rule, |
291 | enum audit_state *state) | 543 | enum audit_state *state) |
292 | { | 544 | { |
293 | int i; | 545 | int i; |
294 | 546 | ||
295 | for (i = 0; i < rule->field_count; i++) { | 547 | for (i = 0; i < rule->field_count; i++) { |
296 | u32 field = rule->fields[i] & ~AUDIT_OPERATORS; | 548 | struct audit_field *f = &rule->fields[i]; |
297 | u32 op = rule->fields[i] & AUDIT_OPERATORS; | ||
298 | u32 value = rule->values[i]; | ||
299 | int result = 0; | 549 | int result = 0; |
300 | 550 | ||
301 | switch (field) { | 551 | switch (f->type) { |
302 | case AUDIT_PID: | 552 | case AUDIT_PID: |
303 | result = audit_comparator(cb->creds.pid, op, value); | 553 | result = audit_comparator(cb->creds.pid, f->op, f->val); |
304 | break; | 554 | break; |
305 | case AUDIT_UID: | 555 | case AUDIT_UID: |
306 | result = audit_comparator(cb->creds.uid, op, value); | 556 | result = audit_comparator(cb->creds.uid, f->op, f->val); |
307 | break; | 557 | break; |
308 | case AUDIT_GID: | 558 | case AUDIT_GID: |
309 | result = audit_comparator(cb->creds.gid, op, value); | 559 | result = audit_comparator(cb->creds.gid, f->op, f->val); |
310 | break; | 560 | break; |
311 | case AUDIT_LOGINUID: | 561 | case AUDIT_LOGINUID: |
312 | result = audit_comparator(cb->loginuid, op, value); | 562 | result = audit_comparator(cb->loginuid, f->op, f->val); |
313 | break; | 563 | break; |
314 | } | 564 | } |
315 | 565 | ||
@@ -354,14 +604,11 @@ int audit_filter_type(int type) | |||
354 | 604 | ||
355 | list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], | 605 | list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], |
356 | list) { | 606 | list) { |
357 | struct audit_rule *rule = &e->rule; | ||
358 | int i; | 607 | int i; |
359 | for (i = 0; i < rule->field_count; i++) { | 608 | for (i = 0; i < e->rule.field_count; i++) { |
360 | u32 field = rule->fields[i] & ~AUDIT_OPERATORS; | 609 | struct audit_field *f = &e->rule.fields[i]; |
361 | u32 op = rule->fields[i] & AUDIT_OPERATORS; | 610 | if (f->type == AUDIT_MSGTYPE) { |
362 | u32 value = rule->values[i]; | 611 | result = audit_comparator(type, f->op, f->val); |
363 | if ( field == AUDIT_MSGTYPE ) { | ||
364 | result = audit_comparator(type, op, value); | ||
365 | if (!result) | 612 | if (!result) |
366 | break; | 613 | break; |
367 | } | 614 | } |