diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-01 09:59:08 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-01 09:59:08 -0700 |
commit | fc25465f09414538afdbceacc517dd4dbabadeca (patch) | |
tree | 0e1b051a7ee108d3062949094da55626729d0a5b /kernel | |
parent | b4bc7b53ccfa0cb793591ba11af49db8f1bc5a4d (diff) | |
parent | b915543b46a2aa599fdd2169e51bcfd88812a12b (diff) |
Merge branch 'audit.b22' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current
* 'audit.b22' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current:
[PATCH] audit syscall classes
[PATCH] audit: support for object context filters
[PATCH] audit: rename AUDIT_SE_* constants
[PATCH] add rule filterkey
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/audit.h | 1 | ||||
-rw-r--r-- | kernel/auditfilter.c | 209 | ||||
-rw-r--r-- | kernel/auditsc.c | 65 |
3 files changed, 209 insertions, 66 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 { u32 mask[AUDIT_BITMASK_SIZE]; u32 buflen; /* for data alloc on list rules */ u32 field_count; + char *filterkey; /* ties events to rules */ struct audit_field *fields; struct audit_field *inode_f; /* quick access to an inode field */ struct audit_watch *watch; /* associated watch */ diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 4c99d2c586ed..5b4e16276ca0 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -141,6 +141,7 @@ static inline void audit_free_rule(struct audit_entry *e) selinux_audit_rule_free(f->se_rule); } kfree(e->rule.fields); + kfree(e->rule.filterkey); kfree(e); } @@ -278,6 +279,29 @@ static int audit_to_watch(struct audit_krule *krule, char *path, int len, return 0; } +static __u32 *classes[AUDIT_SYSCALL_CLASSES]; + +int __init audit_register_class(int class, unsigned *list) +{ + __u32 *p = kzalloc(AUDIT_BITMASK_SIZE * sizeof(__u32), GFP_KERNEL); + if (!p) + return -ENOMEM; + while (*list != ~0U) { + unsigned n = *list++; + if (n >= AUDIT_BITMASK_SIZE * 32 - AUDIT_SYSCALL_CLASSES) { + kfree(p); + return -EINVAL; + } + p[AUDIT_WORD(n)] |= AUDIT_BIT(n); + } + if (class >= AUDIT_SYSCALL_CLASSES || classes[class]) { + kfree(p); + return -EINVAL; + } + classes[class] = p; + return 0; +} + /* Common user-space to kernel rule translation. */ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule) { @@ -321,6 +345,22 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule) for (i = 0; i < AUDIT_BITMASK_SIZE; i++) entry->rule.mask[i] = rule->mask[i]; + for (i = 0; i < AUDIT_SYSCALL_CLASSES; i++) { + int bit = AUDIT_BITMASK_SIZE * 32 - i - 1; + __u32 *p = &entry->rule.mask[AUDIT_WORD(bit)]; + __u32 *class; + + if (!(*p & AUDIT_BIT(bit))) + continue; + *p &= ~AUDIT_BIT(bit); + class = classes[i]; + if (class) { + int j; + for (j = 0; j < AUDIT_BITMASK_SIZE; j++) + entry->rule.mask[j] |= class[j]; + } + } + return entry; exit_err: @@ -469,11 +509,16 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, case AUDIT_ARG2: case AUDIT_ARG3: break; - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: str = audit_unpack_string(&bufp, &remain, f->val); if (IS_ERR(str)) goto exit_free; @@ -511,6 +556,16 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, if (err) goto exit_free; break; + case AUDIT_FILTERKEY: + err = -EINVAL; + if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN) + goto exit_free; + str = audit_unpack_string(&bufp, &remain, f->val); + if (IS_ERR(str)) + goto exit_free; + entry->rule.buflen += f->val; + entry->rule.filterkey = str; + break; default: goto exit_free; } @@ -600,11 +655,16 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) data->fields[i] = f->type; data->fieldflags[i] = f->op; switch(f->type) { - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: data->buflen += data->values[i] = audit_pack_string(&bufp, f->se_str); break; @@ -612,6 +672,10 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) data->buflen += data->values[i] = audit_pack_string(&bufp, krule->watch->path); break; + case AUDIT_FILTERKEY: + data->buflen += data->values[i] = + audit_pack_string(&bufp, krule->filterkey); + break; default: data->values[i] = f->val; } @@ -639,11 +703,16 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) return 1; switch(a->fields[i].type) { - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: if (strcmp(a->fields[i].se_str, b->fields[i].se_str)) return 1; break; @@ -651,6 +720,11 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) if (strcmp(a->watch->path, b->watch->path)) return 1; break; + case AUDIT_FILTERKEY: + /* both filterkeys exist based on above type compare */ + if (strcmp(a->filterkey, b->filterkey)) + return 1; + break; default: if (a->fields[i].val != b->fields[i].val) return 1; @@ -730,6 +804,7 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old, u32 fcount = old->field_count; struct audit_entry *entry; struct audit_krule *new; + char *fk; int i, err = 0; entry = audit_init_entry(fcount); @@ -753,13 +828,25 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old, * the originals will all be freed when the old rule is freed. */ for (i = 0; i < fcount; i++) { switch (new->fields[i].type) { - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: err = audit_dupe_selinux_field(&new->fields[i], &old->fields[i]); + break; + case AUDIT_FILTERKEY: + fk = kstrdup(old->filterkey, GFP_KERNEL); + if (unlikely(!fk)) + err = -ENOMEM; + else + new->filterkey = fk; } if (err) { audit_free_rule(entry); @@ -1245,6 +1332,34 @@ static void audit_list_rules(int pid, int seq, struct sk_buff_head *q) skb_queue_tail(q, skb); } +/* Log rule additions and removals */ +static void audit_log_rule_change(uid_t loginuid, u32 sid, char *action, + struct audit_krule *rule, int res) +{ + struct audit_buffer *ab; + + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (!ab) + return; + audit_log_format(ab, "auid=%u", loginuid); + if (sid) { + char *ctx = NULL; + u32 len; + if (selinux_ctxid_to_string(sid, &ctx, &len)) + audit_log_format(ab, " ssid=%u", sid); + else + audit_log_format(ab, " subj=%s", ctx); + kfree(ctx); + } + audit_log_format(ab, " %s rule key=", action); + if (rule->filterkey) + audit_log_untrustedstring(ab, rule->filterkey); + else + audit_log_format(ab, "(null)"); + audit_log_format(ab, " list=%d res=%d", rule->listnr, res); + audit_log_end(ab); +} + /** * audit_receive_filter - apply all rules to the specified message type * @type: audit message type @@ -1304,24 +1419,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, err = audit_add_rule(entry, &audit_filter_list[entry->rule.listnr]); - - if (sid) { - char *ctx = NULL; - u32 len; - if (selinux_ctxid_to_string(sid, &ctx, &len)) { - /* Maybe call audit_panic? */ - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u ssid=%u add rule to list=%d res=%d", - loginuid, sid, entry->rule.listnr, !err); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u subj=%s add rule to list=%d res=%d", - loginuid, ctx, entry->rule.listnr, !err); - kfree(ctx); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u add rule to list=%d res=%d", - loginuid, entry->rule.listnr, !err); + audit_log_rule_change(loginuid, sid, "add", &entry->rule, !err); if (err) audit_free_rule(entry); @@ -1337,24 +1435,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, err = audit_del_rule(entry, &audit_filter_list[entry->rule.listnr]); - - if (sid) { - char *ctx = NULL; - u32 len; - if (selinux_ctxid_to_string(sid, &ctx, &len)) { - /* Maybe call audit_panic? */ - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u ssid=%u remove rule from list=%d res=%d", - loginuid, sid, entry->rule.listnr, !err); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u subj=%s remove rule from list=%d res=%d", - loginuid, ctx, entry->rule.listnr, !err); - kfree(ctx); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u remove rule from list=%d res=%d", - loginuid, entry->rule.listnr, !err); + audit_log_rule_change(loginuid, sid, "remove", &entry->rule, + !err); audit_free_rule(entry); break; @@ -1514,11 +1596,16 @@ static inline int audit_rule_has_selinux(struct audit_krule *rule) for (i = 0; i < rule->field_count; i++) { struct audit_field *f = &rule->fields[i]; switch (f->type) { - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: return 1; } } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index dc5e3f01efe7..ae40ac8c39e7 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -186,6 +186,7 @@ struct audit_context { int auditable; /* 1 if record should be written */ int name_count; struct audit_names names[AUDIT_NAMES]; + char * filterkey; /* key for rule that triggered record */ struct dentry * pwd; struct vfsmount * pwdmnt; struct audit_context *previous; /* For nested syscalls */ @@ -320,11 +321,11 @@ static int audit_filter_rules(struct task_struct *tsk, if (ctx) result = audit_comparator(ctx->loginuid, f->op, f->val); break; - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: /* NOTE: this may return negative values indicating a temporary error. We simply treat this as a match for now to avoid losing information that @@ -341,6 +342,46 @@ static int audit_filter_rules(struct task_struct *tsk, ctx); } break; + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + /* The above note for AUDIT_SUBJ_USER...AUDIT_SUBJ_CLR + also applies here */ + if (f->se_rule) { + /* Find files that match */ + if (name) { + result = selinux_audit_rule_match( + name->osid, f->type, f->op, + f->se_rule, ctx); + } else if (ctx) { + for (j = 0; j < ctx->name_count; j++) { + if (selinux_audit_rule_match( + ctx->names[j].osid, + f->type, f->op, + f->se_rule, ctx)) { + ++result; + break; + } + } + } + /* Find ipc objects that match */ + if (ctx) { + struct audit_aux_data *aux; + for (aux = ctx->aux; aux; + aux = aux->next) { + if (aux->type == AUDIT_IPC) { + struct audit_aux_data_ipcctl *axi = (void *)aux; + if (selinux_audit_rule_match(axi->osid, f->type, f->op, f->se_rule, ctx)) { + ++result; + break; + } + } + } + } + } + break; case AUDIT_ARG0: case AUDIT_ARG1: case AUDIT_ARG2: @@ -348,11 +389,17 @@ static int audit_filter_rules(struct task_struct *tsk, if (ctx) result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val); break; + case AUDIT_FILTERKEY: + /* ignore this field for filtering */ + result = 1; + break; } if (!result) return 0; } + if (rule->filterkey) + ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC); switch (rule->action) { case AUDIT_NEVER: *state = AUDIT_DISABLED; break; case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; @@ -627,6 +674,7 @@ static inline void audit_free_context(struct audit_context *context) } audit_free_names(context); audit_free_aux(context); + kfree(context->filterkey); kfree(context); context = previous; } while (context); @@ -735,6 +783,11 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts context->euid, context->suid, context->fsuid, context->egid, context->sgid, context->fsgid, tty); audit_log_task_info(ab, tsk); + if (context->filterkey) { + audit_log_format(ab, " key="); + audit_log_untrustedstring(ab, context->filterkey); + } else + audit_log_format(ab, " key=(null)"); audit_log_end(ab); for (aux = context->aux; aux; aux = aux->next) { @@ -1060,6 +1113,8 @@ void audit_syscall_exit(int valid, long return_code) } else { audit_free_names(context); audit_free_aux(context); + kfree(context->filterkey); + context->filterkey = NULL; tsk->audit_context = context; } } |