📄 auditfilter.c
字号:
ret = -ENOENT; else audit_add_to_parent(krule, parent); /* match get in audit_init_parent or inotify_find_watch */ put_inotify_watch(&parent->wdata); return ret;}/* Add rule to given filterlist if not a duplicate. */static inline int audit_add_rule(struct audit_entry *entry, struct list_head *list){ struct audit_entry *e; struct audit_field *inode_f = entry->rule.inode_f; struct audit_watch *watch = entry->rule.watch; struct nameidata *ndp, *ndw; int h, err, putnd_needed = 0;#ifdef CONFIG_AUDITSYSCALL int dont_count = 0; /* If either of these, don't count towards total */ if (entry->rule.listnr == AUDIT_FILTER_USER || entry->rule.listnr == AUDIT_FILTER_TYPE) dont_count = 1;#endif if (inode_f) { h = audit_hash_ino(inode_f->val); list = &audit_inode_hash[h]; } mutex_lock(&audit_filter_mutex); e = audit_find_rule(entry, list); mutex_unlock(&audit_filter_mutex); if (e) { err = -EEXIST; goto error; } /* Avoid calling path_lookup under audit_filter_mutex. */ if (watch) { err = audit_get_nd(watch->path, &ndp, &ndw); if (err) goto error; putnd_needed = 1; } mutex_lock(&audit_filter_mutex); if (watch) { /* audit_filter_mutex is dropped and re-taken during this call */ err = audit_add_watch(&entry->rule, ndp, ndw); if (err) { mutex_unlock(&audit_filter_mutex); goto error; } h = audit_hash_ino((u32)watch->ino); list = &audit_inode_hash[h]; } if (entry->rule.flags & AUDIT_FILTER_PREPEND) { list_add_rcu(&entry->list, list); entry->rule.flags &= ~AUDIT_FILTER_PREPEND; } else { list_add_tail_rcu(&entry->list, list); }#ifdef CONFIG_AUDITSYSCALL if (!dont_count) audit_n_rules++; if (!audit_match_signal(entry)) audit_signals++;#endif mutex_unlock(&audit_filter_mutex); if (putnd_needed) audit_put_nd(ndp, ndw); return 0;error: if (putnd_needed) audit_put_nd(ndp, ndw); if (watch) audit_put_watch(watch); /* tmp watch, matches initial get */ return err;}/* Remove an existing rule from filterlist. */static inline int audit_del_rule(struct audit_entry *entry, struct list_head *list){ struct audit_entry *e; struct audit_field *inode_f = entry->rule.inode_f; struct audit_watch *watch, *tmp_watch = entry->rule.watch; LIST_HEAD(inotify_list); int h, ret = 0;#ifdef CONFIG_AUDITSYSCALL int dont_count = 0; /* If either of these, don't count towards total */ if (entry->rule.listnr == AUDIT_FILTER_USER || entry->rule.listnr == AUDIT_FILTER_TYPE) dont_count = 1;#endif if (inode_f) { h = audit_hash_ino(inode_f->val); list = &audit_inode_hash[h]; } mutex_lock(&audit_filter_mutex); e = audit_find_rule(entry, list); if (!e) { mutex_unlock(&audit_filter_mutex); ret = -ENOENT; goto out; } watch = e->rule.watch; if (watch) { struct audit_parent *parent = watch->parent; list_del(&e->rule.rlist); if (list_empty(&watch->rules)) { audit_remove_watch(watch); if (list_empty(&parent->watches)) { /* Put parent on the inotify un-registration * 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); } } } 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 */ 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 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_sid_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, " 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 * @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 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, 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, 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); } 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, int type){ 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;}/* Check to see if the rule contains any selinux fields. Returns 1 if there are selinux fields specified in the rule, 0 otherwise. */static inline int audit_rule_has_selinux(struct audit_krule *rule){ int i; for (i = 0; i < rule->field_count; i++) { struct audit_field *f = &rule->fields[i]; switch (f->type) { 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; } } return 0;}/* This function will re-initialize the se_rule field of all applicable rules. * It will traverse the filter lists serarching for rules that contain selinux * specific filter fields. When such a rule is found, it is copied, the * selinux field is re-initialized, and the old rule is replaced with the * updated rule. */int selinux_audit_rule_update(void){ struct audit_entry *entry, *n, *nentry; struct audit_watch *watch; 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 (!audit_rule_has_selinux(&entry->rule)) continue; watch = entry->rule.watch; nentry = audit_dupe_rule(&entry->rule, watch); if (unlikely(IS_ERR(nentry))) { /* save the first error encountered for the * return value */ if (!err) err = PTR_ERR(nentry); audit_panic("error updating selinux 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); } 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 + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -