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 | } |
