diff options
Diffstat (limited to 'kernel/auditsc.c')
-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 | /** |