diff options
author | Amy Griffis <amy.griffis@hp.com> | 2005-11-03 11:00:25 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2006-03-20 14:08:53 -0500 |
commit | 73241ccca0f7786933f1d31b3d86f2456549953a (patch) | |
tree | daa7efabfb7aa2f511a467606786820949e8763e /kernel | |
parent | f38aa94224c5517a40ba56d453779f70d3229803 (diff) |
[PATCH] Collect more inode information during syscall processing.
This patch augments the collection of inode info during syscall
processing. It represents part of the functionality that was provided
by the auditfs patch included in RHEL4.
Specifically, it:
- Collects information for target inodes created or removed during
syscalls. Previous code only collects information for the target
inode's parent.
- Adds the audit_inode() hook to syscalls that operate on a file
descriptor (e.g. fchown), enabling audit to do inode filtering for
these calls.
- Modifies filtering code to check audit context for either an inode #
or a parent inode # matching a given rule.
- Modifies logging to provide inode # for both parent and child.
- Protect debug info from NULL audit_names.name.
[AV: folded a later typo fix from the same author]
Signed-off-by: Amy Griffis <amy.griffis@hp.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/auditsc.c | 142 |
1 files changed, 118 insertions, 24 deletions
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 55ba331757c5..73f932b7deb6 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Handles all system-call specific auditing features. | 2 | * Handles all system-call specific auditing features. |
3 | * | 3 | * |
4 | * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. | 4 | * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. |
5 | * Copyright 2005 Hewlett-Packard Development Company, L.P. | ||
5 | * Copyright (C) 2005 IBM Corporation | 6 | * Copyright (C) 2005 IBM Corporation |
6 | * All Rights Reserved. | 7 | * All Rights Reserved. |
7 | * | 8 | * |
@@ -31,11 +32,16 @@ | |||
31 | * The support of additional filter rules compares (>, <, >=, <=) was | 32 | * The support of additional filter rules compares (>, <, >=, <=) was |
32 | * added by Dustin Kirkland <dustin.kirkland@us.ibm.com>, 2005. | 33 | * added by Dustin Kirkland <dustin.kirkland@us.ibm.com>, 2005. |
33 | * | 34 | * |
35 | * Modified by Amy Griffis <amy.griffis@hp.com> to collect additional | ||
36 | * filesystem information. | ||
34 | */ | 37 | */ |
35 | 38 | ||
36 | #include <linux/init.h> | 39 | #include <linux/init.h> |
37 | #include <asm/types.h> | 40 | #include <asm/types.h> |
38 | #include <asm/atomic.h> | 41 | #include <asm/atomic.h> |
42 | #include <asm/types.h> | ||
43 | #include <linux/fs.h> | ||
44 | #include <linux/namei.h> | ||
39 | #include <linux/mm.h> | 45 | #include <linux/mm.h> |
40 | #include <linux/module.h> | 46 | #include <linux/module.h> |
41 | #include <linux/mount.h> | 47 | #include <linux/mount.h> |
@@ -97,12 +103,12 @@ enum audit_state { | |||
97 | struct audit_names { | 103 | struct audit_names { |
98 | const char *name; | 104 | const char *name; |
99 | unsigned long ino; | 105 | unsigned long ino; |
106 | unsigned long pino; | ||
100 | dev_t dev; | 107 | dev_t dev; |
101 | umode_t mode; | 108 | umode_t mode; |
102 | uid_t uid; | 109 | uid_t uid; |
103 | gid_t gid; | 110 | gid_t gid; |
104 | dev_t rdev; | 111 | dev_t rdev; |
105 | unsigned flags; | ||
106 | }; | 112 | }; |
107 | 113 | ||
108 | struct audit_aux_data { | 114 | struct audit_aux_data { |
@@ -515,7 +521,8 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
515 | case AUDIT_INODE: | 521 | case AUDIT_INODE: |
516 | if (ctx) { | 522 | if (ctx) { |
517 | for (j = 0; j < ctx->name_count; j++) { | 523 | for (j = 0; j < ctx->name_count; j++) { |
518 | if ( audit_comparator(ctx->names[j].ino, op, value)) { | 524 | if (audit_comparator(ctx->names[j].ino, op, value) || |
525 | audit_comparator(ctx->names[j].pino, op, value)) { | ||
519 | ++result; | 526 | ++result; |
520 | break; | 527 | break; |
521 | } | 528 | } |
@@ -696,17 +703,17 @@ static inline void audit_free_names(struct audit_context *context) | |||
696 | #if AUDIT_DEBUG == 2 | 703 | #if AUDIT_DEBUG == 2 |
697 | if (context->auditable | 704 | if (context->auditable |
698 | ||context->put_count + context->ino_count != context->name_count) { | 705 | ||context->put_count + context->ino_count != context->name_count) { |
699 | printk(KERN_ERR "audit.c:%d(:%d): major=%d in_syscall=%d" | 706 | printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d" |
700 | " name_count=%d put_count=%d" | 707 | " name_count=%d put_count=%d" |
701 | " ino_count=%d [NOT freeing]\n", | 708 | " ino_count=%d [NOT freeing]\n", |
702 | __LINE__, | 709 | __FILE__, __LINE__, |
703 | context->serial, context->major, context->in_syscall, | 710 | context->serial, context->major, context->in_syscall, |
704 | context->name_count, context->put_count, | 711 | context->name_count, context->put_count, |
705 | context->ino_count); | 712 | context->ino_count); |
706 | for (i = 0; i < context->name_count; i++) | 713 | for (i = 0; i < context->name_count; i++) |
707 | printk(KERN_ERR "names[%d] = %p = %s\n", i, | 714 | printk(KERN_ERR "names[%d] = %p = %s\n", i, |
708 | context->names[i].name, | 715 | context->names[i].name, |
709 | context->names[i].name); | 716 | context->names[i].name ?: "(null)"); |
710 | dump_stack(); | 717 | dump_stack(); |
711 | return; | 718 | return; |
712 | } | 719 | } |
@@ -932,27 +939,34 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) | |||
932 | } | 939 | } |
933 | } | 940 | } |
934 | for (i = 0; i < context->name_count; i++) { | 941 | for (i = 0; i < context->name_count; i++) { |
942 | unsigned long ino = context->names[i].ino; | ||
943 | unsigned long pino = context->names[i].pino; | ||
944 | |||
935 | ab = audit_log_start(context, gfp_mask, AUDIT_PATH); | 945 | ab = audit_log_start(context, gfp_mask, AUDIT_PATH); |
936 | if (!ab) | 946 | if (!ab) |
937 | continue; /* audit_panic has been called */ | 947 | continue; /* audit_panic has been called */ |
938 | 948 | ||
939 | audit_log_format(ab, "item=%d", i); | 949 | audit_log_format(ab, "item=%d", i); |
940 | if (context->names[i].name) { | 950 | |
941 | audit_log_format(ab, " name="); | 951 | audit_log_format(ab, " name="); |
952 | if (context->names[i].name) | ||
942 | audit_log_untrustedstring(ab, context->names[i].name); | 953 | audit_log_untrustedstring(ab, context->names[i].name); |
943 | } | 954 | else |
944 | audit_log_format(ab, " flags=%x\n", context->names[i].flags); | 955 | audit_log_format(ab, "(null)"); |
945 | 956 | ||
946 | if (context->names[i].ino != (unsigned long)-1) | 957 | if (pino != (unsigned long)-1) |
947 | audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o" | 958 | audit_log_format(ab, " parent=%lu", pino); |
948 | " ouid=%u ogid=%u rdev=%02x:%02x", | 959 | if (ino != (unsigned long)-1) |
949 | context->names[i].ino, | 960 | audit_log_format(ab, " inode=%lu", ino); |
950 | MAJOR(context->names[i].dev), | 961 | if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1)) |
951 | MINOR(context->names[i].dev), | 962 | audit_log_format(ab, " dev=%02x:%02x mode=%#o" |
952 | context->names[i].mode, | 963 | " ouid=%u ogid=%u rdev=%02x:%02x", |
953 | context->names[i].uid, | 964 | MAJOR(context->names[i].dev), |
954 | context->names[i].gid, | 965 | MINOR(context->names[i].dev), |
955 | MAJOR(context->names[i].rdev), | 966 | context->names[i].mode, |
967 | context->names[i].uid, | ||
968 | context->names[i].gid, | ||
969 | MAJOR(context->names[i].rdev), | ||
956 | MINOR(context->names[i].rdev)); | 970 | MINOR(context->names[i].rdev)); |
957 | audit_log_end(ab); | 971 | audit_log_end(ab); |
958 | } | 972 | } |
@@ -1174,7 +1188,7 @@ void audit_putname(const char *name) | |||
1174 | for (i = 0; i < context->name_count; i++) | 1188 | for (i = 0; i < context->name_count; i++) |
1175 | printk(KERN_ERR "name[%d] = %p = %s\n", i, | 1189 | printk(KERN_ERR "name[%d] = %p = %s\n", i, |
1176 | context->names[i].name, | 1190 | context->names[i].name, |
1177 | context->names[i].name); | 1191 | context->names[i].name ?: "(null)"); |
1178 | } | 1192 | } |
1179 | #endif | 1193 | #endif |
1180 | __putname(name); | 1194 | __putname(name); |
@@ -1204,7 +1218,7 @@ void audit_putname(const char *name) | |||
1204 | * | 1218 | * |
1205 | * Called from fs/namei.c:path_lookup(). | 1219 | * Called from fs/namei.c:path_lookup(). |
1206 | */ | 1220 | */ |
1207 | void audit_inode(const char *name, const struct inode *inode, unsigned flags) | 1221 | void __audit_inode(const char *name, const struct inode *inode, unsigned flags) |
1208 | { | 1222 | { |
1209 | int idx; | 1223 | int idx; |
1210 | struct audit_context *context = current->audit_context; | 1224 | struct audit_context *context = current->audit_context; |
@@ -1230,13 +1244,93 @@ void audit_inode(const char *name, const struct inode *inode, unsigned flags) | |||
1230 | ++context->ino_count; | 1244 | ++context->ino_count; |
1231 | #endif | 1245 | #endif |
1232 | } | 1246 | } |
1233 | context->names[idx].flags = flags; | ||
1234 | context->names[idx].ino = inode->i_ino; | ||
1235 | context->names[idx].dev = inode->i_sb->s_dev; | 1247 | context->names[idx].dev = inode->i_sb->s_dev; |
1236 | context->names[idx].mode = inode->i_mode; | 1248 | context->names[idx].mode = inode->i_mode; |
1237 | context->names[idx].uid = inode->i_uid; | 1249 | context->names[idx].uid = inode->i_uid; |
1238 | context->names[idx].gid = inode->i_gid; | 1250 | context->names[idx].gid = inode->i_gid; |
1239 | context->names[idx].rdev = inode->i_rdev; | 1251 | context->names[idx].rdev = inode->i_rdev; |
1252 | if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) && | ||
1253 | (strcmp(name, ".") != 0)) { | ||
1254 | context->names[idx].ino = (unsigned long)-1; | ||
1255 | context->names[idx].pino = inode->i_ino; | ||
1256 | } else { | ||
1257 | context->names[idx].ino = inode->i_ino; | ||
1258 | context->names[idx].pino = (unsigned long)-1; | ||
1259 | } | ||
1260 | } | ||
1261 | |||
1262 | /** | ||
1263 | * audit_inode_child - collect inode info for created/removed objects | ||
1264 | * @dname: inode's dentry name | ||
1265 | * @inode: inode being audited | ||
1266 | * @pino: inode number of dentry parent | ||
1267 | * | ||
1268 | * For syscalls that create or remove filesystem objects, audit_inode | ||
1269 | * can only collect information for the filesystem object's parent. | ||
1270 | * This call updates the audit context with the child's information. | ||
1271 | * Syscalls that create a new filesystem object must be hooked after | ||
1272 | * the object is created. Syscalls that remove a filesystem object | ||
1273 | * must be hooked prior, in order to capture the target inode during | ||
1274 | * unsuccessful attempts. | ||
1275 | */ | ||
1276 | void __audit_inode_child(const char *dname, const struct inode *inode, | ||
1277 | unsigned long pino) | ||
1278 | { | ||
1279 | int idx; | ||
1280 | struct audit_context *context = current->audit_context; | ||
1281 | |||
1282 | if (!context->in_syscall) | ||
1283 | return; | ||
1284 | |||
1285 | /* determine matching parent */ | ||
1286 | if (dname) | ||
1287 | for (idx = 0; idx < context->name_count; idx++) | ||
1288 | if (context->names[idx].pino == pino) { | ||
1289 | const char *n; | ||
1290 | const char *name = context->names[idx].name; | ||
1291 | int dlen = strlen(dname); | ||
1292 | int nlen = name ? strlen(name) : 0; | ||
1293 | |||
1294 | if (nlen < dlen) | ||
1295 | continue; | ||
1296 | |||
1297 | /* disregard trailing slashes */ | ||
1298 | n = name + nlen - 1; | ||
1299 | while ((*n == '/') && (n > name)) | ||
1300 | n--; | ||
1301 | |||
1302 | /* find last path component */ | ||
1303 | n = n - dlen + 1; | ||
1304 | if (n < name) | ||
1305 | continue; | ||
1306 | else if (n > name) { | ||
1307 | if (*--n != '/') | ||
1308 | continue; | ||
1309 | else | ||
1310 | n++; | ||
1311 | } | ||
1312 | |||
1313 | if (strncmp(n, dname, dlen) == 0) | ||
1314 | goto update_context; | ||
1315 | } | ||
1316 | |||
1317 | /* catch-all in case match not found */ | ||
1318 | idx = context->name_count++; | ||
1319 | context->names[idx].name = NULL; | ||
1320 | context->names[idx].pino = pino; | ||
1321 | #if AUDIT_DEBUG | ||
1322 | context->ino_count++; | ||
1323 | #endif | ||
1324 | |||
1325 | update_context: | ||
1326 | if (inode) { | ||
1327 | context->names[idx].ino = inode->i_ino; | ||
1328 | context->names[idx].dev = inode->i_sb->s_dev; | ||
1329 | context->names[idx].mode = inode->i_mode; | ||
1330 | context->names[idx].uid = inode->i_uid; | ||
1331 | context->names[idx].gid = inode->i_gid; | ||
1332 | context->names[idx].rdev = inode->i_rdev; | ||
1333 | } | ||
1240 | } | 1334 | } |
1241 | 1335 | ||
1242 | /** | 1336 | /** |