auditfilter.c
来自「Kernel code of linux kernel」· C语言 代码 · 共 1,851 行 · 第 1/4 页
C
1,851 行
* list. Grab a reference before releasing * audit_filter_mutex, to be released in * audit_inotify_unregister(). */ list_add(&parent->ilist, &inotify_list); get_inotify_watch(&parent->wdata); } } } if (e->rule.tree) audit_remove_tree_rule(&e->rule); list_del_rcu(&e->list); call_rcu(&e->rcu, audit_free_rule_rcu);#ifdef CONFIG_AUDITSYSCALL if (!dont_count) audit_n_rules--; if (!audit_match_signal(entry)) audit_signals--;#endif mutex_unlock(&audit_filter_mutex); if (!list_empty(&inotify_list)) audit_inotify_unregister(&inotify_list);out: if (tmp_watch) audit_put_watch(tmp_watch); /* match initial get */ if (tree) audit_put_tree(tree); /* that's the temporary one */ return ret;}/* List rules using struct audit_rule. Exists for backward * compatibility with userspace. */static void audit_list(int pid, int seq, struct sk_buff_head *q){ struct sk_buff *skb; struct audit_entry *entry; int i; /* This is a blocking read, so use audit_filter_mutex instead of rcu * iterator to sync with list writers. */ for (i=0; i<AUDIT_NR_FILTERS; i++) { list_for_each_entry(entry, &audit_filter_list[i], list) { struct audit_rule *rule; rule = audit_krule_to_rule(&entry->rule); if (unlikely(!rule)) break; skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1, rule, sizeof(*rule)); if (skb) skb_queue_tail(q, skb); kfree(rule); } } for (i = 0; i < AUDIT_INODE_BUCKETS; i++) { list_for_each_entry(entry, &audit_inode_hash[i], list) { struct audit_rule *rule; rule = audit_krule_to_rule(&entry->rule); if (unlikely(!rule)) break; skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1, rule, sizeof(*rule)); if (skb) skb_queue_tail(q, skb); kfree(rule); } } skb = audit_make_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); if (skb) skb_queue_tail(q, skb);}/* List rules using struct audit_rule_data. */static void audit_list_rules(int pid, int seq, struct sk_buff_head *q){ struct sk_buff *skb; struct audit_entry *e; int i; /* This is a blocking read, so use audit_filter_mutex instead of rcu * iterator to sync with list writers. */ for (i=0; i<AUDIT_NR_FILTERS; i++) { list_for_each_entry(e, &audit_filter_list[i], list) { struct audit_rule_data *data; data = audit_krule_to_data(&e->rule); if (unlikely(!data)) break; skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, data, sizeof(*data) + data->buflen); if (skb) skb_queue_tail(q, skb); kfree(data); } } for (i=0; i< AUDIT_INODE_BUCKETS; i++) { list_for_each_entry(e, &audit_inode_hash[i], list) { struct audit_rule_data *data; data = audit_krule_to_data(&e->rule); if (unlikely(!data)) break; skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, data, sizeof(*data) + data->buflen); if (skb) skb_queue_tail(q, skb); kfree(data); } } skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0); if (skb) skb_queue_tail(q, skb);}/* Log rule additions and removals */static void audit_log_rule_change(uid_t loginuid, u32 sessionid, u32 sid, char *action, struct audit_krule *rule, int res){ struct audit_buffer *ab; if (!audit_enabled) return; ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); if (!ab) return; audit_log_format(ab, "auid=%u ses=%u", loginuid, sessionid); if (sid) { char *ctx = NULL; u32 len; if (security_secid_to_secctx(sid, &ctx, &len)) audit_log_format(ab, " ssid=%u", sid); else { audit_log_format(ab, " subj=%s", ctx); security_release_secctx(ctx, len); } } audit_log_format(ab, " op=%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 * @pid: target pid for netlink audit messages * @uid: target uid for netlink audit messages * @seq: netlink audit message sequence (serial) number * @data: payload data * @datasz: size of payload data * @loginuid: loginuid of sender * @sessionid: sessionid for netlink audit message * @sid: SE Linux Security ID of sender */int audit_receive_filter(int type, int pid, int uid, int seq, void *data, size_t datasz, uid_t loginuid, u32 sessionid, u32 sid){ struct task_struct *tsk; struct audit_netlink_list *dest; int err = 0; struct audit_entry *entry; switch (type) { case AUDIT_LIST: case AUDIT_LIST_RULES: /* We can't just spew out the rules here because we might fill * the available socket buffer space and deadlock waiting for * auditctl to read from it... which isn't ever going to * happen if we're actually running in the context of auditctl * trying to _send_ the stuff */ dest = kmalloc(sizeof(struct audit_netlink_list), GFP_KERNEL); if (!dest) return -ENOMEM; dest->pid = pid; skb_queue_head_init(&dest->q); mutex_lock(&audit_filter_mutex); if (type == AUDIT_LIST) audit_list(pid, seq, &dest->q); else audit_list_rules(pid, seq, &dest->q); mutex_unlock(&audit_filter_mutex); tsk = kthread_run(audit_send_list, dest, "audit_send_list"); if (IS_ERR(tsk)) { skb_queue_purge(&dest->q); kfree(dest); err = PTR_ERR(tsk); } break; case AUDIT_ADD: case AUDIT_ADD_RULE: if (type == AUDIT_ADD) entry = audit_rule_to_entry(data); else entry = audit_data_to_entry(data, datasz); if (IS_ERR(entry)) return PTR_ERR(entry); err = audit_add_rule(entry, &audit_filter_list[entry->rule.listnr]); audit_log_rule_change(loginuid, sessionid, sid, "add", &entry->rule, !err); if (err) audit_free_rule(entry); break; case AUDIT_DEL: case AUDIT_DEL_RULE: if (type == AUDIT_DEL) entry = audit_rule_to_entry(data); else entry = audit_data_to_entry(data, datasz); if (IS_ERR(entry)) return PTR_ERR(entry); err = audit_del_rule(entry, &audit_filter_list[entry->rule.listnr]); audit_log_rule_change(loginuid, sessionid, sid, "remove", &entry->rule, !err); audit_free_rule(entry); break; default: return -EINVAL; } return err;}int audit_comparator(const u32 left, const u32 op, const u32 right){ switch (op) { case AUDIT_EQUAL: return (left == right); case AUDIT_NOT_EQUAL: return (left != right); case AUDIT_LESS_THAN: return (left < right); case AUDIT_LESS_THAN_OR_EQUAL: return (left <= right); case AUDIT_GREATER_THAN: return (left > right); case AUDIT_GREATER_THAN_OR_EQUAL: return (left >= right); case AUDIT_BIT_MASK: return (left & right); case AUDIT_BIT_TEST: return ((left & right) == right); } BUG(); return 0;}/* Compare given dentry name with last component in given path, * return of 0 indicates a match. */int audit_compare_dname_path(const char *dname, const char *path, int *dirlen){ int dlen, plen; const char *p; if (!dname || !path) return 1; dlen = strlen(dname); plen = strlen(path); if (plen < dlen) return 1; /* disregard trailing slashes */ p = path + plen - 1; while ((*p == '/') && (p > path)) p--; /* find last path component */ p = p - dlen + 1; if (p < path) return 1; else if (p > path) { if (*--p != '/') return 1; else p++; } /* return length of path's directory component */ if (dirlen) *dirlen = p - path; return strncmp(p, dname, dlen);}static int audit_filter_user_rules(struct netlink_skb_parms *cb, struct audit_krule *rule, enum audit_state *state){ int i; for (i = 0; i < rule->field_count; i++) { struct audit_field *f = &rule->fields[i]; int result = 0; switch (f->type) { case AUDIT_PID: result = audit_comparator(cb->creds.pid, f->op, f->val); break; case AUDIT_UID: result = audit_comparator(cb->creds.uid, f->op, f->val); break; case AUDIT_GID: result = audit_comparator(cb->creds.gid, f->op, f->val); break; case AUDIT_LOGINUID: result = audit_comparator(cb->loginuid, f->op, f->val); break; } if (!result) return 0; } switch (rule->action) { case AUDIT_NEVER: *state = AUDIT_DISABLED; break; case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; } return 1;}int audit_filter_user(struct netlink_skb_parms *cb){ enum audit_state state = AUDIT_DISABLED; struct audit_entry *e; int ret = 1; rcu_read_lock(); list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) { if (audit_filter_user_rules(cb, &e->rule, &state)) { if (state == AUDIT_DISABLED) ret = 0; break; } } rcu_read_unlock(); return ret; /* Audit by default */}int audit_filter_type(int type){ struct audit_entry *e; int result = 0; rcu_read_lock(); if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE])) goto unlock_and_return; list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], list) { int i; for (i = 0; i < e->rule.field_count; i++) { struct audit_field *f = &e->rule.fields[i]; if (f->type == AUDIT_MSGTYPE) { result = audit_comparator(type, f->op, f->val); if (!result) break; } } if (result) goto unlock_and_return; }unlock_and_return: rcu_read_unlock(); return result;}/* This function will re-initialize the lsm_rule field of all applicable rules. * It will traverse the filter lists serarching for rules that contain LSM * specific filter fields. When such a rule is found, it is copied, the * LSM field is re-initialized, and the old rule is replaced with the * updated rule. */int audit_update_lsm_rules(void){ struct audit_entry *entry, *n, *nentry; struct audit_watch *watch; struct audit_tree *tree; int i, err = 0; /* audit_filter_mutex synchronizes the writers */ mutex_lock(&audit_filter_mutex); for (i = 0; i < AUDIT_NR_FILTERS; i++) { list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) { if (!security_audit_rule_known(&entry->rule)) continue; watch = entry->rule.watch; tree = entry->rule.tree; nentry = audit_dupe_rule(&entry->rule, watch); if (IS_ERR(nentry)) { /* save the first error encountered for the * return value */ if (!err) err = PTR_ERR(nentry); audit_panic("error updating LSM filters"); if (watch) list_del(&entry->rule.rlist); list_del_rcu(&entry->list); } else { if (watch) { list_add(&nentry->rule.rlist, &watch->rules); list_del(&entry->rule.rlist); } else if (tree) list_replace_init(&entry->rule.rlist, &nentry->rule.rlist); list_replace_rcu(&entry->list, &nentry->list); } call_rcu(&entry->rcu, audit_free_rule_rcu); } } mutex_unlock(&audit_filter_mutex); return err;}/* Update watch data in audit rules based on inotify events. */void audit_handle_ievent(struct inotify_watch *i_watch, u32 wd, u32 mask, u32 cookie, const char *dname, struct inode *inode){ struct audit_parent *parent; parent = container_of(i_watch, struct audit_parent, wdata); if (mask & (IN_CREATE|IN_MOVED_TO) && inode) audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0); else if (mask & (IN_DELETE|IN_MOVED_FROM)) audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1); /* inotify automatically removes the watch and sends IN_IGNORED */ else if (mask & (IN_DELETE_SELF|IN_UNMOUNT)) audit_remove_parent_watches(parent); /* inotify does not remove the watch, so remove it manually */ else if(mask & IN_MOVE_SELF) { audit_remove_parent_watches(parent); inotify_remove_watch_locked(audit_ih, i_watch); } else if (mask & IN_IGNORED) put_inotify_watch(i_watch);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?