aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/auditsc.c
diff options
context:
space:
mode:
authorAmy Griffis <amy.griffis@hp.com>2006-04-07 16:55:56 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2006-06-20 05:25:27 -0400
commitf368c07d7214a7c41dfceb76c8db473b850f0229 (patch)
treee3f1e2d1a6ffbe61bf99ece51b906654728db4c9 /kernel/auditsc.c
parent20ca73bc792be9625af184cbec36e1372611d1c3 (diff)
[PATCH] audit: path-based rules
In this implementation, audit registers inotify watches on the parent directories of paths specified in audit rules. When audit's inotify event handler is called, it updates any affected rules based on the filesystem event. If the parent directory is renamed, removed, or its filesystem is unmounted, audit removes all rules referencing that inotify watch. To keep things simple, this implementation limits location-based auditing to the directory entries in an existing directory. Given a path-based rule for /foo/bar/passwd, the following table applies: passwd modified -- audit event logged passwd replaced -- audit event logged, rules list updated bar renamed -- rule removed foo renamed -- untracked, meaning that the rule now applies to the new location Audit users typically want to have many rules referencing filesystem objects, which can significantly impact filtering performance. This patch also adds an inode-number-based rule hash to mitigate this situation. The patch is relative to the audit git tree: http://kernel.org/git/?p=linux/kernel/git/viro/audit-current.git;a=summary and uses the inotify kernel API: http://lkml.org/lkml/2006/6/1/145 Signed-off-by: Amy Griffis <amy.griffis@hp.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'kernel/auditsc.c')
-rw-r--r--kernel/auditsc.c124
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. */
206static int audit_filter_rules(struct task_struct *tsk, 206static 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 */
420enum 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
453void audit_set_auditable(struct audit_context *ctx)
454{
455 ctx->auditable = 1;
456}
457
398static inline struct audit_context *audit_get_context(struct task_struct *tsk, 458static 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
484get_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
1227no_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;