diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/audit.c | 41 | ||||
-rw-r--r-- | kernel/audit.h | 38 | ||||
-rw-r--r-- | kernel/auditfilter.c | 785 | ||||
-rw-r--r-- | kernel/auditsc.c | 124 |
4 files changed, 900 insertions, 88 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index 0738a4b290e6..0fbf1c116363 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -56,6 +56,7 @@ | |||
56 | #include <linux/skbuff.h> | 56 | #include <linux/skbuff.h> |
57 | #include <linux/netlink.h> | 57 | #include <linux/netlink.h> |
58 | #include <linux/selinux.h> | 58 | #include <linux/selinux.h> |
59 | #include <linux/inotify.h> | ||
59 | 60 | ||
60 | #include "audit.h" | 61 | #include "audit.h" |
61 | 62 | ||
@@ -103,6 +104,12 @@ static atomic_t audit_lost = ATOMIC_INIT(0); | |||
103 | /* The netlink socket. */ | 104 | /* The netlink socket. */ |
104 | static struct sock *audit_sock; | 105 | static struct sock *audit_sock; |
105 | 106 | ||
107 | /* Inotify handle. */ | ||
108 | struct inotify_handle *audit_ih; | ||
109 | |||
110 | /* Hash for inode-based rules */ | ||
111 | struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS]; | ||
112 | |||
106 | /* The audit_freelist is a list of pre-allocated audit buffers (if more | 113 | /* The audit_freelist is a list of pre-allocated audit buffers (if more |
107 | * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of | 114 | * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of |
108 | * being placed on the freelist). */ | 115 | * being placed on the freelist). */ |
@@ -115,10 +122,8 @@ static struct task_struct *kauditd_task; | |||
115 | static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait); | 122 | static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait); |
116 | static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait); | 123 | static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait); |
117 | 124 | ||
118 | /* The netlink socket is only to be read by 1 CPU, which lets us assume | 125 | /* Serialize requests from userspace. */ |
119 | * that list additions and deletions never happen simultaneously in | 126 | static DEFINE_MUTEX(audit_cmd_mutex); |
120 | * auditsc.c */ | ||
121 | DEFINE_MUTEX(audit_netlink_mutex); | ||
122 | 127 | ||
123 | /* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting | 128 | /* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting |
124 | * audit records. Since printk uses a 1024 byte buffer, this buffer | 129 | * audit records. Since printk uses a 1024 byte buffer, this buffer |
@@ -373,8 +378,8 @@ int audit_send_list(void *_dest) | |||
373 | struct sk_buff *skb; | 378 | struct sk_buff *skb; |
374 | 379 | ||
375 | /* wait for parent to finish and send an ACK */ | 380 | /* wait for parent to finish and send an ACK */ |
376 | mutex_lock(&audit_netlink_mutex); | 381 | mutex_lock(&audit_cmd_mutex); |
377 | mutex_unlock(&audit_netlink_mutex); | 382 | mutex_unlock(&audit_cmd_mutex); |
378 | 383 | ||
379 | while ((skb = __skb_dequeue(&dest->q)) != NULL) | 384 | while ((skb = __skb_dequeue(&dest->q)) != NULL) |
380 | netlink_unicast(audit_sock, skb, pid, 0); | 385 | netlink_unicast(audit_sock, skb, pid, 0); |
@@ -665,20 +670,30 @@ static void audit_receive(struct sock *sk, int length) | |||
665 | struct sk_buff *skb; | 670 | struct sk_buff *skb; |
666 | unsigned int qlen; | 671 | unsigned int qlen; |
667 | 672 | ||
668 | mutex_lock(&audit_netlink_mutex); | 673 | mutex_lock(&audit_cmd_mutex); |
669 | 674 | ||
670 | for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) { | 675 | for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) { |
671 | skb = skb_dequeue(&sk->sk_receive_queue); | 676 | skb = skb_dequeue(&sk->sk_receive_queue); |
672 | audit_receive_skb(skb); | 677 | audit_receive_skb(skb); |
673 | kfree_skb(skb); | 678 | kfree_skb(skb); |
674 | } | 679 | } |
675 | mutex_unlock(&audit_netlink_mutex); | 680 | mutex_unlock(&audit_cmd_mutex); |
676 | } | 681 | } |
677 | 682 | ||
683 | #ifdef CONFIG_AUDITSYSCALL | ||
684 | static const struct inotify_operations audit_inotify_ops = { | ||
685 | .handle_event = audit_handle_ievent, | ||
686 | .destroy_watch = audit_free_parent, | ||
687 | }; | ||
688 | #endif | ||
678 | 689 | ||
679 | /* Initialize audit support at boot time. */ | 690 | /* Initialize audit support at boot time. */ |
680 | static int __init audit_init(void) | 691 | static int __init audit_init(void) |
681 | { | 692 | { |
693 | #ifdef CONFIG_AUDITSYSCALL | ||
694 | int i; | ||
695 | #endif | ||
696 | |||
682 | printk(KERN_INFO "audit: initializing netlink socket (%s)\n", | 697 | printk(KERN_INFO "audit: initializing netlink socket (%s)\n", |
683 | audit_default ? "enabled" : "disabled"); | 698 | audit_default ? "enabled" : "disabled"); |
684 | audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive, | 699 | audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive, |
@@ -697,6 +712,16 @@ static int __init audit_init(void) | |||
697 | selinux_audit_set_callback(&selinux_audit_rule_update); | 712 | selinux_audit_set_callback(&selinux_audit_rule_update); |
698 | 713 | ||
699 | audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized"); | 714 | audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized"); |
715 | |||
716 | #ifdef CONFIG_AUDITSYSCALL | ||
717 | audit_ih = inotify_init(&audit_inotify_ops); | ||
718 | if (IS_ERR(audit_ih)) | ||
719 | audit_panic("cannot initialize inotify handle"); | ||
720 | |||
721 | for (i = 0; i < AUDIT_INODE_BUCKETS; i++) | ||
722 | INIT_LIST_HEAD(&audit_inode_hash[i]); | ||
723 | #endif | ||
724 | |||
700 | return 0; | 725 | return 0; |
701 | } | 726 | } |
702 | __initcall(audit_init); | 727 | __initcall(audit_init); |
diff --git a/kernel/audit.h b/kernel/audit.h index 52cb1e31d522..58fa44cb8d01 100644 --- a/kernel/audit.h +++ b/kernel/audit.h | |||
@@ -19,7 +19,6 @@ | |||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/mutex.h> | ||
23 | #include <linux/fs.h> | 22 | #include <linux/fs.h> |
24 | #include <linux/audit.h> | 23 | #include <linux/audit.h> |
25 | #include <linux/skbuff.h> | 24 | #include <linux/skbuff.h> |
@@ -54,6 +53,18 @@ enum audit_state { | |||
54 | }; | 53 | }; |
55 | 54 | ||
56 | /* Rule lists */ | 55 | /* Rule lists */ |
56 | struct audit_parent; | ||
57 | |||
58 | struct audit_watch { | ||
59 | atomic_t count; /* reference count */ | ||
60 | char *path; /* insertion path */ | ||
61 | dev_t dev; /* associated superblock device */ | ||
62 | unsigned long ino; /* associated inode number */ | ||
63 | struct audit_parent *parent; /* associated parent */ | ||
64 | struct list_head wlist; /* entry in parent->watches list */ | ||
65 | struct list_head rules; /* associated rules */ | ||
66 | }; | ||
67 | |||
57 | struct audit_field { | 68 | struct audit_field { |
58 | u32 type; | 69 | u32 type; |
59 | u32 val; | 70 | u32 val; |
@@ -71,6 +82,9 @@ struct audit_krule { | |||
71 | u32 buflen; /* for data alloc on list rules */ | 82 | u32 buflen; /* for data alloc on list rules */ |
72 | u32 field_count; | 83 | u32 field_count; |
73 | struct audit_field *fields; | 84 | struct audit_field *fields; |
85 | struct audit_field *inode_f; /* quick access to an inode field */ | ||
86 | struct audit_watch *watch; /* associated watch */ | ||
87 | struct list_head rlist; /* entry in audit_watch.rules list */ | ||
74 | }; | 88 | }; |
75 | 89 | ||
76 | struct audit_entry { | 90 | struct audit_entry { |
@@ -79,10 +93,18 @@ struct audit_entry { | |||
79 | struct audit_krule rule; | 93 | struct audit_krule rule; |
80 | }; | 94 | }; |
81 | 95 | ||
82 | |||
83 | extern int audit_pid; | 96 | extern int audit_pid; |
84 | extern int audit_comparator(const u32 left, const u32 op, const u32 right); | ||
85 | 97 | ||
98 | #define AUDIT_INODE_BUCKETS 32 | ||
99 | extern struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS]; | ||
100 | |||
101 | static inline int audit_hash_ino(u32 ino) | ||
102 | { | ||
103 | return (ino & (AUDIT_INODE_BUCKETS-1)); | ||
104 | } | ||
105 | |||
106 | extern int audit_comparator(const u32 left, const u32 op, const u32 right); | ||
107 | extern int audit_compare_dname_path(const char *dname, const char *path); | ||
86 | extern struct sk_buff * audit_make_reply(int pid, int seq, int type, | 108 | extern struct sk_buff * audit_make_reply(int pid, int seq, int type, |
87 | int done, int multi, | 109 | int done, int multi, |
88 | void *payload, int size); | 110 | void *payload, int size); |
@@ -91,7 +113,6 @@ extern void audit_send_reply(int pid, int seq, int type, | |||
91 | void *payload, int size); | 113 | void *payload, int size); |
92 | extern void audit_log_lost(const char *message); | 114 | extern void audit_log_lost(const char *message); |
93 | extern void audit_panic(const char *message); | 115 | extern void audit_panic(const char *message); |
94 | extern struct mutex audit_netlink_mutex; | ||
95 | 116 | ||
96 | struct audit_netlink_list { | 117 | struct audit_netlink_list { |
97 | int pid; | 118 | int pid; |
@@ -100,6 +121,10 @@ struct audit_netlink_list { | |||
100 | 121 | ||
101 | int audit_send_list(void *); | 122 | int audit_send_list(void *); |
102 | 123 | ||
124 | struct inotify_watch; | ||
125 | extern void audit_free_parent(struct inotify_watch *); | ||
126 | extern void audit_handle_ievent(struct inotify_watch *, u32, u32, u32, | ||
127 | const char *, struct inode *); | ||
103 | extern int selinux_audit_rule_update(void); | 128 | extern int selinux_audit_rule_update(void); |
104 | 129 | ||
105 | #ifdef CONFIG_AUDITSYSCALL | 130 | #ifdef CONFIG_AUDITSYSCALL |
@@ -109,6 +134,11 @@ static inline void audit_signal_info(int sig, struct task_struct *t) | |||
109 | if (unlikely(audit_pid && t->tgid == audit_pid)) | 134 | if (unlikely(audit_pid && t->tgid == audit_pid)) |
110 | __audit_signal_info(sig, t); | 135 | __audit_signal_info(sig, t); |
111 | } | 136 | } |
137 | extern enum audit_state audit_filter_inodes(struct task_struct *, | ||
138 | struct audit_context *); | ||
139 | extern void audit_set_auditable(struct audit_context *); | ||
112 | #else | 140 | #else |
113 | #define audit_signal_info(s,t) | 141 | #define audit_signal_info(s,t) |
142 | #define audit_filter_inodes(t,c) AUDIT_DISABLED | ||
143 | #define audit_set_auditable(c) | ||
114 | #endif | 144 | #endif |
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index df9503da40fb..03a6919103d4 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c | |||
@@ -22,13 +22,59 @@ | |||
22 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
23 | #include <linux/audit.h> | 23 | #include <linux/audit.h> |
24 | #include <linux/kthread.h> | 24 | #include <linux/kthread.h> |
25 | #include <linux/mutex.h> | ||
26 | #include <linux/fs.h> | ||
27 | #include <linux/namei.h> | ||
25 | #include <linux/netlink.h> | 28 | #include <linux/netlink.h> |
29 | #include <linux/sched.h> | ||
30 | #include <linux/inotify.h> | ||
26 | #include <linux/selinux.h> | 31 | #include <linux/selinux.h> |
27 | #include "audit.h" | 32 | #include "audit.h" |
28 | 33 | ||
29 | /* There are three lists of rules -- one to search at task creation | 34 | /* |
30 | * time, one to search at syscall entry time, and another to search at | 35 | * Locking model: |
31 | * syscall exit time. */ | 36 | * |
37 | * audit_filter_mutex: | ||
38 | * Synchronizes writes and blocking reads of audit's filterlist | ||
39 | * data. Rcu is used to traverse the filterlist and access | ||
40 | * contents of structs audit_entry, audit_watch and opaque | ||
41 | * selinux rules during filtering. If modified, these structures | ||
42 | * must be copied and replace their counterparts in the filterlist. | ||
43 | * An audit_parent struct is not accessed during filtering, so may | ||
44 | * be written directly provided audit_filter_mutex is held. | ||
45 | */ | ||
46 | |||
47 | /* | ||
48 | * Reference counting: | ||
49 | * | ||
50 | * audit_parent: lifetime is from audit_init_parent() to receipt of an IN_IGNORED | ||
51 | * event. Each audit_watch holds a reference to its associated parent. | ||
52 | * | ||
53 | * audit_watch: if added to lists, lifetime is from audit_init_watch() to | ||
54 | * audit_remove_watch(). Additionally, an audit_watch may exist | ||
55 | * temporarily to assist in searching existing filter data. Each | ||
56 | * audit_krule holds a reference to its associated watch. | ||
57 | */ | ||
58 | |||
59 | struct audit_parent { | ||
60 | struct list_head ilist; /* entry in inotify registration list */ | ||
61 | struct list_head watches; /* associated watches */ | ||
62 | struct inotify_watch wdata; /* inotify watch data */ | ||
63 | unsigned flags; /* status flags */ | ||
64 | }; | ||
65 | |||
66 | /* | ||
67 | * audit_parent status flags: | ||
68 | * | ||
69 | * AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to | ||
70 | * a filesystem event to ensure we're adding audit watches to a valid parent. | ||
71 | * Technically not needed for IN_DELETE_SELF or IN_UNMOUNT events, as we cannot | ||
72 | * receive them while we have nameidata, but must be used for IN_MOVE_SELF which | ||
73 | * we can receive while holding nameidata. | ||
74 | */ | ||
75 | #define AUDIT_PARENT_INVALID 0x001 | ||
76 | |||
77 | /* Audit filter lists, defined in <linux/audit.h> */ | ||
32 | struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { | 78 | struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { |
33 | LIST_HEAD_INIT(audit_filter_list[0]), | 79 | LIST_HEAD_INIT(audit_filter_list[0]), |
34 | LIST_HEAD_INIT(audit_filter_list[1]), | 80 | LIST_HEAD_INIT(audit_filter_list[1]), |
@@ -41,9 +87,53 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { | |||
41 | #endif | 87 | #endif |
42 | }; | 88 | }; |
43 | 89 | ||
90 | static DEFINE_MUTEX(audit_filter_mutex); | ||
91 | |||
92 | /* Inotify handle */ | ||
93 | extern struct inotify_handle *audit_ih; | ||
94 | |||
95 | /* Inotify events we care about. */ | ||
96 | #define AUDIT_IN_WATCH IN_MOVE|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF | ||
97 | |||
98 | void audit_free_parent(struct inotify_watch *i_watch) | ||
99 | { | ||
100 | struct audit_parent *parent; | ||
101 | |||
102 | parent = container_of(i_watch, struct audit_parent, wdata); | ||
103 | WARN_ON(!list_empty(&parent->watches)); | ||
104 | kfree(parent); | ||
105 | } | ||
106 | |||
107 | static inline void audit_get_watch(struct audit_watch *watch) | ||
108 | { | ||
109 | atomic_inc(&watch->count); | ||
110 | } | ||
111 | |||
112 | static void audit_put_watch(struct audit_watch *watch) | ||
113 | { | ||
114 | if (atomic_dec_and_test(&watch->count)) { | ||
115 | WARN_ON(watch->parent); | ||
116 | WARN_ON(!list_empty(&watch->rules)); | ||
117 | kfree(watch->path); | ||
118 | kfree(watch); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | static void audit_remove_watch(struct audit_watch *watch) | ||
123 | { | ||
124 | list_del(&watch->wlist); | ||
125 | put_inotify_watch(&watch->parent->wdata); | ||
126 | watch->parent = NULL; | ||
127 | audit_put_watch(watch); /* match initial get */ | ||
128 | } | ||
129 | |||
44 | static inline void audit_free_rule(struct audit_entry *e) | 130 | static inline void audit_free_rule(struct audit_entry *e) |
45 | { | 131 | { |
46 | int i; | 132 | int i; |
133 | |||
134 | /* some rules don't have associated watches */ | ||
135 | if (e->rule.watch) | ||
136 | audit_put_watch(e->rule.watch); | ||
47 | if (e->rule.fields) | 137 | if (e->rule.fields) |
48 | for (i = 0; i < e->rule.field_count; i++) { | 138 | for (i = 0; i < e->rule.field_count; i++) { |
49 | struct audit_field *f = &e->rule.fields[i]; | 139 | struct audit_field *f = &e->rule.fields[i]; |
@@ -60,6 +150,50 @@ static inline void audit_free_rule_rcu(struct rcu_head *head) | |||
60 | audit_free_rule(e); | 150 | audit_free_rule(e); |
61 | } | 151 | } |
62 | 152 | ||
153 | /* Initialize a parent watch entry. */ | ||
154 | static struct audit_parent *audit_init_parent(struct nameidata *ndp) | ||
155 | { | ||
156 | struct audit_parent *parent; | ||
157 | s32 wd; | ||
158 | |||
159 | parent = kzalloc(sizeof(*parent), GFP_KERNEL); | ||
160 | if (unlikely(!parent)) | ||
161 | return ERR_PTR(-ENOMEM); | ||
162 | |||
163 | INIT_LIST_HEAD(&parent->watches); | ||
164 | parent->flags = 0; | ||
165 | |||
166 | inotify_init_watch(&parent->wdata); | ||
167 | /* grab a ref so inotify watch hangs around until we take audit_filter_mutex */ | ||
168 | get_inotify_watch(&parent->wdata); | ||
169 | wd = inotify_add_watch(audit_ih, &parent->wdata, ndp->dentry->d_inode, | ||
170 | AUDIT_IN_WATCH); | ||
171 | if (wd < 0) { | ||
172 | audit_free_parent(&parent->wdata); | ||
173 | return ERR_PTR(wd); | ||
174 | } | ||
175 | |||
176 | return parent; | ||
177 | } | ||
178 | |||
179 | /* Initialize a watch entry. */ | ||
180 | static struct audit_watch *audit_init_watch(char *path) | ||
181 | { | ||
182 | struct audit_watch *watch; | ||
183 | |||
184 | watch = kzalloc(sizeof(*watch), GFP_KERNEL); | ||
185 | if (unlikely(!watch)) | ||
186 | return ERR_PTR(-ENOMEM); | ||
187 | |||
188 | INIT_LIST_HEAD(&watch->rules); | ||
189 | atomic_set(&watch->count, 1); | ||
190 | watch->path = path; | ||
191 | watch->dev = (dev_t)-1; | ||
192 | watch->ino = (unsigned long)-1; | ||
193 | |||
194 | return watch; | ||
195 | } | ||
196 | |||
63 | /* Initialize an audit filterlist entry. */ | 197 | /* Initialize an audit filterlist entry. */ |
64 | static inline struct audit_entry *audit_init_entry(u32 field_count) | 198 | static inline struct audit_entry *audit_init_entry(u32 field_count) |
65 | { | 199 | { |
@@ -107,6 +241,43 @@ static char *audit_unpack_string(void **bufp, size_t *remain, size_t len) | |||
107 | return str; | 241 | return str; |
108 | } | 242 | } |
109 | 243 | ||
244 | /* Translate an inode field to kernel respresentation. */ | ||
245 | static inline int audit_to_inode(struct audit_krule *krule, | ||
246 | struct audit_field *f) | ||
247 | { | ||
248 | if (krule->listnr != AUDIT_FILTER_EXIT || | ||
249 | krule->watch || krule->inode_f) | ||
250 | return -EINVAL; | ||
251 | |||
252 | krule->inode_f = f; | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | /* Translate a watch string to kernel respresentation. */ | ||
257 | static int audit_to_watch(struct audit_krule *krule, char *path, int len, | ||
258 | u32 op) | ||
259 | { | ||
260 | struct audit_watch *watch; | ||
261 | |||
262 | if (!audit_ih) | ||
263 | return -EOPNOTSUPP; | ||
264 | |||
265 | if (path[0] != '/' || path[len-1] == '/' || | ||
266 | krule->listnr != AUDIT_FILTER_EXIT || | ||
267 | op & ~AUDIT_EQUAL || | ||
268 | krule->inode_f || krule->watch) /* 1 inode # per rule, for hash */ | ||
269 | return -EINVAL; | ||
270 | |||
271 | watch = audit_init_watch(path); | ||
272 | if (unlikely(IS_ERR(watch))) | ||
273 | return PTR_ERR(watch); | ||
274 | |||
275 | audit_get_watch(watch); | ||
276 | krule->watch = watch; | ||
277 | |||
278 | return 0; | ||
279 | } | ||
280 | |||
110 | /* Common user-space to kernel rule translation. */ | 281 | /* Common user-space to kernel rule translation. */ |
111 | static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule) | 282 | static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule) |
112 | { | 283 | { |
@@ -161,6 +332,7 @@ exit_err: | |||
161 | static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) | 332 | static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) |
162 | { | 333 | { |
163 | struct audit_entry *entry; | 334 | struct audit_entry *entry; |
335 | struct audit_field *f; | ||
164 | int err = 0; | 336 | int err = 0; |
165 | int i; | 337 | int i; |
166 | 338 | ||
@@ -175,14 +347,23 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) | |||
175 | f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); | 347 | f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); |
176 | f->val = rule->values[i]; | 348 | f->val = rule->values[i]; |
177 | 349 | ||
178 | if (f->type & AUDIT_UNUSED_BITS || | 350 | err = -EINVAL; |
179 | f->type == AUDIT_SE_USER || | 351 | if (f->type & AUDIT_UNUSED_BITS) |
180 | f->type == AUDIT_SE_ROLE || | 352 | goto exit_free; |
181 | f->type == AUDIT_SE_TYPE || | 353 | |
182 | f->type == AUDIT_SE_SEN || | 354 | switch(f->type) { |
183 | f->type == AUDIT_SE_CLR) { | 355 | case AUDIT_SE_USER: |
184 | err = -EINVAL; | 356 | case AUDIT_SE_ROLE: |
357 | case AUDIT_SE_TYPE: | ||
358 | case AUDIT_SE_SEN: | ||
359 | case AUDIT_SE_CLR: | ||
360 | case AUDIT_WATCH: | ||
185 | goto exit_free; | 361 | goto exit_free; |
362 | case AUDIT_INODE: | ||
363 | err = audit_to_inode(&entry->rule, f); | ||
364 | if (err) | ||
365 | goto exit_free; | ||
366 | break; | ||
186 | } | 367 | } |
187 | 368 | ||
188 | entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1; | 369 | entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1; |
@@ -199,6 +380,18 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) | |||
199 | } | 380 | } |
200 | } | 381 | } |
201 | 382 | ||
383 | f = entry->rule.inode_f; | ||
384 | if (f) { | ||
385 | switch(f->op) { | ||
386 | case AUDIT_NOT_EQUAL: | ||
387 | entry->rule.inode_f = NULL; | ||
388 | case AUDIT_EQUAL: | ||
389 | break; | ||
390 | default: | ||
391 | goto exit_free; | ||
392 | } | ||
393 | } | ||
394 | |||
202 | exit_nofree: | 395 | exit_nofree: |
203 | return entry; | 396 | return entry; |
204 | 397 | ||
@@ -213,6 +406,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, | |||
213 | { | 406 | { |
214 | int err = 0; | 407 | int err = 0; |
215 | struct audit_entry *entry; | 408 | struct audit_entry *entry; |
409 | struct audit_field *f; | ||
216 | void *bufp; | 410 | void *bufp; |
217 | size_t remain = datasz - sizeof(struct audit_rule_data); | 411 | size_t remain = datasz - sizeof(struct audit_rule_data); |
218 | int i; | 412 | int i; |
@@ -263,6 +457,35 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, | |||
263 | } else | 457 | } else |
264 | f->se_str = str; | 458 | f->se_str = str; |
265 | break; | 459 | break; |
460 | case AUDIT_WATCH: | ||
461 | str = audit_unpack_string(&bufp, &remain, f->val); | ||
462 | if (IS_ERR(str)) | ||
463 | goto exit_free; | ||
464 | entry->rule.buflen += f->val; | ||
465 | |||
466 | err = audit_to_watch(&entry->rule, str, f->val, f->op); | ||
467 | if (err) { | ||
468 | kfree(str); | ||
469 | goto exit_free; | ||
470 | } | ||
471 | break; | ||
472 | case AUDIT_INODE: | ||
473 | err = audit_to_inode(&entry->rule, f); | ||
474 | if (err) | ||
475 | goto exit_free; | ||
476 | break; | ||
477 | } | ||
478 | } | ||
479 | |||
480 | f = entry->rule.inode_f; | ||
481 | if (f) { | ||
482 | switch(f->op) { | ||
483 | case AUDIT_NOT_EQUAL: | ||
484 | entry->rule.inode_f = NULL; | ||
485 | case AUDIT_EQUAL: | ||
486 | break; | ||
487 | default: | ||
488 | goto exit_free; | ||
266 | } | 489 | } |
267 | } | 490 | } |
268 | 491 | ||
@@ -346,6 +569,10 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) | |||
346 | data->buflen += data->values[i] = | 569 | data->buflen += data->values[i] = |
347 | audit_pack_string(&bufp, f->se_str); | 570 | audit_pack_string(&bufp, f->se_str); |
348 | break; | 571 | break; |
572 | case AUDIT_WATCH: | ||
573 | data->buflen += data->values[i] = | ||
574 | audit_pack_string(&bufp, krule->watch->path); | ||
575 | break; | ||
349 | default: | 576 | default: |
350 | data->values[i] = f->val; | 577 | data->values[i] = f->val; |
351 | } | 578 | } |
@@ -381,6 +608,10 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) | |||
381 | if (strcmp(a->fields[i].se_str, b->fields[i].se_str)) | 608 | if (strcmp(a->fields[i].se_str, b->fields[i].se_str)) |
382 | return 1; | 609 | return 1; |
383 | break; | 610 | break; |
611 | case AUDIT_WATCH: | ||
612 | if (strcmp(a->watch->path, b->watch->path)) | ||
613 | return 1; | ||
614 | break; | ||
384 | default: | 615 | default: |
385 | if (a->fields[i].val != b->fields[i].val) | 616 | if (a->fields[i].val != b->fields[i].val) |
386 | return 1; | 617 | return 1; |
@@ -394,6 +625,32 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) | |||
394 | return 0; | 625 | return 0; |
395 | } | 626 | } |
396 | 627 | ||
628 | /* Duplicate the given audit watch. The new watch's rules list is initialized | ||
629 | * to an empty list and wlist is undefined. */ | ||
630 | static struct audit_watch *audit_dupe_watch(struct audit_watch *old) | ||
631 | { | ||
632 | char *path; | ||
633 | struct audit_watch *new; | ||
634 | |||
635 | path = kstrdup(old->path, GFP_KERNEL); | ||
636 | if (unlikely(!path)) | ||
637 | return ERR_PTR(-ENOMEM); | ||
638 | |||
639 | new = audit_init_watch(path); | ||
640 | if (unlikely(IS_ERR(new))) { | ||
641 | kfree(path); | ||
642 | goto out; | ||
643 | } | ||
644 | |||
645 | new->dev = old->dev; | ||
646 | new->ino = old->ino; | ||
647 | get_inotify_watch(&old->parent->wdata); | ||
648 | new->parent = old->parent; | ||
649 | |||
650 | out: | ||
651 | return new; | ||
652 | } | ||
653 | |||
397 | /* Duplicate selinux field information. The se_rule is opaque, so must be | 654 | /* Duplicate selinux field information. The se_rule is opaque, so must be |
398 | * re-initialized. */ | 655 | * re-initialized. */ |
399 | static inline int audit_dupe_selinux_field(struct audit_field *df, | 656 | static inline int audit_dupe_selinux_field(struct audit_field *df, |
@@ -425,8 +682,11 @@ static inline int audit_dupe_selinux_field(struct audit_field *df, | |||
425 | /* Duplicate an audit rule. This will be a deep copy with the exception | 682 | /* Duplicate an audit rule. This will be a deep copy with the exception |
426 | * of the watch - that pointer is carried over. The selinux specific fields | 683 | * of the watch - that pointer is carried over. The selinux specific fields |
427 | * will be updated in the copy. The point is to be able to replace the old | 684 | * will be updated in the copy. The point is to be able to replace the old |
428 | * rule with the new rule in the filterlist, then free the old rule. */ | 685 | * rule with the new rule in the filterlist, then free the old rule. |
429 | static struct audit_entry *audit_dupe_rule(struct audit_krule *old) | 686 | * The rlist element is undefined; list manipulations are handled apart from |
687 | * the initial copy. */ | ||
688 | static struct audit_entry *audit_dupe_rule(struct audit_krule *old, | ||
689 | struct audit_watch *watch) | ||
430 | { | 690 | { |
431 | u32 fcount = old->field_count; | 691 | u32 fcount = old->field_count; |
432 | struct audit_entry *entry; | 692 | struct audit_entry *entry; |
@@ -445,6 +705,8 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old) | |||
445 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) | 705 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) |
446 | new->mask[i] = old->mask[i]; | 706 | new->mask[i] = old->mask[i]; |
447 | new->buflen = old->buflen; | 707 | new->buflen = old->buflen; |
708 | new->inode_f = old->inode_f; | ||
709 | new->watch = NULL; | ||
448 | new->field_count = old->field_count; | 710 | new->field_count = old->field_count; |
449 | memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount); | 711 | memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount); |
450 | 712 | ||
@@ -466,21 +728,318 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old) | |||
466 | } | 728 | } |
467 | } | 729 | } |
468 | 730 | ||
731 | if (watch) { | ||
732 | audit_get_watch(watch); | ||
733 | new->watch = watch; | ||
734 | } | ||
735 | |||
469 | return entry; | 736 | return entry; |
470 | } | 737 | } |
471 | 738 | ||
472 | /* Add rule to given filterlist if not a duplicate. Protected by | 739 | /* Update inode info in audit rules based on filesystem event. */ |
473 | * audit_netlink_mutex. */ | 740 | static void audit_update_watch(struct audit_parent *parent, |
741 | const char *dname, dev_t dev, | ||
742 | unsigned long ino, unsigned invalidating) | ||
743 | { | ||
744 | struct audit_watch *owatch, *nwatch, *nextw; | ||
745 | struct audit_krule *r, *nextr; | ||
746 | struct audit_entry *oentry, *nentry; | ||
747 | struct audit_buffer *ab; | ||
748 | |||
749 | mutex_lock(&audit_filter_mutex); | ||
750 | list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) { | ||
751 | if (audit_compare_dname_path(dname, owatch->path)) | ||
752 | continue; | ||
753 | |||
754 | /* If the update involves invalidating rules, do the inode-based | ||
755 | * filtering now, so we don't omit records. */ | ||
756 | if (invalidating && | ||
757 | audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT) | ||
758 | audit_set_auditable(current->audit_context); | ||
759 | |||
760 | nwatch = audit_dupe_watch(owatch); | ||
761 | if (unlikely(IS_ERR(nwatch))) { | ||
762 | mutex_unlock(&audit_filter_mutex); | ||
763 | audit_panic("error updating watch, skipping"); | ||
764 | return; | ||
765 | } | ||
766 | nwatch->dev = dev; | ||
767 | nwatch->ino = ino; | ||
768 | |||
769 | list_for_each_entry_safe(r, nextr, &owatch->rules, rlist) { | ||
770 | |||
771 | oentry = container_of(r, struct audit_entry, rule); | ||
772 | list_del(&oentry->rule.rlist); | ||
773 | list_del_rcu(&oentry->list); | ||
774 | |||
775 | nentry = audit_dupe_rule(&oentry->rule, nwatch); | ||
776 | if (unlikely(IS_ERR(nentry))) | ||
777 | audit_panic("error updating watch, removing"); | ||
778 | else { | ||
779 | int h = audit_hash_ino((u32)ino); | ||
780 | list_add(&nentry->rule.rlist, &nwatch->rules); | ||
781 | list_add_rcu(&nentry->list, &audit_inode_hash[h]); | ||
782 | } | ||
783 | |||
784 | call_rcu(&oentry->rcu, audit_free_rule_rcu); | ||
785 | } | ||
786 | |||
787 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); | ||
788 | audit_log_format(ab, "audit updated rules specifying watch="); | ||
789 | audit_log_untrustedstring(ab, owatch->path); | ||
790 | audit_log_format(ab, " with dev=%u ino=%lu\n", dev, ino); | ||
791 | audit_log_end(ab); | ||
792 | |||
793 | audit_remove_watch(owatch); | ||
794 | goto add_watch_to_parent; /* event applies to a single watch */ | ||
795 | } | ||
796 | mutex_unlock(&audit_filter_mutex); | ||
797 | return; | ||
798 | |||
799 | add_watch_to_parent: | ||
800 | list_add(&nwatch->wlist, &parent->watches); | ||
801 | mutex_unlock(&audit_filter_mutex); | ||
802 | return; | ||
803 | } | ||
804 | |||
805 | /* Remove all watches & rules associated with a parent that is going away. */ | ||
806 | static void audit_remove_parent_watches(struct audit_parent *parent) | ||
807 | { | ||
808 | struct audit_watch *w, *nextw; | ||
809 | struct audit_krule *r, *nextr; | ||
810 | struct audit_entry *e; | ||
811 | |||
812 | mutex_lock(&audit_filter_mutex); | ||
813 | parent->flags |= AUDIT_PARENT_INVALID; | ||
814 | list_for_each_entry_safe(w, nextw, &parent->watches, wlist) { | ||
815 | list_for_each_entry_safe(r, nextr, &w->rules, rlist) { | ||
816 | e = container_of(r, struct audit_entry, rule); | ||
817 | list_del(&r->rlist); | ||
818 | list_del_rcu(&e->list); | ||
819 | call_rcu(&e->rcu, audit_free_rule_rcu); | ||
820 | |||
821 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
822 | "audit implicitly removed rule from list=%d\n", | ||
823 | AUDIT_FILTER_EXIT); | ||
824 | } | ||
825 | audit_remove_watch(w); | ||
826 | } | ||
827 | mutex_unlock(&audit_filter_mutex); | ||
828 | } | ||
829 | |||
830 | /* Unregister inotify watches for parents on in_list. | ||
831 | * Generates an IN_IGNORED event. */ | ||
832 | static void audit_inotify_unregister(struct list_head *in_list) | ||
833 | { | ||
834 | struct audit_parent *p, *n; | ||
835 | |||
836 | list_for_each_entry_safe(p, n, in_list, ilist) { | ||
837 | list_del(&p->ilist); | ||
838 | inotify_rm_watch(audit_ih, &p->wdata); | ||
839 | /* the put matching the get in audit_do_del_rule() */ | ||
840 | put_inotify_watch(&p->wdata); | ||
841 | } | ||
842 | } | ||
843 | |||
844 | /* Find an existing audit rule. | ||
845 | * Caller must hold audit_filter_mutex to prevent stale rule data. */ | ||
846 | static struct audit_entry *audit_find_rule(struct audit_entry *entry, | ||
847 | struct list_head *list) | ||
848 | { | ||
849 | struct audit_entry *e, *found = NULL; | ||
850 | int h; | ||
851 | |||
852 | if (entry->rule.watch) { | ||
853 | /* we don't know the inode number, so must walk entire hash */ | ||
854 | for (h = 0; h < AUDIT_INODE_BUCKETS; h++) { | ||
855 | list = &audit_inode_hash[h]; | ||
856 | list_for_each_entry(e, list, list) | ||
857 | if (!audit_compare_rule(&entry->rule, &e->rule)) { | ||
858 | found = e; | ||
859 | goto out; | ||
860 | } | ||
861 | } | ||
862 | goto out; | ||
863 | } | ||
864 | |||
865 | list_for_each_entry(e, list, list) | ||
866 | if (!audit_compare_rule(&entry->rule, &e->rule)) { | ||
867 | found = e; | ||
868 | goto out; | ||
869 | } | ||
870 | |||
871 | out: | ||
872 | return found; | ||
873 | } | ||
874 | |||
875 | /* Get path information necessary for adding watches. */ | ||
876 | static int audit_get_nd(char *path, struct nameidata **ndp, | ||
877 | struct nameidata **ndw) | ||
878 | { | ||
879 | struct nameidata *ndparent, *ndwatch; | ||
880 | int err; | ||
881 | |||
882 | ndparent = kmalloc(sizeof(*ndparent), GFP_KERNEL); | ||
883 | if (unlikely(!ndparent)) | ||
884 | return -ENOMEM; | ||
885 | |||
886 | ndwatch = kmalloc(sizeof(*ndwatch), GFP_KERNEL); | ||
887 | if (unlikely(!ndwatch)) { | ||
888 | kfree(ndparent); | ||
889 | return -ENOMEM; | ||
890 | } | ||
891 | |||
892 | err = path_lookup(path, LOOKUP_PARENT, ndparent); | ||
893 | if (err) { | ||
894 | kfree(ndparent); | ||
895 | kfree(ndwatch); | ||
896 | return err; | ||
897 | } | ||
898 | |||
899 | err = path_lookup(path, 0, ndwatch); | ||
900 | if (err) { | ||
901 | kfree(ndwatch); | ||
902 | ndwatch = NULL; | ||
903 | } | ||
904 | |||
905 | *ndp = ndparent; | ||
906 | *ndw = ndwatch; | ||
907 | |||
908 | return 0; | ||
909 | } | ||
910 | |||
911 | /* Release resources used for watch path information. */ | ||
912 | static void audit_put_nd(struct nameidata *ndp, struct nameidata *ndw) | ||
913 | { | ||
914 | if (ndp) { | ||
915 | path_release(ndp); | ||
916 | kfree(ndp); | ||
917 | } | ||
918 | if (ndw) { | ||
919 | path_release(ndw); | ||
920 | kfree(ndw); | ||
921 | } | ||
922 | } | ||
923 | |||
924 | /* Associate the given rule with an existing parent inotify_watch. | ||
925 | * Caller must hold audit_filter_mutex. */ | ||
926 | static void audit_add_to_parent(struct audit_krule *krule, | ||
927 | struct audit_parent *parent) | ||
928 | { | ||
929 | struct audit_watch *w, *watch = krule->watch; | ||
930 | int watch_found = 0; | ||
931 | |||
932 | list_for_each_entry(w, &parent->watches, wlist) { | ||
933 | if (strcmp(watch->path, w->path)) | ||
934 | continue; | ||
935 | |||
936 | watch_found = 1; | ||
937 | |||
938 | /* put krule's and initial refs to temporary watch */ | ||
939 | audit_put_watch(watch); | ||
940 | audit_put_watch(watch); | ||
941 | |||
942 | audit_get_watch(w); | ||
943 | krule->watch = watch = w; | ||
944 | break; | ||
945 | } | ||
946 | |||
947 | if (!watch_found) { | ||
948 | get_inotify_watch(&parent->wdata); | ||
949 | watch->parent = parent; | ||
950 | |||
951 | list_add(&watch->wlist, &parent->watches); | ||
952 | } | ||
953 | list_add(&krule->rlist, &watch->rules); | ||
954 | } | ||
955 | |||
956 | /* Find a matching watch entry, or add this one. | ||
957 | * Caller must hold audit_filter_mutex. */ | ||
958 | static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp, | ||
959 | struct nameidata *ndw) | ||
960 | { | ||
961 | struct audit_watch *watch = krule->watch; | ||
962 | struct inotify_watch *i_watch; | ||
963 | struct audit_parent *parent; | ||
964 | int ret = 0; | ||
965 | |||
966 | /* update watch filter fields */ | ||
967 | if (ndw) { | ||
968 | watch->dev = ndw->dentry->d_inode->i_sb->s_dev; | ||
969 | watch->ino = ndw->dentry->d_inode->i_ino; | ||
970 | } | ||
971 | |||
972 | /* The audit_filter_mutex must not be held during inotify calls because | ||
973 | * we hold it during inotify event callback processing. If an existing | ||
974 | * inotify watch is found, inotify_find_watch() grabs a reference before | ||
975 | * returning. | ||
976 | */ | ||
977 | mutex_unlock(&audit_filter_mutex); | ||
978 | |||
979 | if (inotify_find_watch(audit_ih, ndp->dentry->d_inode, &i_watch) < 0) { | ||
980 | parent = audit_init_parent(ndp); | ||
981 | if (IS_ERR(parent)) { | ||
982 | /* caller expects mutex locked */ | ||
983 | mutex_lock(&audit_filter_mutex); | ||
984 | return PTR_ERR(parent); | ||
985 | } | ||
986 | } else | ||
987 | parent = container_of(i_watch, struct audit_parent, wdata); | ||
988 | |||
989 | mutex_lock(&audit_filter_mutex); | ||
990 | |||
991 | /* parent was moved before we took audit_filter_mutex */ | ||
992 | if (parent->flags & AUDIT_PARENT_INVALID) | ||
993 | ret = -ENOENT; | ||
994 | else | ||
995 | audit_add_to_parent(krule, parent); | ||
996 | |||
997 | /* match get in audit_init_parent or inotify_find_watch */ | ||
998 | put_inotify_watch(&parent->wdata); | ||
999 | return ret; | ||
1000 | } | ||
1001 | |||
1002 | /* Add rule to given filterlist if not a duplicate. */ | ||
474 | static inline int audit_add_rule(struct audit_entry *entry, | 1003 | static inline int audit_add_rule(struct audit_entry *entry, |
475 | struct list_head *list) | 1004 | struct list_head *list) |
476 | { | 1005 | { |
477 | struct audit_entry *e; | 1006 | struct audit_entry *e; |
1007 | struct audit_field *inode_f = entry->rule.inode_f; | ||
1008 | struct audit_watch *watch = entry->rule.watch; | ||
1009 | struct nameidata *ndp, *ndw; | ||
1010 | int h, err, putnd_needed = 0; | ||
1011 | |||
1012 | if (inode_f) { | ||
1013 | h = audit_hash_ino(inode_f->val); | ||
1014 | list = &audit_inode_hash[h]; | ||
1015 | } | ||
1016 | |||
1017 | mutex_lock(&audit_filter_mutex); | ||
1018 | e = audit_find_rule(entry, list); | ||
1019 | mutex_unlock(&audit_filter_mutex); | ||
1020 | if (e) { | ||
1021 | err = -EEXIST; | ||
1022 | goto error; | ||
1023 | } | ||
478 | 1024 | ||
479 | /* Do not use the _rcu iterator here, since this is the only | 1025 | /* Avoid calling path_lookup under audit_filter_mutex. */ |
480 | * addition routine. */ | 1026 | if (watch) { |
481 | list_for_each_entry(e, list, list) { | 1027 | err = audit_get_nd(watch->path, &ndp, &ndw); |
482 | if (!audit_compare_rule(&entry->rule, &e->rule)) | 1028 | if (err) |
483 | return -EEXIST; | 1029 | goto error; |
1030 | putnd_needed = 1; | ||
1031 | } | ||
1032 | |||
1033 | mutex_lock(&audit_filter_mutex); | ||
1034 | if (watch) { | ||
1035 | /* audit_filter_mutex is dropped and re-taken during this call */ | ||
1036 | err = audit_add_watch(&entry->rule, ndp, ndw); | ||
1037 | if (err) { | ||
1038 | mutex_unlock(&audit_filter_mutex); | ||
1039 | goto error; | ||
1040 | } | ||
1041 | h = audit_hash_ino((u32)watch->ino); | ||
1042 | list = &audit_inode_hash[h]; | ||
484 | } | 1043 | } |
485 | 1044 | ||
486 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { | 1045 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { |
@@ -488,27 +1047,77 @@ static inline int audit_add_rule(struct audit_entry *entry, | |||
488 | } else { | 1047 | } else { |
489 | list_add_tail_rcu(&entry->list, list); | 1048 | list_add_tail_rcu(&entry->list, list); |
490 | } | 1049 | } |
1050 | mutex_unlock(&audit_filter_mutex); | ||
491 | 1051 | ||
492 | return 0; | 1052 | if (putnd_needed) |
1053 | audit_put_nd(ndp, ndw); | ||
1054 | |||
1055 | return 0; | ||
1056 | |||
1057 | error: | ||
1058 | if (putnd_needed) | ||
1059 | audit_put_nd(ndp, ndw); | ||
1060 | if (watch) | ||
1061 | audit_put_watch(watch); /* tmp watch, matches initial get */ | ||
1062 | return err; | ||
493 | } | 1063 | } |
494 | 1064 | ||
495 | /* Remove an existing rule from filterlist. Protected by | 1065 | /* Remove an existing rule from filterlist. */ |
496 | * audit_netlink_mutex. */ | ||
497 | static inline int audit_del_rule(struct audit_entry *entry, | 1066 | static inline int audit_del_rule(struct audit_entry *entry, |
498 | struct list_head *list) | 1067 | struct list_head *list) |
499 | { | 1068 | { |
500 | struct audit_entry *e; | 1069 | struct audit_entry *e; |
1070 | struct audit_field *inode_f = entry->rule.inode_f; | ||
1071 | struct audit_watch *watch, *tmp_watch = entry->rule.watch; | ||
1072 | LIST_HEAD(inotify_list); | ||
1073 | int h, ret = 0; | ||
1074 | |||
1075 | if (inode_f) { | ||
1076 | h = audit_hash_ino(inode_f->val); | ||
1077 | list = &audit_inode_hash[h]; | ||
1078 | } | ||
501 | 1079 | ||
502 | /* Do not use the _rcu iterator here, since this is the only | 1080 | mutex_lock(&audit_filter_mutex); |
503 | * deletion routine. */ | 1081 | e = audit_find_rule(entry, list); |
504 | list_for_each_entry(e, list, list) { | 1082 | if (!e) { |
505 | if (!audit_compare_rule(&entry->rule, &e->rule)) { | 1083 | mutex_unlock(&audit_filter_mutex); |
506 | list_del_rcu(&e->list); | 1084 | ret = -ENOENT; |
507 | call_rcu(&e->rcu, audit_free_rule_rcu); | 1085 | goto out; |
508 | return 0; | 1086 | } |
1087 | |||
1088 | watch = e->rule.watch; | ||
1089 | if (watch) { | ||
1090 | struct audit_parent *parent = watch->parent; | ||
1091 | |||
1092 | list_del(&e->rule.rlist); | ||
1093 | |||
1094 | if (list_empty(&watch->rules)) { | ||
1095 | audit_remove_watch(watch); | ||
1096 | |||
1097 | if (list_empty(&parent->watches)) { | ||
1098 | /* Put parent on the inotify un-registration | ||
1099 | * list. Grab a reference before releasing | ||
1100 | * audit_filter_mutex, to be released in | ||
1101 | * audit_inotify_unregister(). */ | ||
1102 | list_add(&parent->ilist, &inotify_list); | ||
1103 | get_inotify_watch(&parent->wdata); | ||
1104 | } | ||
509 | } | 1105 | } |
510 | } | 1106 | } |
511 | return -ENOENT; /* No matching rule */ | 1107 | |
1108 | list_del_rcu(&e->list); | ||
1109 | call_rcu(&e->rcu, audit_free_rule_rcu); | ||
1110 | |||
1111 | mutex_unlock(&audit_filter_mutex); | ||
1112 | |||
1113 | if (!list_empty(&inotify_list)) | ||
1114 | audit_inotify_unregister(&inotify_list); | ||
1115 | |||
1116 | out: | ||
1117 | if (tmp_watch) | ||
1118 | audit_put_watch(tmp_watch); /* match initial get */ | ||
1119 | |||
1120 | return ret; | ||
512 | } | 1121 | } |
513 | 1122 | ||
514 | /* List rules using struct audit_rule. Exists for backward | 1123 | /* List rules using struct audit_rule. Exists for backward |
@@ -519,8 +1128,8 @@ static void audit_list(int pid, int seq, struct sk_buff_head *q) | |||
519 | struct audit_entry *entry; | 1128 | struct audit_entry *entry; |
520 | int i; | 1129 | int i; |
521 | 1130 | ||
522 | /* The *_rcu iterators not needed here because we are | 1131 | /* This is a blocking read, so use audit_filter_mutex instead of rcu |
523 | always called with audit_netlink_mutex held. */ | 1132 | * iterator to sync with list writers. */ |
524 | for (i=0; i<AUDIT_NR_FILTERS; i++) { | 1133 | for (i=0; i<AUDIT_NR_FILTERS; i++) { |
525 | list_for_each_entry(entry, &audit_filter_list[i], list) { | 1134 | list_for_each_entry(entry, &audit_filter_list[i], list) { |
526 | struct audit_rule *rule; | 1135 | struct audit_rule *rule; |
@@ -535,6 +1144,20 @@ static void audit_list(int pid, int seq, struct sk_buff_head *q) | |||
535 | kfree(rule); | 1144 | kfree(rule); |
536 | } | 1145 | } |
537 | } | 1146 | } |
1147 | for (i = 0; i < AUDIT_INODE_BUCKETS; i++) { | ||
1148 | list_for_each_entry(entry, &audit_inode_hash[i], list) { | ||
1149 | struct audit_rule *rule; | ||
1150 | |||
1151 | rule = audit_krule_to_rule(&entry->rule); | ||
1152 | if (unlikely(!rule)) | ||
1153 | break; | ||
1154 | skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1, | ||
1155 | rule, sizeof(*rule)); | ||
1156 | if (skb) | ||
1157 | skb_queue_tail(q, skb); | ||
1158 | kfree(rule); | ||
1159 | } | ||
1160 | } | ||
538 | skb = audit_make_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); | 1161 | skb = audit_make_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); |
539 | if (skb) | 1162 | if (skb) |
540 | skb_queue_tail(q, skb); | 1163 | skb_queue_tail(q, skb); |
@@ -547,8 +1170,8 @@ static void audit_list_rules(int pid, int seq, struct sk_buff_head *q) | |||
547 | struct audit_entry *e; | 1170 | struct audit_entry *e; |
548 | int i; | 1171 | int i; |
549 | 1172 | ||
550 | /* The *_rcu iterators not needed here because we are | 1173 | /* This is a blocking read, so use audit_filter_mutex instead of rcu |
551 | always called with audit_netlink_mutex held. */ | 1174 | * iterator to sync with list writers. */ |
552 | for (i=0; i<AUDIT_NR_FILTERS; i++) { | 1175 | for (i=0; i<AUDIT_NR_FILTERS; i++) { |
553 | list_for_each_entry(e, &audit_filter_list[i], list) { | 1176 | list_for_each_entry(e, &audit_filter_list[i], list) { |
554 | struct audit_rule_data *data; | 1177 | struct audit_rule_data *data; |
@@ -557,7 +1180,21 @@ static void audit_list_rules(int pid, int seq, struct sk_buff_head *q) | |||
557 | if (unlikely(!data)) | 1180 | if (unlikely(!data)) |
558 | break; | 1181 | break; |
559 | skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, | 1182 | skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, |
560 | data, sizeof(*data)); | 1183 | data, sizeof(*data) + data->buflen); |
1184 | if (skb) | ||
1185 | skb_queue_tail(q, skb); | ||
1186 | kfree(data); | ||
1187 | } | ||
1188 | } | ||
1189 | for (i=0; i< AUDIT_INODE_BUCKETS; i++) { | ||
1190 | list_for_each_entry(e, &audit_inode_hash[i], list) { | ||
1191 | struct audit_rule_data *data; | ||
1192 | |||
1193 | data = audit_krule_to_data(&e->rule); | ||
1194 | if (unlikely(!data)) | ||
1195 | break; | ||
1196 | skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, | ||
1197 | data, sizeof(*data) + data->buflen); | ||
561 | if (skb) | 1198 | if (skb) |
562 | skb_queue_tail(q, skb); | 1199 | skb_queue_tail(q, skb); |
563 | kfree(data); | 1200 | kfree(data); |
@@ -602,10 +1239,12 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
602 | dest->pid = pid; | 1239 | dest->pid = pid; |
603 | skb_queue_head_init(&dest->q); | 1240 | skb_queue_head_init(&dest->q); |
604 | 1241 | ||
1242 | mutex_lock(&audit_filter_mutex); | ||
605 | if (type == AUDIT_LIST) | 1243 | if (type == AUDIT_LIST) |
606 | audit_list(pid, seq, &dest->q); | 1244 | audit_list(pid, seq, &dest->q); |
607 | else | 1245 | else |
608 | audit_list_rules(pid, seq, &dest->q); | 1246 | audit_list_rules(pid, seq, &dest->q); |
1247 | mutex_unlock(&audit_filter_mutex); | ||
609 | 1248 | ||
610 | tsk = kthread_run(audit_send_list, dest, "audit_send_list"); | 1249 | tsk = kthread_run(audit_send_list, dest, "audit_send_list"); |
611 | if (IS_ERR(tsk)) { | 1250 | if (IS_ERR(tsk)) { |
@@ -625,6 +1264,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
625 | 1264 | ||
626 | err = audit_add_rule(entry, | 1265 | err = audit_add_rule(entry, |
627 | &audit_filter_list[entry->rule.listnr]); | 1266 | &audit_filter_list[entry->rule.listnr]); |
1267 | |||
628 | if (sid) { | 1268 | if (sid) { |
629 | char *ctx = NULL; | 1269 | char *ctx = NULL; |
630 | u32 len; | 1270 | u32 len; |
@@ -705,7 +1345,39 @@ int audit_comparator(const u32 left, const u32 op, const u32 right) | |||
705 | return 0; | 1345 | return 0; |
706 | } | 1346 | } |
707 | 1347 | ||
1348 | /* Compare given dentry name with last component in given path, | ||
1349 | * return of 0 indicates a match. */ | ||
1350 | int audit_compare_dname_path(const char *dname, const char *path) | ||
1351 | { | ||
1352 | int dlen, plen; | ||
1353 | const char *p; | ||
1354 | |||
1355 | if (!dname || !path) | ||
1356 | return 1; | ||
1357 | |||
1358 | dlen = strlen(dname); | ||
1359 | plen = strlen(path); | ||
1360 | if (plen < dlen) | ||
1361 | return 1; | ||
1362 | |||
1363 | /* disregard trailing slashes */ | ||
1364 | p = path + plen - 1; | ||
1365 | while ((*p == '/') && (p > path)) | ||
1366 | p--; | ||
1367 | |||
1368 | /* find last path component */ | ||
1369 | p = p - dlen + 1; | ||
1370 | if (p < path) | ||
1371 | return 1; | ||
1372 | else if (p > path) { | ||
1373 | if (*--p != '/') | ||
1374 | return 1; | ||
1375 | else | ||
1376 | p++; | ||
1377 | } | ||
708 | 1378 | ||
1379 | return strncmp(p, dname, dlen); | ||
1380 | } | ||
709 | 1381 | ||
710 | static int audit_filter_user_rules(struct netlink_skb_parms *cb, | 1382 | static int audit_filter_user_rules(struct netlink_skb_parms *cb, |
711 | struct audit_krule *rule, | 1383 | struct audit_krule *rule, |
@@ -818,32 +1490,65 @@ static inline int audit_rule_has_selinux(struct audit_krule *rule) | |||
818 | int selinux_audit_rule_update(void) | 1490 | int selinux_audit_rule_update(void) |
819 | { | 1491 | { |
820 | struct audit_entry *entry, *n, *nentry; | 1492 | struct audit_entry *entry, *n, *nentry; |
1493 | struct audit_watch *watch; | ||
821 | int i, err = 0; | 1494 | int i, err = 0; |
822 | 1495 | ||
823 | /* audit_netlink_mutex synchronizes the writers */ | 1496 | /* audit_filter_mutex synchronizes the writers */ |
824 | mutex_lock(&audit_netlink_mutex); | 1497 | mutex_lock(&audit_filter_mutex); |
825 | 1498 | ||
826 | for (i = 0; i < AUDIT_NR_FILTERS; i++) { | 1499 | for (i = 0; i < AUDIT_NR_FILTERS; i++) { |
827 | list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) { | 1500 | list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) { |
828 | if (!audit_rule_has_selinux(&entry->rule)) | 1501 | if (!audit_rule_has_selinux(&entry->rule)) |
829 | continue; | 1502 | continue; |
830 | 1503 | ||
831 | nentry = audit_dupe_rule(&entry->rule); | 1504 | watch = entry->rule.watch; |
1505 | nentry = audit_dupe_rule(&entry->rule, watch); | ||
832 | if (unlikely(IS_ERR(nentry))) { | 1506 | if (unlikely(IS_ERR(nentry))) { |
833 | /* save the first error encountered for the | 1507 | /* save the first error encountered for the |
834 | * return value */ | 1508 | * return value */ |
835 | if (!err) | 1509 | if (!err) |
836 | err = PTR_ERR(nentry); | 1510 | err = PTR_ERR(nentry); |
837 | audit_panic("error updating selinux filters"); | 1511 | audit_panic("error updating selinux filters"); |
1512 | if (watch) | ||
1513 | list_del(&entry->rule.rlist); | ||
838 | list_del_rcu(&entry->list); | 1514 | list_del_rcu(&entry->list); |
839 | } else { | 1515 | } else { |
1516 | if (watch) { | ||
1517 | list_add(&nentry->rule.rlist, | ||
1518 | &watch->rules); | ||
1519 | list_del(&entry->rule.rlist); | ||
1520 | } | ||
840 | list_replace_rcu(&entry->list, &nentry->list); | 1521 | list_replace_rcu(&entry->list, &nentry->list); |
841 | } | 1522 | } |
842 | call_rcu(&entry->rcu, audit_free_rule_rcu); | 1523 | call_rcu(&entry->rcu, audit_free_rule_rcu); |
843 | } | 1524 | } |
844 | } | 1525 | } |
845 | 1526 | ||
846 | mutex_unlock(&audit_netlink_mutex); | 1527 | mutex_unlock(&audit_filter_mutex); |
847 | 1528 | ||
848 | return err; | 1529 | return err; |
849 | } | 1530 | } |
1531 | |||
1532 | /* Update watch data in audit rules based on inotify events. */ | ||
1533 | void audit_handle_ievent(struct inotify_watch *i_watch, u32 wd, u32 mask, | ||
1534 | u32 cookie, const char *dname, struct inode *inode) | ||
1535 | { | ||
1536 | struct audit_parent *parent; | ||
1537 | |||
1538 | parent = container_of(i_watch, struct audit_parent, wdata); | ||
1539 | |||
1540 | if (mask & (IN_CREATE|IN_MOVED_TO) && inode) | ||
1541 | audit_update_watch(parent, dname, inode->i_sb->s_dev, | ||
1542 | inode->i_ino, 0); | ||
1543 | else if (mask & (IN_DELETE|IN_MOVED_FROM)) | ||
1544 | audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1); | ||
1545 | /* inotify automatically removes the watch and sends IN_IGNORED */ | ||
1546 | else if (mask & (IN_DELETE_SELF|IN_UNMOUNT)) | ||
1547 | audit_remove_parent_watches(parent); | ||
1548 | /* inotify does not remove the watch, so remove it manually */ | ||
1549 | else if(mask & IN_MOVE_SELF) { | ||
1550 | audit_remove_parent_watches(parent); | ||
1551 | inotify_remove_watch_locked(audit_ih, i_watch); | ||
1552 | } else if (mask & IN_IGNORED) | ||
1553 | put_inotify_watch(i_watch); | ||
1554 | } | ||
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 14e295a4121b..174a3f624892 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -200,12 +200,13 @@ struct audit_context { | |||
200 | #endif | 200 | #endif |
201 | }; | 201 | }; |
202 | 202 | ||
203 | 203 | /* Determine if any context name data matches a rule's watch data */ | |
204 | /* Compare a task_struct with an audit_rule. Return 1 on match, 0 | 204 | /* Compare a task_struct with an audit_rule. Return 1 on match, 0 |
205 | * otherwise. */ | 205 | * otherwise. */ |
206 | static int audit_filter_rules(struct task_struct *tsk, | 206 | static int audit_filter_rules(struct task_struct *tsk, |
207 | struct audit_krule *rule, | 207 | struct audit_krule *rule, |
208 | struct audit_context *ctx, | 208 | struct audit_context *ctx, |
209 | struct audit_names *name, | ||
209 | enum audit_state *state) | 210 | enum audit_state *state) |
210 | { | 211 | { |
211 | int i, j, need_sid = 1; | 212 | int i, j, need_sid = 1; |
@@ -268,7 +269,10 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
268 | } | 269 | } |
269 | break; | 270 | break; |
270 | case AUDIT_DEVMAJOR: | 271 | case AUDIT_DEVMAJOR: |
271 | if (ctx) { | 272 | if (name) |
273 | result = audit_comparator(MAJOR(name->dev), | ||
274 | f->op, f->val); | ||
275 | else if (ctx) { | ||
272 | for (j = 0; j < ctx->name_count; j++) { | 276 | for (j = 0; j < ctx->name_count; j++) { |
273 | if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) { | 277 | if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) { |
274 | ++result; | 278 | ++result; |
@@ -278,7 +282,10 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
278 | } | 282 | } |
279 | break; | 283 | break; |
280 | case AUDIT_DEVMINOR: | 284 | case AUDIT_DEVMINOR: |
281 | if (ctx) { | 285 | if (name) |
286 | result = audit_comparator(MINOR(name->dev), | ||
287 | f->op, f->val); | ||
288 | else if (ctx) { | ||
282 | for (j = 0; j < ctx->name_count; j++) { | 289 | for (j = 0; j < ctx->name_count; j++) { |
283 | if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) { | 290 | if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) { |
284 | ++result; | 291 | ++result; |
@@ -288,7 +295,10 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
288 | } | 295 | } |
289 | break; | 296 | break; |
290 | case AUDIT_INODE: | 297 | case AUDIT_INODE: |
291 | if (ctx) { | 298 | if (name) |
299 | result = (name->ino == f->val || | ||
300 | name->pino == f->val); | ||
301 | else if (ctx) { | ||
292 | for (j = 0; j < ctx->name_count; j++) { | 302 | for (j = 0; j < ctx->name_count; j++) { |
293 | if (audit_comparator(ctx->names[j].ino, f->op, f->val) || | 303 | if (audit_comparator(ctx->names[j].ino, f->op, f->val) || |
294 | audit_comparator(ctx->names[j].pino, f->op, f->val)) { | 304 | audit_comparator(ctx->names[j].pino, f->op, f->val)) { |
@@ -298,6 +308,12 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
298 | } | 308 | } |
299 | } | 309 | } |
300 | break; | 310 | break; |
311 | case AUDIT_WATCH: | ||
312 | if (name && rule->watch->ino != (unsigned long)-1) | ||
313 | result = (name->dev == rule->watch->dev && | ||
314 | (name->ino == rule->watch->ino || | ||
315 | name->pino == rule->watch->ino)); | ||
316 | break; | ||
301 | case AUDIT_LOGINUID: | 317 | case AUDIT_LOGINUID: |
302 | result = 0; | 318 | result = 0; |
303 | if (ctx) | 319 | if (ctx) |
@@ -354,7 +370,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk) | |||
354 | 370 | ||
355 | rcu_read_lock(); | 371 | rcu_read_lock(); |
356 | list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) { | 372 | list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) { |
357 | if (audit_filter_rules(tsk, &e->rule, NULL, &state)) { | 373 | if (audit_filter_rules(tsk, &e->rule, NULL, NULL, &state)) { |
358 | rcu_read_unlock(); | 374 | rcu_read_unlock(); |
359 | return state; | 375 | return state; |
360 | } | 376 | } |
@@ -384,8 +400,9 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, | |||
384 | int bit = AUDIT_BIT(ctx->major); | 400 | int bit = AUDIT_BIT(ctx->major); |
385 | 401 | ||
386 | list_for_each_entry_rcu(e, list, list) { | 402 | list_for_each_entry_rcu(e, list, list) { |
387 | if ((e->rule.mask[word] & bit) == bit | 403 | if ((e->rule.mask[word] & bit) == bit && |
388 | && audit_filter_rules(tsk, &e->rule, ctx, &state)) { | 404 | audit_filter_rules(tsk, &e->rule, ctx, NULL, |
405 | &state)) { | ||
389 | rcu_read_unlock(); | 406 | rcu_read_unlock(); |
390 | return state; | 407 | return state; |
391 | } | 408 | } |
@@ -395,6 +412,49 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, | |||
395 | return AUDIT_BUILD_CONTEXT; | 412 | return AUDIT_BUILD_CONTEXT; |
396 | } | 413 | } |
397 | 414 | ||
415 | /* At syscall exit time, this filter is called if any audit_names[] have been | ||
416 | * collected during syscall processing. We only check rules in sublists at hash | ||
417 | * buckets applicable to the inode numbers in audit_names[]. | ||
418 | * Regarding audit_state, same rules apply as for audit_filter_syscall(). | ||
419 | */ | ||
420 | enum audit_state audit_filter_inodes(struct task_struct *tsk, | ||
421 | struct audit_context *ctx) | ||
422 | { | ||
423 | int i; | ||
424 | struct audit_entry *e; | ||
425 | enum audit_state state; | ||
426 | |||
427 | if (audit_pid && tsk->tgid == audit_pid) | ||
428 | return AUDIT_DISABLED; | ||
429 | |||
430 | rcu_read_lock(); | ||
431 | for (i = 0; i < ctx->name_count; i++) { | ||
432 | int word = AUDIT_WORD(ctx->major); | ||
433 | int bit = AUDIT_BIT(ctx->major); | ||
434 | struct audit_names *n = &ctx->names[i]; | ||
435 | int h = audit_hash_ino((u32)n->ino); | ||
436 | struct list_head *list = &audit_inode_hash[h]; | ||
437 | |||
438 | if (list_empty(list)) | ||
439 | continue; | ||
440 | |||
441 | list_for_each_entry_rcu(e, list, list) { | ||
442 | if ((e->rule.mask[word] & bit) == bit && | ||
443 | audit_filter_rules(tsk, &e->rule, ctx, n, &state)) { | ||
444 | rcu_read_unlock(); | ||
445 | return state; | ||
446 | } | ||
447 | } | ||
448 | } | ||
449 | rcu_read_unlock(); | ||
450 | return AUDIT_BUILD_CONTEXT; | ||
451 | } | ||
452 | |||
453 | void audit_set_auditable(struct audit_context *ctx) | ||
454 | { | ||
455 | ctx->auditable = 1; | ||
456 | } | ||
457 | |||
398 | static inline struct audit_context *audit_get_context(struct task_struct *tsk, | 458 | static inline struct audit_context *audit_get_context(struct task_struct *tsk, |
399 | int return_valid, | 459 | int return_valid, |
400 | int return_code) | 460 | int return_code) |
@@ -408,11 +468,20 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk, | |||
408 | 468 | ||
409 | if (context->in_syscall && !context->auditable) { | 469 | if (context->in_syscall && !context->auditable) { |
410 | enum audit_state state; | 470 | enum audit_state state; |
471 | |||
411 | state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]); | 472 | state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]); |
473 | if (state == AUDIT_RECORD_CONTEXT) { | ||
474 | context->auditable = 1; | ||
475 | goto get_context; | ||
476 | } | ||
477 | |||
478 | state = audit_filter_inodes(tsk, context); | ||
412 | if (state == AUDIT_RECORD_CONTEXT) | 479 | if (state == AUDIT_RECORD_CONTEXT) |
413 | context->auditable = 1; | 480 | context->auditable = 1; |
481 | |||
414 | } | 482 | } |
415 | 483 | ||
484 | get_context: | ||
416 | context->pid = tsk->pid; | 485 | context->pid = tsk->pid; |
417 | context->ppid = sys_getppid(); /* sic. tsk == current in all cases */ | 486 | context->ppid = sys_getppid(); /* sic. tsk == current in all cases */ |
418 | context->uid = tsk->uid; | 487 | context->uid = tsk->uid; |
@@ -1142,37 +1211,20 @@ void __audit_inode_child(const char *dname, const struct inode *inode, | |||
1142 | return; | 1211 | return; |
1143 | 1212 | ||
1144 | /* determine matching parent */ | 1213 | /* determine matching parent */ |
1145 | if (dname) | 1214 | if (!dname) |
1146 | for (idx = 0; idx < context->name_count; idx++) | 1215 | goto no_match; |
1147 | if (context->names[idx].pino == pino) { | 1216 | for (idx = 0; idx < context->name_count; idx++) |
1148 | const char *n; | 1217 | if (context->names[idx].pino == pino) { |
1149 | const char *name = context->names[idx].name; | 1218 | const char *name = context->names[idx].name; |
1150 | int dlen = strlen(dname); | ||
1151 | int nlen = name ? strlen(name) : 0; | ||
1152 | |||
1153 | if (nlen < dlen) | ||
1154 | continue; | ||
1155 | |||
1156 | /* disregard trailing slashes */ | ||
1157 | n = name + nlen - 1; | ||
1158 | while ((*n == '/') && (n > name)) | ||
1159 | n--; | ||
1160 | |||
1161 | /* find last path component */ | ||
1162 | n = n - dlen + 1; | ||
1163 | if (n < name) | ||
1164 | continue; | ||
1165 | else if (n > name) { | ||
1166 | if (*--n != '/') | ||
1167 | continue; | ||
1168 | else | ||
1169 | n++; | ||
1170 | } | ||
1171 | 1219 | ||
1172 | if (strncmp(n, dname, dlen) == 0) | 1220 | if (!name) |
1173 | goto update_context; | 1221 | continue; |
1174 | } | 1222 | |
1223 | if (audit_compare_dname_path(dname, name) == 0) | ||
1224 | goto update_context; | ||
1225 | } | ||
1175 | 1226 | ||
1227 | no_match: | ||
1176 | /* catch-all in case match not found */ | 1228 | /* catch-all in case match not found */ |
1177 | idx = context->name_count++; | 1229 | idx = context->name_count++; |
1178 | context->names[idx].name = NULL; | 1230 | context->names[idx].name = NULL; |