diff options
Diffstat (limited to 'kernel/auditsc.c')
-rw-r--r-- | kernel/auditsc.c | 124 |
1 files changed, 88 insertions, 36 deletions
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; |