diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/audit.c | 19 | ||||
-rw-r--r-- | kernel/audit.h | 23 | ||||
-rw-r--r-- | kernel/auditfilter.c | 467 | ||||
-rw-r--r-- | kernel/auditsc.c | 50 |
4 files changed, 418 insertions, 141 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index 07c5d2bdd38c..4eb97b62d7fa 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -52,6 +52,7 @@ | |||
52 | #include <linux/audit.h> | 52 | #include <linux/audit.h> |
53 | 53 | ||
54 | #include <net/sock.h> | 54 | #include <net/sock.h> |
55 | #include <net/netlink.h> | ||
55 | #include <linux/skbuff.h> | 56 | #include <linux/skbuff.h> |
56 | #include <linux/netlink.h> | 57 | #include <linux/netlink.h> |
57 | 58 | ||
@@ -361,9 +362,12 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type) | |||
361 | switch (msg_type) { | 362 | switch (msg_type) { |
362 | case AUDIT_GET: | 363 | case AUDIT_GET: |
363 | case AUDIT_LIST: | 364 | case AUDIT_LIST: |
365 | case AUDIT_LIST_RULES: | ||
364 | case AUDIT_SET: | 366 | case AUDIT_SET: |
365 | case AUDIT_ADD: | 367 | case AUDIT_ADD: |
368 | case AUDIT_ADD_RULE: | ||
366 | case AUDIT_DEL: | 369 | case AUDIT_DEL: |
370 | case AUDIT_DEL_RULE: | ||
367 | case AUDIT_SIGNAL_INFO: | 371 | case AUDIT_SIGNAL_INFO: |
368 | if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL)) | 372 | if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL)) |
369 | err = -EPERM; | 373 | err = -EPERM; |
@@ -470,12 +474,23 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
470 | break; | 474 | break; |
471 | case AUDIT_ADD: | 475 | case AUDIT_ADD: |
472 | case AUDIT_DEL: | 476 | case AUDIT_DEL: |
473 | if (nlh->nlmsg_len < sizeof(struct audit_rule)) | 477 | if (nlmsg_len(nlh) < sizeof(struct audit_rule)) |
474 | return -EINVAL; | 478 | return -EINVAL; |
475 | /* fallthrough */ | 479 | /* fallthrough */ |
476 | case AUDIT_LIST: | 480 | case AUDIT_LIST: |
477 | err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, | 481 | err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, |
478 | uid, seq, data, loginuid); | 482 | uid, seq, data, nlmsg_len(nlh), |
483 | loginuid); | ||
484 | break; | ||
485 | case AUDIT_ADD_RULE: | ||
486 | case AUDIT_DEL_RULE: | ||
487 | if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) | ||
488 | return -EINVAL; | ||
489 | /* fallthrough */ | ||
490 | case AUDIT_LIST_RULES: | ||
491 | err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, | ||
492 | uid, seq, data, nlmsg_len(nlh), | ||
493 | loginuid); | ||
479 | break; | 494 | break; |
480 | case AUDIT_SIGNAL_INFO: | 495 | case AUDIT_SIGNAL_INFO: |
481 | sig_data.uid = audit_sig_uid; | 496 | sig_data.uid = audit_sig_uid; |
diff --git a/kernel/audit.h b/kernel/audit.h index 7643e46daeb2..4b602cdcabef 100644 --- a/kernel/audit.h +++ b/kernel/audit.h | |||
@@ -52,10 +52,27 @@ enum audit_state { | |||
52 | }; | 52 | }; |
53 | 53 | ||
54 | /* Rule lists */ | 54 | /* Rule lists */ |
55 | struct audit_field { | ||
56 | u32 type; | ||
57 | u32 val; | ||
58 | u32 op; | ||
59 | }; | ||
60 | |||
61 | struct audit_krule { | ||
62 | int vers_ops; | ||
63 | u32 flags; | ||
64 | u32 listnr; | ||
65 | u32 action; | ||
66 | u32 mask[AUDIT_BITMASK_SIZE]; | ||
67 | u32 buflen; /* for data alloc on list rules */ | ||
68 | u32 field_count; | ||
69 | struct audit_field *fields; | ||
70 | }; | ||
71 | |||
55 | struct audit_entry { | 72 | struct audit_entry { |
56 | struct list_head list; | 73 | struct list_head list; |
57 | struct rcu_head rcu; | 74 | struct rcu_head rcu; |
58 | struct audit_rule rule; | 75 | struct audit_krule rule; |
59 | }; | 76 | }; |
60 | 77 | ||
61 | 78 | ||
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index a3a32752f973..686d514a3518 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c | |||
@@ -40,52 +40,279 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { | |||
40 | #endif | 40 | #endif |
41 | }; | 41 | }; |
42 | 42 | ||
43 | /* Copy rule from user-space to kernel-space. Called from | 43 | static inline void audit_free_rule(struct audit_entry *e) |
44 | * audit_add_rule during AUDIT_ADD. */ | ||
45 | static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) | ||
46 | { | 44 | { |
45 | kfree(e->rule.fields); | ||
46 | kfree(e); | ||
47 | } | ||
48 | |||
49 | static inline void audit_free_rule_rcu(struct rcu_head *head) | ||
50 | { | ||
51 | struct audit_entry *e = container_of(head, struct audit_entry, rcu); | ||
52 | audit_free_rule(e); | ||
53 | } | ||
54 | |||
55 | /* Unpack a filter field's string representation from user-space | ||
56 | * buffer. */ | ||
57 | static __attribute__((unused)) char *audit_unpack_string(void **bufp, size_t *remain, size_t len) | ||
58 | { | ||
59 | char *str; | ||
60 | |||
61 | if (!*bufp || (len == 0) || (len > *remain)) | ||
62 | return ERR_PTR(-EINVAL); | ||
63 | |||
64 | /* Of the currently implemented string fields, PATH_MAX | ||
65 | * defines the longest valid length. | ||
66 | */ | ||
67 | if (len > PATH_MAX) | ||
68 | return ERR_PTR(-ENAMETOOLONG); | ||
69 | |||
70 | str = kmalloc(len + 1, GFP_KERNEL); | ||
71 | if (unlikely(!str)) | ||
72 | return ERR_PTR(-ENOMEM); | ||
73 | |||
74 | memcpy(str, *bufp, len); | ||
75 | str[len] = 0; | ||
76 | *bufp += len; | ||
77 | *remain -= len; | ||
78 | |||
79 | return str; | ||
80 | } | ||
81 | |||
82 | /* Common user-space to kernel rule translation. */ | ||
83 | static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule) | ||
84 | { | ||
85 | unsigned listnr; | ||
86 | struct audit_entry *entry; | ||
87 | struct audit_field *fields; | ||
88 | int i, err; | ||
89 | |||
90 | err = -EINVAL; | ||
91 | listnr = rule->flags & ~AUDIT_FILTER_PREPEND; | ||
92 | switch(listnr) { | ||
93 | default: | ||
94 | goto exit_err; | ||
95 | case AUDIT_FILTER_USER: | ||
96 | case AUDIT_FILTER_TYPE: | ||
97 | #ifdef CONFIG_AUDITSYSCALL | ||
98 | case AUDIT_FILTER_ENTRY: | ||
99 | case AUDIT_FILTER_EXIT: | ||
100 | case AUDIT_FILTER_TASK: | ||
101 | #endif | ||
102 | ; | ||
103 | } | ||
104 | if (rule->action != AUDIT_NEVER && rule->action != AUDIT_POSSIBLE && | ||
105 | rule->action != AUDIT_ALWAYS) | ||
106 | goto exit_err; | ||
107 | if (rule->field_count > AUDIT_MAX_FIELDS) | ||
108 | goto exit_err; | ||
109 | |||
110 | err = -ENOMEM; | ||
111 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | ||
112 | if (unlikely(!entry)) | ||
113 | goto exit_err; | ||
114 | fields = kmalloc(sizeof(*fields) * rule->field_count, GFP_KERNEL); | ||
115 | if (unlikely(!fields)) { | ||
116 | kfree(entry); | ||
117 | goto exit_err; | ||
118 | } | ||
119 | |||
120 | memset(&entry->rule, 0, sizeof(struct audit_krule)); | ||
121 | memset(fields, 0, sizeof(struct audit_field)); | ||
122 | |||
123 | entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND; | ||
124 | entry->rule.listnr = listnr; | ||
125 | entry->rule.action = rule->action; | ||
126 | entry->rule.field_count = rule->field_count; | ||
127 | entry->rule.fields = fields; | ||
128 | |||
129 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) | ||
130 | entry->rule.mask[i] = rule->mask[i]; | ||
131 | |||
132 | return entry; | ||
133 | |||
134 | exit_err: | ||
135 | return ERR_PTR(err); | ||
136 | } | ||
137 | |||
138 | /* Translate struct audit_rule to kernel's rule respresentation. | ||
139 | * Exists for backward compatibility with userspace. */ | ||
140 | static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) | ||
141 | { | ||
142 | struct audit_entry *entry; | ||
143 | int err = 0; | ||
47 | int i; | 144 | int i; |
48 | 145 | ||
49 | if (s->action != AUDIT_NEVER | 146 | entry = audit_to_entry_common(rule); |
50 | && s->action != AUDIT_POSSIBLE | 147 | if (IS_ERR(entry)) |
51 | && s->action != AUDIT_ALWAYS) | 148 | goto exit_nofree; |
52 | return -1; | 149 | |
53 | if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS) | 150 | for (i = 0; i < rule->field_count; i++) { |
54 | return -1; | 151 | struct audit_field *f = &entry->rule.fields[i]; |
55 | if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS) | 152 | |
56 | return -1; | 153 | if (rule->fields[i] & AUDIT_UNUSED_BITS) { |
57 | 154 | err = -EINVAL; | |
58 | d->flags = s->flags; | 155 | goto exit_free; |
59 | d->action = s->action; | 156 | } |
60 | d->field_count = s->field_count; | 157 | |
61 | for (i = 0; i < d->field_count; i++) { | 158 | f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS); |
62 | d->fields[i] = s->fields[i]; | 159 | f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); |
63 | d->values[i] = s->values[i]; | 160 | f->val = rule->values[i]; |
161 | |||
162 | entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1; | ||
163 | if (f->op & AUDIT_NEGATE) | ||
164 | f->op |= AUDIT_NOT_EQUAL; | ||
165 | else if (!(f->op & AUDIT_OPERATORS)) | ||
166 | f->op |= AUDIT_EQUAL; | ||
167 | f->op &= ~AUDIT_NEGATE; | ||
64 | } | 168 | } |
65 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i]; | 169 | |
66 | return 0; | 170 | exit_nofree: |
171 | return entry; | ||
172 | |||
173 | exit_free: | ||
174 | audit_free_rule(entry); | ||
175 | return ERR_PTR(err); | ||
67 | } | 176 | } |
68 | 177 | ||
69 | /* Check to see if two rules are identical. It is called from | 178 | /* Translate struct audit_rule_data to kernel's rule respresentation. */ |
70 | * audit_add_rule during AUDIT_ADD and | 179 | static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, |
71 | * audit_del_rule during AUDIT_DEL. */ | 180 | size_t datasz) |
72 | static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) | ||
73 | { | 181 | { |
182 | int err = 0; | ||
183 | struct audit_entry *entry; | ||
184 | void *bufp; | ||
185 | /* size_t remain = datasz - sizeof(struct audit_rule_data); */ | ||
74 | int i; | 186 | int i; |
75 | 187 | ||
76 | if (a->flags != b->flags) | 188 | entry = audit_to_entry_common((struct audit_rule *)data); |
77 | return 1; | 189 | if (IS_ERR(entry)) |
190 | goto exit_nofree; | ||
78 | 191 | ||
79 | if (a->action != b->action) | 192 | bufp = data->buf; |
80 | return 1; | 193 | entry->rule.vers_ops = 2; |
194 | for (i = 0; i < data->field_count; i++) { | ||
195 | struct audit_field *f = &entry->rule.fields[i]; | ||
196 | |||
197 | err = -EINVAL; | ||
198 | if (!(data->fieldflags[i] & AUDIT_OPERATORS) || | ||
199 | data->fieldflags[i] & ~AUDIT_OPERATORS) | ||
200 | goto exit_free; | ||
201 | |||
202 | f->op = data->fieldflags[i] & AUDIT_OPERATORS; | ||
203 | f->type = data->fields[i]; | ||
204 | switch(f->type) { | ||
205 | /* call type-specific conversion routines here */ | ||
206 | default: | ||
207 | f->val = data->values[i]; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | exit_nofree: | ||
212 | return entry; | ||
213 | |||
214 | exit_free: | ||
215 | audit_free_rule(entry); | ||
216 | return ERR_PTR(err); | ||
217 | } | ||
218 | |||
219 | /* Pack a filter field's string representation into data block. */ | ||
220 | static inline size_t audit_pack_string(void **bufp, char *str) | ||
221 | { | ||
222 | size_t len = strlen(str); | ||
223 | |||
224 | memcpy(*bufp, str, len); | ||
225 | *bufp += len; | ||
226 | |||
227 | return len; | ||
228 | } | ||
229 | |||
230 | /* Translate kernel rule respresentation to struct audit_rule. | ||
231 | * Exists for backward compatibility with userspace. */ | ||
232 | static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule) | ||
233 | { | ||
234 | struct audit_rule *rule; | ||
235 | int i; | ||
236 | |||
237 | rule = kmalloc(sizeof(*rule), GFP_KERNEL); | ||
238 | if (unlikely(!rule)) | ||
239 | return ERR_PTR(-ENOMEM); | ||
240 | memset(rule, 0, sizeof(*rule)); | ||
241 | |||
242 | rule->flags = krule->flags | krule->listnr; | ||
243 | rule->action = krule->action; | ||
244 | rule->field_count = krule->field_count; | ||
245 | for (i = 0; i < rule->field_count; i++) { | ||
246 | rule->values[i] = krule->fields[i].val; | ||
247 | rule->fields[i] = krule->fields[i].type; | ||
248 | |||
249 | if (krule->vers_ops == 1) { | ||
250 | if (krule->fields[i].op & AUDIT_NOT_EQUAL) | ||
251 | rule->fields[i] |= AUDIT_NEGATE; | ||
252 | } else { | ||
253 | rule->fields[i] |= krule->fields[i].op; | ||
254 | } | ||
255 | } | ||
256 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i]; | ||
257 | |||
258 | return rule; | ||
259 | } | ||
81 | 260 | ||
82 | if (a->field_count != b->field_count) | 261 | /* Translate kernel rule respresentation to struct audit_rule_data. */ |
262 | static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) | ||
263 | { | ||
264 | struct audit_rule_data *data; | ||
265 | void *bufp; | ||
266 | int i; | ||
267 | |||
268 | data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL); | ||
269 | if (unlikely(!data)) | ||
270 | return ERR_PTR(-ENOMEM); | ||
271 | memset(data, 0, sizeof(*data)); | ||
272 | |||
273 | data->flags = krule->flags | krule->listnr; | ||
274 | data->action = krule->action; | ||
275 | data->field_count = krule->field_count; | ||
276 | bufp = data->buf; | ||
277 | for (i = 0; i < data->field_count; i++) { | ||
278 | struct audit_field *f = &krule->fields[i]; | ||
279 | |||
280 | data->fields[i] = f->type; | ||
281 | data->fieldflags[i] = f->op; | ||
282 | switch(f->type) { | ||
283 | /* call type-specific conversion routines here */ | ||
284 | default: | ||
285 | data->values[i] = f->val; | ||
286 | } | ||
287 | } | ||
288 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) data->mask[i] = krule->mask[i]; | ||
289 | |||
290 | return data; | ||
291 | } | ||
292 | |||
293 | /* Compare two rules in kernel format. Considered success if rules | ||
294 | * don't match. */ | ||
295 | static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) | ||
296 | { | ||
297 | int i; | ||
298 | |||
299 | if (a->flags != b->flags || | ||
300 | a->listnr != b->listnr || | ||
301 | a->action != b->action || | ||
302 | a->field_count != b->field_count) | ||
83 | return 1; | 303 | return 1; |
84 | 304 | ||
85 | for (i = 0; i < a->field_count; i++) { | 305 | for (i = 0; i < a->field_count; i++) { |
86 | if (a->fields[i] != b->fields[i] | 306 | if (a->fields[i].type != b->fields[i].type || |
87 | || a->values[i] != b->values[i]) | 307 | a->fields[i].op != b->fields[i].op) |
88 | return 1; | 308 | return 1; |
309 | |||
310 | switch(a->fields[i].type) { | ||
311 | /* call type-specific comparison routines here */ | ||
312 | default: | ||
313 | if (a->fields[i].val != b->fields[i].val) | ||
314 | return 1; | ||
315 | } | ||
89 | } | 316 | } |
90 | 317 | ||
91 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) | 318 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) |
@@ -95,41 +322,21 @@ static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) | |||
95 | return 0; | 322 | return 0; |
96 | } | 323 | } |
97 | 324 | ||
98 | /* Note that audit_add_rule and audit_del_rule are called via | 325 | /* Add rule to given filterlist if not a duplicate. Protected by |
99 | * audit_receive() in audit.c, and are protected by | ||
100 | * audit_netlink_sem. */ | 326 | * audit_netlink_sem. */ |
101 | static inline int audit_add_rule(struct audit_rule *rule, | 327 | static inline int audit_add_rule(struct audit_entry *entry, |
102 | struct list_head *list) | 328 | struct list_head *list) |
103 | { | 329 | { |
104 | struct audit_entry *entry; | 330 | struct audit_entry *e; |
105 | int i; | ||
106 | 331 | ||
107 | /* Do not use the _rcu iterator here, since this is the only | 332 | /* Do not use the _rcu iterator here, since this is the only |
108 | * addition routine. */ | 333 | * addition routine. */ |
109 | list_for_each_entry(entry, list, list) { | 334 | list_for_each_entry(e, list, list) { |
110 | if (!audit_compare_rule(rule, &entry->rule)) | 335 | if (!audit_compare_rule(&entry->rule, &e->rule)) |
111 | return -EEXIST; | 336 | return -EEXIST; |
112 | } | 337 | } |
113 | 338 | ||
114 | for (i = 0; i < rule->field_count; i++) { | ||
115 | if (rule->fields[i] & AUDIT_UNUSED_BITS) | ||
116 | return -EINVAL; | ||
117 | if ( rule->fields[i] & AUDIT_NEGATE) | ||
118 | rule->fields[i] |= AUDIT_NOT_EQUAL; | ||
119 | else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 ) | ||
120 | rule->fields[i] |= AUDIT_EQUAL; | ||
121 | rule->fields[i] &= ~AUDIT_NEGATE; | ||
122 | } | ||
123 | |||
124 | if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) | ||
125 | return -ENOMEM; | ||
126 | if (audit_copy_rule(&entry->rule, rule)) { | ||
127 | kfree(entry); | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | |||
131 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { | 339 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { |
132 | entry->rule.flags &= ~AUDIT_FILTER_PREPEND; | ||
133 | list_add_rcu(&entry->list, list); | 340 | list_add_rcu(&entry->list, list); |
134 | } else { | 341 | } else { |
135 | list_add_tail_rcu(&entry->list, list); | 342 | list_add_tail_rcu(&entry->list, list); |
@@ -138,16 +345,9 @@ static inline int audit_add_rule(struct audit_rule *rule, | |||
138 | return 0; | 345 | return 0; |
139 | } | 346 | } |
140 | 347 | ||
141 | static inline void audit_free_rule(struct rcu_head *head) | 348 | /* Remove an existing rule from filterlist. Protected by |
142 | { | ||
143 | struct audit_entry *e = container_of(head, struct audit_entry, rcu); | ||
144 | kfree(e); | ||
145 | } | ||
146 | |||
147 | /* Note that audit_add_rule and audit_del_rule are called via | ||
148 | * audit_receive() in audit.c, and are protected by | ||
149 | * audit_netlink_sem. */ | 349 | * audit_netlink_sem. */ |
150 | static inline int audit_del_rule(struct audit_rule *rule, | 350 | static inline int audit_del_rule(struct audit_entry *entry, |
151 | struct list_head *list) | 351 | struct list_head *list) |
152 | { | 352 | { |
153 | struct audit_entry *e; | 353 | struct audit_entry *e; |
@@ -155,16 +355,18 @@ static inline int audit_del_rule(struct audit_rule *rule, | |||
155 | /* Do not use the _rcu iterator here, since this is the only | 355 | /* Do not use the _rcu iterator here, since this is the only |
156 | * deletion routine. */ | 356 | * deletion routine. */ |
157 | list_for_each_entry(e, list, list) { | 357 | list_for_each_entry(e, list, list) { |
158 | if (!audit_compare_rule(rule, &e->rule)) { | 358 | if (!audit_compare_rule(&entry->rule, &e->rule)) { |
159 | list_del_rcu(&e->list); | 359 | list_del_rcu(&e->list); |
160 | call_rcu(&e->rcu, audit_free_rule); | 360 | call_rcu(&e->rcu, audit_free_rule_rcu); |
161 | return 0; | 361 | return 0; |
162 | } | 362 | } |
163 | } | 363 | } |
164 | return -ENOENT; /* No matching rule */ | 364 | return -ENOENT; /* No matching rule */ |
165 | } | 365 | } |
166 | 366 | ||
167 | static int audit_list_rules(void *_dest) | 367 | /* List rules using struct audit_rule. Exists for backward |
368 | * compatibility with userspace. */ | ||
369 | static int audit_list(void *_dest) | ||
168 | { | 370 | { |
169 | int pid, seq; | 371 | int pid, seq; |
170 | int *dest = _dest; | 372 | int *dest = _dest; |
@@ -180,9 +382,16 @@ static int audit_list_rules(void *_dest) | |||
180 | /* The *_rcu iterators not needed here because we are | 382 | /* The *_rcu iterators not needed here because we are |
181 | always called with audit_netlink_sem held. */ | 383 | always called with audit_netlink_sem held. */ |
182 | for (i=0; i<AUDIT_NR_FILTERS; i++) { | 384 | for (i=0; i<AUDIT_NR_FILTERS; i++) { |
183 | list_for_each_entry(entry, &audit_filter_list[i], list) | 385 | list_for_each_entry(entry, &audit_filter_list[i], list) { |
386 | struct audit_rule *rule; | ||
387 | |||
388 | rule = audit_krule_to_rule(&entry->rule); | ||
389 | if (unlikely(!rule)) | ||
390 | break; | ||
184 | audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, | 391 | audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, |
185 | &entry->rule, sizeof(entry->rule)); | 392 | rule, sizeof(*rule)); |
393 | kfree(rule); | ||
394 | } | ||
186 | } | 395 | } |
187 | audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); | 396 | audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); |
188 | 397 | ||
@@ -190,6 +399,40 @@ static int audit_list_rules(void *_dest) | |||
190 | return 0; | 399 | return 0; |
191 | } | 400 | } |
192 | 401 | ||
402 | /* List rules using struct audit_rule_data. */ | ||
403 | static int audit_list_rules(void *_dest) | ||
404 | { | ||
405 | int pid, seq; | ||
406 | int *dest = _dest; | ||
407 | struct audit_entry *e; | ||
408 | int i; | ||
409 | |||
410 | pid = dest[0]; | ||
411 | seq = dest[1]; | ||
412 | kfree(dest); | ||
413 | |||
414 | down(&audit_netlink_sem); | ||
415 | |||
416 | /* The *_rcu iterators not needed here because we are | ||
417 | always called with audit_netlink_sem held. */ | ||
418 | for (i=0; i<AUDIT_NR_FILTERS; i++) { | ||
419 | list_for_each_entry(e, &audit_filter_list[i], list) { | ||
420 | struct audit_rule_data *data; | ||
421 | |||
422 | data = audit_krule_to_data(&e->rule); | ||
423 | if (unlikely(!data)) | ||
424 | break; | ||
425 | audit_send_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, | ||
426 | data, sizeof(*data)); | ||
427 | kfree(data); | ||
428 | } | ||
429 | } | ||
430 | audit_send_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0); | ||
431 | |||
432 | up(&audit_netlink_sem); | ||
433 | return 0; | ||
434 | } | ||
435 | |||
193 | /** | 436 | /** |
194 | * audit_receive_filter - apply all rules to the specified message type | 437 | * audit_receive_filter - apply all rules to the specified message type |
195 | * @type: audit message type | 438 | * @type: audit message type |
@@ -197,18 +440,20 @@ static int audit_list_rules(void *_dest) | |||
197 | * @uid: target uid for netlink audit messages | 440 | * @uid: target uid for netlink audit messages |
198 | * @seq: netlink audit message sequence (serial) number | 441 | * @seq: netlink audit message sequence (serial) number |
199 | * @data: payload data | 442 | * @data: payload data |
443 | * @datasz: size of payload data | ||
200 | * @loginuid: loginuid of sender | 444 | * @loginuid: loginuid of sender |
201 | */ | 445 | */ |
202 | int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | 446 | int audit_receive_filter(int type, int pid, int uid, int seq, void *data, |
203 | uid_t loginuid) | 447 | size_t datasz, uid_t loginuid) |
204 | { | 448 | { |
205 | struct task_struct *tsk; | 449 | struct task_struct *tsk; |
206 | int *dest; | 450 | int *dest; |
207 | int err = 0; | 451 | int err = 0; |
208 | unsigned listnr; | 452 | struct audit_entry *entry; |
209 | 453 | ||
210 | switch (type) { | 454 | switch (type) { |
211 | case AUDIT_LIST: | 455 | case AUDIT_LIST: |
456 | case AUDIT_LIST_RULES: | ||
212 | /* We can't just spew out the rules here because we might fill | 457 | /* We can't just spew out the rules here because we might fill |
213 | * the available socket buffer space and deadlock waiting for | 458 | * the available socket buffer space and deadlock waiting for |
214 | * auditctl to read from it... which isn't ever going to | 459 | * auditctl to read from it... which isn't ever going to |
@@ -221,41 +466,48 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
221 | dest[0] = pid; | 466 | dest[0] = pid; |
222 | dest[1] = seq; | 467 | dest[1] = seq; |
223 | 468 | ||
224 | tsk = kthread_run(audit_list_rules, dest, "audit_list_rules"); | 469 | if (type == AUDIT_LIST) |
470 | tsk = kthread_run(audit_list, dest, "audit_list"); | ||
471 | else | ||
472 | tsk = kthread_run(audit_list_rules, dest, | ||
473 | "audit_list_rules"); | ||
225 | if (IS_ERR(tsk)) { | 474 | if (IS_ERR(tsk)) { |
226 | kfree(dest); | 475 | kfree(dest); |
227 | err = PTR_ERR(tsk); | 476 | err = PTR_ERR(tsk); |
228 | } | 477 | } |
229 | break; | 478 | break; |
230 | case AUDIT_ADD: | 479 | case AUDIT_ADD: |
231 | listnr = ((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; | 480 | case AUDIT_ADD_RULE: |
232 | switch(listnr) { | 481 | if (type == AUDIT_ADD) |
233 | default: | 482 | entry = audit_rule_to_entry(data); |
234 | return -EINVAL; | 483 | else |
235 | 484 | entry = audit_data_to_entry(data, datasz); | |
236 | case AUDIT_FILTER_USER: | 485 | if (IS_ERR(entry)) |
237 | case AUDIT_FILTER_TYPE: | 486 | return PTR_ERR(entry); |
238 | #ifdef CONFIG_AUDITSYSCALL | 487 | |
239 | case AUDIT_FILTER_ENTRY: | 488 | err = audit_add_rule(entry, |
240 | case AUDIT_FILTER_EXIT: | 489 | &audit_filter_list[entry->rule.listnr]); |
241 | case AUDIT_FILTER_TASK: | ||
242 | #endif | ||
243 | ; | ||
244 | } | ||
245 | err = audit_add_rule(data, &audit_filter_list[listnr]); | ||
246 | if (!err) | 490 | if (!err) |
247 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 491 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
248 | "auid=%u added an audit rule\n", loginuid); | 492 | "auid=%u added an audit rule\n", loginuid); |
493 | else | ||
494 | audit_free_rule(entry); | ||
249 | break; | 495 | break; |
250 | case AUDIT_DEL: | 496 | case AUDIT_DEL: |
251 | listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; | 497 | case AUDIT_DEL_RULE: |
252 | if (listnr >= AUDIT_NR_FILTERS) | 498 | if (type == AUDIT_DEL) |
253 | return -EINVAL; | 499 | entry = audit_rule_to_entry(data); |
254 | 500 | else | |
255 | err = audit_del_rule(data, &audit_filter_list[listnr]); | 501 | entry = audit_data_to_entry(data, datasz); |
502 | if (IS_ERR(entry)) | ||
503 | return PTR_ERR(entry); | ||
504 | |||
505 | err = audit_del_rule(entry, | ||
506 | &audit_filter_list[entry->rule.listnr]); | ||
256 | if (!err) | 507 | if (!err) |
257 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 508 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
258 | "auid=%u removed an audit rule\n", loginuid); | 509 | "auid=%u removed an audit rule\n", loginuid); |
510 | audit_free_rule(entry); | ||
259 | break; | 511 | break; |
260 | default: | 512 | default: |
261 | return -EINVAL; | 513 | return -EINVAL; |
@@ -287,29 +539,27 @@ int audit_comparator(const u32 left, const u32 op, const u32 right) | |||
287 | 539 | ||
288 | 540 | ||
289 | static int audit_filter_user_rules(struct netlink_skb_parms *cb, | 541 | static int audit_filter_user_rules(struct netlink_skb_parms *cb, |
290 | struct audit_rule *rule, | 542 | struct audit_krule *rule, |
291 | enum audit_state *state) | 543 | enum audit_state *state) |
292 | { | 544 | { |
293 | int i; | 545 | int i; |
294 | 546 | ||
295 | for (i = 0; i < rule->field_count; i++) { | 547 | for (i = 0; i < rule->field_count; i++) { |
296 | u32 field = rule->fields[i] & ~AUDIT_OPERATORS; | 548 | struct audit_field *f = &rule->fields[i]; |
297 | u32 op = rule->fields[i] & AUDIT_OPERATORS; | ||
298 | u32 value = rule->values[i]; | ||
299 | int result = 0; | 549 | int result = 0; |
300 | 550 | ||
301 | switch (field) { | 551 | switch (f->type) { |
302 | case AUDIT_PID: | 552 | case AUDIT_PID: |
303 | result = audit_comparator(cb->creds.pid, op, value); | 553 | result = audit_comparator(cb->creds.pid, f->op, f->val); |
304 | break; | 554 | break; |
305 | case AUDIT_UID: | 555 | case AUDIT_UID: |
306 | result = audit_comparator(cb->creds.uid, op, value); | 556 | result = audit_comparator(cb->creds.uid, f->op, f->val); |
307 | break; | 557 | break; |
308 | case AUDIT_GID: | 558 | case AUDIT_GID: |
309 | result = audit_comparator(cb->creds.gid, op, value); | 559 | result = audit_comparator(cb->creds.gid, f->op, f->val); |
310 | break; | 560 | break; |
311 | case AUDIT_LOGINUID: | 561 | case AUDIT_LOGINUID: |
312 | result = audit_comparator(cb->loginuid, op, value); | 562 | result = audit_comparator(cb->loginuid, f->op, f->val); |
313 | break; | 563 | break; |
314 | } | 564 | } |
315 | 565 | ||
@@ -354,14 +604,11 @@ int audit_filter_type(int type) | |||
354 | 604 | ||
355 | list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], | 605 | list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], |
356 | list) { | 606 | list) { |
357 | struct audit_rule *rule = &e->rule; | ||
358 | int i; | 607 | int i; |
359 | for (i = 0; i < rule->field_count; i++) { | 608 | for (i = 0; i < e->rule.field_count; i++) { |
360 | u32 field = rule->fields[i] & ~AUDIT_OPERATORS; | 609 | struct audit_field *f = &e->rule.fields[i]; |
361 | u32 op = rule->fields[i] & AUDIT_OPERATORS; | 610 | if (f->type == AUDIT_MSGTYPE) { |
362 | u32 value = rule->values[i]; | 611 | result = audit_comparator(type, f->op, f->val); |
363 | if ( field == AUDIT_MSGTYPE ) { | ||
364 | result = audit_comparator(type, op, value); | ||
365 | if (!result) | 612 | if (!result) |
366 | break; | 613 | break; |
367 | } | 614 | } |
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 17719b303638..ba0878854777 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -162,70 +162,68 @@ struct audit_context { | |||
162 | /* Compare a task_struct with an audit_rule. Return 1 on match, 0 | 162 | /* Compare a task_struct with an audit_rule. Return 1 on match, 0 |
163 | * otherwise. */ | 163 | * otherwise. */ |
164 | static int audit_filter_rules(struct task_struct *tsk, | 164 | static int audit_filter_rules(struct task_struct *tsk, |
165 | struct audit_rule *rule, | 165 | struct audit_krule *rule, |
166 | struct audit_context *ctx, | 166 | struct audit_context *ctx, |
167 | enum audit_state *state) | 167 | enum audit_state *state) |
168 | { | 168 | { |
169 | int i, j; | 169 | int i, j; |
170 | 170 | ||
171 | for (i = 0; i < rule->field_count; i++) { | 171 | for (i = 0; i < rule->field_count; i++) { |
172 | u32 field = rule->fields[i] & ~AUDIT_OPERATORS; | 172 | struct audit_field *f = &rule->fields[i]; |
173 | u32 op = rule->fields[i] & AUDIT_OPERATORS; | ||
174 | u32 value = rule->values[i]; | ||
175 | int result = 0; | 173 | int result = 0; |
176 | 174 | ||
177 | switch (field) { | 175 | switch (f->type) { |
178 | case AUDIT_PID: | 176 | case AUDIT_PID: |
179 | result = audit_comparator(tsk->pid, op, value); | 177 | result = audit_comparator(tsk->pid, f->op, f->val); |
180 | break; | 178 | break; |
181 | case AUDIT_UID: | 179 | case AUDIT_UID: |
182 | result = audit_comparator(tsk->uid, op, value); | 180 | result = audit_comparator(tsk->uid, f->op, f->val); |
183 | break; | 181 | break; |
184 | case AUDIT_EUID: | 182 | case AUDIT_EUID: |
185 | result = audit_comparator(tsk->euid, op, value); | 183 | result = audit_comparator(tsk->euid, f->op, f->val); |
186 | break; | 184 | break; |
187 | case AUDIT_SUID: | 185 | case AUDIT_SUID: |
188 | result = audit_comparator(tsk->suid, op, value); | 186 | result = audit_comparator(tsk->suid, f->op, f->val); |
189 | break; | 187 | break; |
190 | case AUDIT_FSUID: | 188 | case AUDIT_FSUID: |
191 | result = audit_comparator(tsk->fsuid, op, value); | 189 | result = audit_comparator(tsk->fsuid, f->op, f->val); |
192 | break; | 190 | break; |
193 | case AUDIT_GID: | 191 | case AUDIT_GID: |
194 | result = audit_comparator(tsk->gid, op, value); | 192 | result = audit_comparator(tsk->gid, f->op, f->val); |
195 | break; | 193 | break; |
196 | case AUDIT_EGID: | 194 | case AUDIT_EGID: |
197 | result = audit_comparator(tsk->egid, op, value); | 195 | result = audit_comparator(tsk->egid, f->op, f->val); |
198 | break; | 196 | break; |
199 | case AUDIT_SGID: | 197 | case AUDIT_SGID: |
200 | result = audit_comparator(tsk->sgid, op, value); | 198 | result = audit_comparator(tsk->sgid, f->op, f->val); |
201 | break; | 199 | break; |
202 | case AUDIT_FSGID: | 200 | case AUDIT_FSGID: |
203 | result = audit_comparator(tsk->fsgid, op, value); | 201 | result = audit_comparator(tsk->fsgid, f->op, f->val); |
204 | break; | 202 | break; |
205 | case AUDIT_PERS: | 203 | case AUDIT_PERS: |
206 | result = audit_comparator(tsk->personality, op, value); | 204 | result = audit_comparator(tsk->personality, f->op, f->val); |
207 | break; | 205 | break; |
208 | case AUDIT_ARCH: | 206 | case AUDIT_ARCH: |
209 | if (ctx) | 207 | if (ctx) |
210 | result = audit_comparator(ctx->arch, op, value); | 208 | result = audit_comparator(ctx->arch, f->op, f->val); |
211 | break; | 209 | break; |
212 | 210 | ||
213 | case AUDIT_EXIT: | 211 | case AUDIT_EXIT: |
214 | if (ctx && ctx->return_valid) | 212 | if (ctx && ctx->return_valid) |
215 | result = audit_comparator(ctx->return_code, op, value); | 213 | result = audit_comparator(ctx->return_code, f->op, f->val); |
216 | break; | 214 | break; |
217 | case AUDIT_SUCCESS: | 215 | case AUDIT_SUCCESS: |
218 | if (ctx && ctx->return_valid) { | 216 | if (ctx && ctx->return_valid) { |
219 | if (value) | 217 | if (f->val) |
220 | result = audit_comparator(ctx->return_valid, op, AUDITSC_SUCCESS); | 218 | result = audit_comparator(ctx->return_valid, f->op, AUDITSC_SUCCESS); |
221 | else | 219 | else |
222 | result = audit_comparator(ctx->return_valid, op, AUDITSC_FAILURE); | 220 | result = audit_comparator(ctx->return_valid, f->op, AUDITSC_FAILURE); |
223 | } | 221 | } |
224 | break; | 222 | break; |
225 | case AUDIT_DEVMAJOR: | 223 | case AUDIT_DEVMAJOR: |
226 | if (ctx) { | 224 | if (ctx) { |
227 | for (j = 0; j < ctx->name_count; j++) { | 225 | for (j = 0; j < ctx->name_count; j++) { |
228 | if (audit_comparator(MAJOR(ctx->names[j].dev), op, value)) { | 226 | if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) { |
229 | ++result; | 227 | ++result; |
230 | break; | 228 | break; |
231 | } | 229 | } |
@@ -235,7 +233,7 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
235 | case AUDIT_DEVMINOR: | 233 | case AUDIT_DEVMINOR: |
236 | if (ctx) { | 234 | if (ctx) { |
237 | for (j = 0; j < ctx->name_count; j++) { | 235 | for (j = 0; j < ctx->name_count; j++) { |
238 | if (audit_comparator(MINOR(ctx->names[j].dev), op, value)) { | 236 | if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) { |
239 | ++result; | 237 | ++result; |
240 | break; | 238 | break; |
241 | } | 239 | } |
@@ -245,8 +243,8 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
245 | case AUDIT_INODE: | 243 | case AUDIT_INODE: |
246 | if (ctx) { | 244 | if (ctx) { |
247 | for (j = 0; j < ctx->name_count; j++) { | 245 | for (j = 0; j < ctx->name_count; j++) { |
248 | if (audit_comparator(ctx->names[j].ino, op, value) || | 246 | if (audit_comparator(ctx->names[j].ino, f->op, f->val) || |
249 | audit_comparator(ctx->names[j].pino, op, value)) { | 247 | audit_comparator(ctx->names[j].pino, f->op, f->val)) { |
250 | ++result; | 248 | ++result; |
251 | break; | 249 | break; |
252 | } | 250 | } |
@@ -256,14 +254,14 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
256 | case AUDIT_LOGINUID: | 254 | case AUDIT_LOGINUID: |
257 | result = 0; | 255 | result = 0; |
258 | if (ctx) | 256 | if (ctx) |
259 | result = audit_comparator(ctx->loginuid, op, value); | 257 | result = audit_comparator(ctx->loginuid, f->op, f->val); |
260 | break; | 258 | break; |
261 | case AUDIT_ARG0: | 259 | case AUDIT_ARG0: |
262 | case AUDIT_ARG1: | 260 | case AUDIT_ARG1: |
263 | case AUDIT_ARG2: | 261 | case AUDIT_ARG2: |
264 | case AUDIT_ARG3: | 262 | case AUDIT_ARG3: |
265 | if (ctx) | 263 | if (ctx) |
266 | result = audit_comparator(ctx->argv[field-AUDIT_ARG0], op, value); | 264 | result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val); |
267 | break; | 265 | break; |
268 | } | 266 | } |
269 | 267 | ||