diff options
author | Amy Griffis <amy.griffis@hp.com> | 2006-06-14 18:45:21 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2006-07-01 05:43:06 -0400 |
commit | 5adc8a6adc91c4c85a64c75a70a619fffc924817 (patch) | |
tree | ace9af6bbc3cf711f43cfd88e834baeb6989ca3f /kernel | |
parent | 9262e9149f346a5443300f8c451b8e7631e81a42 (diff) |
[PATCH] add rule filterkey
Add support for a rule key, which can be used to tie audit records to audit
rules. This is useful when a watched file is accessed through a link or
symlink, as well as for general audit log analysis.
Because this patch uses a string key instead of an integer key, there is a bit
of extra overhead to do the kstrdup() when a rule fires. However, we're also
allocating memory for the audit record buffer, so it's probably not that
significant. I went ahead with a string key because it seems more
user-friendly.
Note that the user must ensure that filterkeys are unique. The kernel only
checks for duplicate rules.
Signed-off-by: Amy Griffis <amy.griffis@hpd.com>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/audit.h | 1 | ||||
-rw-r--r-- | kernel/auditfilter.c | 95 | ||||
-rw-r--r-- | kernel/auditsc.c | 15 |
3 files changed, 75 insertions, 36 deletions
diff --git a/kernel/audit.h b/kernel/audit.h index 8323e4132a33..6aa33b848cf2 100644 --- a/kernel/audit.h +++ b/kernel/audit.h | |||
@@ -81,6 +81,7 @@ struct audit_krule { | |||
81 | u32 mask[AUDIT_BITMASK_SIZE]; | 81 | u32 mask[AUDIT_BITMASK_SIZE]; |
82 | u32 buflen; /* for data alloc on list rules */ | 82 | u32 buflen; /* for data alloc on list rules */ |
83 | u32 field_count; | 83 | u32 field_count; |
84 | char *filterkey; /* ties events to rules */ | ||
84 | struct audit_field *fields; | 85 | struct audit_field *fields; |
85 | struct audit_field *inode_f; /* quick access to an inode field */ | 86 | struct audit_field *inode_f; /* quick access to an inode field */ |
86 | struct audit_watch *watch; /* associated watch */ | 87 | struct audit_watch *watch; /* associated watch */ |
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 4c99d2c586ed..e98db08fc6df 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c | |||
@@ -141,6 +141,7 @@ static inline void audit_free_rule(struct audit_entry *e) | |||
141 | selinux_audit_rule_free(f->se_rule); | 141 | selinux_audit_rule_free(f->se_rule); |
142 | } | 142 | } |
143 | kfree(e->rule.fields); | 143 | kfree(e->rule.fields); |
144 | kfree(e->rule.filterkey); | ||
144 | kfree(e); | 145 | kfree(e); |
145 | } | 146 | } |
146 | 147 | ||
@@ -511,6 +512,16 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, | |||
511 | if (err) | 512 | if (err) |
512 | goto exit_free; | 513 | goto exit_free; |
513 | break; | 514 | break; |
515 | case AUDIT_FILTERKEY: | ||
516 | err = -EINVAL; | ||
517 | if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN) | ||
518 | goto exit_free; | ||
519 | str = audit_unpack_string(&bufp, &remain, f->val); | ||
520 | if (IS_ERR(str)) | ||
521 | goto exit_free; | ||
522 | entry->rule.buflen += f->val; | ||
523 | entry->rule.filterkey = str; | ||
524 | break; | ||
514 | default: | 525 | default: |
515 | goto exit_free; | 526 | goto exit_free; |
516 | } | 527 | } |
@@ -612,6 +623,10 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) | |||
612 | data->buflen += data->values[i] = | 623 | data->buflen += data->values[i] = |
613 | audit_pack_string(&bufp, krule->watch->path); | 624 | audit_pack_string(&bufp, krule->watch->path); |
614 | break; | 625 | break; |
626 | case AUDIT_FILTERKEY: | ||
627 | data->buflen += data->values[i] = | ||
628 | audit_pack_string(&bufp, krule->filterkey); | ||
629 | break; | ||
615 | default: | 630 | default: |
616 | data->values[i] = f->val; | 631 | data->values[i] = f->val; |
617 | } | 632 | } |
@@ -651,6 +666,11 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) | |||
651 | if (strcmp(a->watch->path, b->watch->path)) | 666 | if (strcmp(a->watch->path, b->watch->path)) |
652 | return 1; | 667 | return 1; |
653 | break; | 668 | break; |
669 | case AUDIT_FILTERKEY: | ||
670 | /* both filterkeys exist based on above type compare */ | ||
671 | if (strcmp(a->filterkey, b->filterkey)) | ||
672 | return 1; | ||
673 | break; | ||
654 | default: | 674 | default: |
655 | if (a->fields[i].val != b->fields[i].val) | 675 | if (a->fields[i].val != b->fields[i].val) |
656 | return 1; | 676 | return 1; |
@@ -730,6 +750,7 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old, | |||
730 | u32 fcount = old->field_count; | 750 | u32 fcount = old->field_count; |
731 | struct audit_entry *entry; | 751 | struct audit_entry *entry; |
732 | struct audit_krule *new; | 752 | struct audit_krule *new; |
753 | char *fk; | ||
733 | int i, err = 0; | 754 | int i, err = 0; |
734 | 755 | ||
735 | entry = audit_init_entry(fcount); | 756 | entry = audit_init_entry(fcount); |
@@ -760,6 +781,13 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old, | |||
760 | case AUDIT_SE_CLR: | 781 | case AUDIT_SE_CLR: |
761 | err = audit_dupe_selinux_field(&new->fields[i], | 782 | err = audit_dupe_selinux_field(&new->fields[i], |
762 | &old->fields[i]); | 783 | &old->fields[i]); |
784 | break; | ||
785 | case AUDIT_FILTERKEY: | ||
786 | fk = kstrdup(old->filterkey, GFP_KERNEL); | ||
787 | if (unlikely(!fk)) | ||
788 | err = -ENOMEM; | ||
789 | else | ||
790 | new->filterkey = fk; | ||
763 | } | 791 | } |
764 | if (err) { | 792 | if (err) { |
765 | audit_free_rule(entry); | 793 | audit_free_rule(entry); |
@@ -1245,6 +1273,34 @@ static void audit_list_rules(int pid, int seq, struct sk_buff_head *q) | |||
1245 | skb_queue_tail(q, skb); | 1273 | skb_queue_tail(q, skb); |
1246 | } | 1274 | } |
1247 | 1275 | ||
1276 | /* Log rule additions and removals */ | ||
1277 | static void audit_log_rule_change(uid_t loginuid, u32 sid, char *action, | ||
1278 | struct audit_krule *rule, int res) | ||
1279 | { | ||
1280 | struct audit_buffer *ab; | ||
1281 | |||
1282 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); | ||
1283 | if (!ab) | ||
1284 | return; | ||
1285 | audit_log_format(ab, "auid=%u", loginuid); | ||
1286 | if (sid) { | ||
1287 | char *ctx = NULL; | ||
1288 | u32 len; | ||
1289 | if (selinux_ctxid_to_string(sid, &ctx, &len)) | ||
1290 | audit_log_format(ab, " ssid=%u", sid); | ||
1291 | else | ||
1292 | audit_log_format(ab, " subj=%s", ctx); | ||
1293 | kfree(ctx); | ||
1294 | } | ||
1295 | audit_log_format(ab, " %s rule key=", action); | ||
1296 | if (rule->filterkey) | ||
1297 | audit_log_untrustedstring(ab, rule->filterkey); | ||
1298 | else | ||
1299 | audit_log_format(ab, "(null)"); | ||
1300 | audit_log_format(ab, " list=%d res=%d", rule->listnr, res); | ||
1301 | audit_log_end(ab); | ||
1302 | } | ||
1303 | |||
1248 | /** | 1304 | /** |
1249 | * audit_receive_filter - apply all rules to the specified message type | 1305 | * audit_receive_filter - apply all rules to the specified message type |
1250 | * @type: audit message type | 1306 | * @type: audit message type |
@@ -1304,24 +1360,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
1304 | 1360 | ||
1305 | err = audit_add_rule(entry, | 1361 | err = audit_add_rule(entry, |
1306 | &audit_filter_list[entry->rule.listnr]); | 1362 | &audit_filter_list[entry->rule.listnr]); |
1307 | 1363 | audit_log_rule_change(loginuid, sid, "add", &entry->rule, !err); | |
1308 | if (sid) { | ||
1309 | char *ctx = NULL; | ||
1310 | u32 len; | ||
1311 | if (selinux_ctxid_to_string(sid, &ctx, &len)) { | ||
1312 | /* Maybe call audit_panic? */ | ||
1313 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
1314 | "auid=%u ssid=%u add rule to list=%d res=%d", | ||
1315 | loginuid, sid, entry->rule.listnr, !err); | ||
1316 | } else | ||
1317 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
1318 | "auid=%u subj=%s add rule to list=%d res=%d", | ||
1319 | loginuid, ctx, entry->rule.listnr, !err); | ||
1320 | kfree(ctx); | ||
1321 | } else | ||
1322 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
1323 | "auid=%u add rule to list=%d res=%d", | ||
1324 | loginuid, entry->rule.listnr, !err); | ||
1325 | 1364 | ||
1326 | if (err) | 1365 | if (err) |
1327 | audit_free_rule(entry); | 1366 | audit_free_rule(entry); |
@@ -1337,24 +1376,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
1337 | 1376 | ||
1338 | err = audit_del_rule(entry, | 1377 | err = audit_del_rule(entry, |
1339 | &audit_filter_list[entry->rule.listnr]); | 1378 | &audit_filter_list[entry->rule.listnr]); |
1340 | 1379 | audit_log_rule_change(loginuid, sid, "remove", &entry->rule, | |
1341 | if (sid) { | 1380 | !err); |
1342 | char *ctx = NULL; | ||
1343 | u32 len; | ||
1344 | if (selinux_ctxid_to_string(sid, &ctx, &len)) { | ||
1345 | /* Maybe call audit_panic? */ | ||
1346 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
1347 | "auid=%u ssid=%u remove rule from list=%d res=%d", | ||
1348 | loginuid, sid, entry->rule.listnr, !err); | ||
1349 | } else | ||
1350 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
1351 | "auid=%u subj=%s remove rule from list=%d res=%d", | ||
1352 | loginuid, ctx, entry->rule.listnr, !err); | ||
1353 | kfree(ctx); | ||
1354 | } else | ||
1355 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
1356 | "auid=%u remove rule from list=%d res=%d", | ||
1357 | loginuid, entry->rule.listnr, !err); | ||
1358 | 1381 | ||
1359 | audit_free_rule(entry); | 1382 | audit_free_rule(entry); |
1360 | break; | 1383 | break; |
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index dc5e3f01efe7..316657855165 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -186,6 +186,7 @@ struct audit_context { | |||
186 | int auditable; /* 1 if record should be written */ | 186 | int auditable; /* 1 if record should be written */ |
187 | int name_count; | 187 | int name_count; |
188 | struct audit_names names[AUDIT_NAMES]; | 188 | struct audit_names names[AUDIT_NAMES]; |
189 | char * filterkey; /* key for rule that triggered record */ | ||
189 | struct dentry * pwd; | 190 | struct dentry * pwd; |
190 | struct vfsmount * pwdmnt; | 191 | struct vfsmount * pwdmnt; |
191 | struct audit_context *previous; /* For nested syscalls */ | 192 | struct audit_context *previous; /* For nested syscalls */ |
@@ -348,11 +349,17 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
348 | if (ctx) | 349 | if (ctx) |
349 | result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val); | 350 | result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val); |
350 | break; | 351 | break; |
352 | case AUDIT_FILTERKEY: | ||
353 | /* ignore this field for filtering */ | ||
354 | result = 1; | ||
355 | break; | ||
351 | } | 356 | } |
352 | 357 | ||
353 | if (!result) | 358 | if (!result) |
354 | return 0; | 359 | return 0; |
355 | } | 360 | } |
361 | if (rule->filterkey) | ||
362 | ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC); | ||
356 | switch (rule->action) { | 363 | switch (rule->action) { |
357 | case AUDIT_NEVER: *state = AUDIT_DISABLED; break; | 364 | case AUDIT_NEVER: *state = AUDIT_DISABLED; break; |
358 | case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; | 365 | case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; |
@@ -627,6 +634,7 @@ static inline void audit_free_context(struct audit_context *context) | |||
627 | } | 634 | } |
628 | audit_free_names(context); | 635 | audit_free_names(context); |
629 | audit_free_aux(context); | 636 | audit_free_aux(context); |
637 | kfree(context->filterkey); | ||
630 | kfree(context); | 638 | kfree(context); |
631 | context = previous; | 639 | context = previous; |
632 | } while (context); | 640 | } while (context); |
@@ -735,6 +743,11 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts | |||
735 | context->euid, context->suid, context->fsuid, | 743 | context->euid, context->suid, context->fsuid, |
736 | context->egid, context->sgid, context->fsgid, tty); | 744 | context->egid, context->sgid, context->fsgid, tty); |
737 | audit_log_task_info(ab, tsk); | 745 | audit_log_task_info(ab, tsk); |
746 | if (context->filterkey) { | ||
747 | audit_log_format(ab, " key="); | ||
748 | audit_log_untrustedstring(ab, context->filterkey); | ||
749 | } else | ||
750 | audit_log_format(ab, " key=(null)"); | ||
738 | audit_log_end(ab); | 751 | audit_log_end(ab); |
739 | 752 | ||
740 | for (aux = context->aux; aux; aux = aux->next) { | 753 | for (aux = context->aux; aux; aux = aux->next) { |
@@ -1060,6 +1073,8 @@ void audit_syscall_exit(int valid, long return_code) | |||
1060 | } else { | 1073 | } else { |
1061 | audit_free_names(context); | 1074 | audit_free_names(context); |
1062 | audit_free_aux(context); | 1075 | audit_free_aux(context); |
1076 | kfree(context->filterkey); | ||
1077 | context->filterkey = NULL; | ||
1063 | tsk->audit_context = context; | 1078 | tsk->audit_context = context; |
1064 | } | 1079 | } |
1065 | } | 1080 | } |