diff options
author | Dustin Kirkland <dustin.kirkland@us.ibm.com> | 2005-11-03 12:15:16 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2006-03-20 14:08:54 -0500 |
commit | 8c8570fb8feef2bc166bee75a85748b25cda22d9 (patch) | |
tree | ed783d405ea9d5f3d3ccc57fb56c7b7cb2cdfb82 /kernel | |
parent | c8edc80c8b8c397c53f4f659a05b9ea6208029bf (diff) |
[PATCH] Capture selinux subject/object context information.
This patch extends existing audit records with subject/object context
information. Audit records associated with filesystem inodes, ipc, and
tasks now contain SELinux label information in the field "subj" if the
item is performing the action, or in "obj" if the item is the receiver
of an action.
These labels are collected via hooks in SELinux and appended to the
appropriate record in the audit code.
This additional information is required for Common Criteria Labeled
Security Protection Profile (LSPP).
[AV: fixed kmalloc flags use]
[folded leak fixes]
[folded cleanup from akpm (kfree(NULL)]
[folded audit_inode_context() leak fix]
[folded akpm's fix for audit_ipc_perm() definition in case of !CONFIG_AUDIT]
Signed-off-by: Dustin Kirkland <dustin.kirkland@us.ibm.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/audit.c | 2 | ||||
-rw-r--r-- | kernel/auditsc.c | 142 |
2 files changed, 135 insertions, 9 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index 1c3eb1b12bfc..45c123ef77a7 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -142,7 +142,7 @@ static void audit_set_pid(struct audit_buffer *ab, pid_t pid) | |||
142 | nlh->nlmsg_pid = pid; | 142 | nlh->nlmsg_pid = pid; |
143 | } | 143 | } |
144 | 144 | ||
145 | static void audit_panic(const char *message) | 145 | void audit_panic(const char *message) |
146 | { | 146 | { |
147 | switch (audit_failure) | 147 | switch (audit_failure) |
148 | { | 148 | { |
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 31917ac730af..4e2256ec7cf3 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -34,6 +34,9 @@ | |||
34 | * | 34 | * |
35 | * Modified by Amy Griffis <amy.griffis@hp.com> to collect additional | 35 | * Modified by Amy Griffis <amy.griffis@hp.com> to collect additional |
36 | * filesystem information. | 36 | * filesystem information. |
37 | * | ||
38 | * Subject and object context labeling support added by <danjones@us.ibm.com> | ||
39 | * and <dustin.kirkland@us.ibm.com> for LSPP certification compliance. | ||
37 | */ | 40 | */ |
38 | 41 | ||
39 | #include <linux/init.h> | 42 | #include <linux/init.h> |
@@ -53,6 +56,7 @@ | |||
53 | #include <linux/netlink.h> | 56 | #include <linux/netlink.h> |
54 | #include <linux/compiler.h> | 57 | #include <linux/compiler.h> |
55 | #include <asm/unistd.h> | 58 | #include <asm/unistd.h> |
59 | #include <linux/security.h> | ||
56 | 60 | ||
57 | /* 0 = no checking | 61 | /* 0 = no checking |
58 | 1 = put_count checking | 62 | 1 = put_count checking |
@@ -109,6 +113,7 @@ struct audit_names { | |||
109 | uid_t uid; | 113 | uid_t uid; |
110 | gid_t gid; | 114 | gid_t gid; |
111 | dev_t rdev; | 115 | dev_t rdev; |
116 | char *ctx; | ||
112 | }; | 117 | }; |
113 | 118 | ||
114 | struct audit_aux_data { | 119 | struct audit_aux_data { |
@@ -125,6 +130,7 @@ struct audit_aux_data_ipcctl { | |||
125 | uid_t uid; | 130 | uid_t uid; |
126 | gid_t gid; | 131 | gid_t gid; |
127 | mode_t mode; | 132 | mode_t mode; |
133 | char *ctx; | ||
128 | }; | 134 | }; |
129 | 135 | ||
130 | struct audit_aux_data_socketcall { | 136 | struct audit_aux_data_socketcall { |
@@ -743,10 +749,11 @@ static inline void audit_free_names(struct audit_context *context) | |||
743 | context->serial, context->major, context->in_syscall, | 749 | context->serial, context->major, context->in_syscall, |
744 | context->name_count, context->put_count, | 750 | context->name_count, context->put_count, |
745 | context->ino_count); | 751 | context->ino_count); |
746 | for (i = 0; i < context->name_count; i++) | 752 | for (i = 0; i < context->name_count; i++) { |
747 | printk(KERN_ERR "names[%d] = %p = %s\n", i, | 753 | printk(KERN_ERR "names[%d] = %p = %s\n", i, |
748 | context->names[i].name, | 754 | context->names[i].name, |
749 | context->names[i].name ?: "(null)"); | 755 | context->names[i].name ?: "(null)"); |
756 | } | ||
750 | dump_stack(); | 757 | dump_stack(); |
751 | return; | 758 | return; |
752 | } | 759 | } |
@@ -756,9 +763,13 @@ static inline void audit_free_names(struct audit_context *context) | |||
756 | context->ino_count = 0; | 763 | context->ino_count = 0; |
757 | #endif | 764 | #endif |
758 | 765 | ||
759 | for (i = 0; i < context->name_count; i++) | 766 | for (i = 0; i < context->name_count; i++) { |
767 | char *p = context->names[i].ctx; | ||
768 | context->names[i].ctx = NULL; | ||
769 | kfree(p); | ||
760 | if (context->names[i].name) | 770 | if (context->names[i].name) |
761 | __putname(context->names[i].name); | 771 | __putname(context->names[i].name); |
772 | } | ||
762 | context->name_count = 0; | 773 | context->name_count = 0; |
763 | if (context->pwd) | 774 | if (context->pwd) |
764 | dput(context->pwd); | 775 | dput(context->pwd); |
@@ -778,6 +789,12 @@ static inline void audit_free_aux(struct audit_context *context) | |||
778 | dput(axi->dentry); | 789 | dput(axi->dentry); |
779 | mntput(axi->mnt); | 790 | mntput(axi->mnt); |
780 | } | 791 | } |
792 | if ( aux->type == AUDIT_IPC ) { | ||
793 | struct audit_aux_data_ipcctl *axi = (void *)aux; | ||
794 | if (axi->ctx) | ||
795 | kfree(axi->ctx); | ||
796 | } | ||
797 | |||
781 | context->aux = aux->next; | 798 | context->aux = aux->next; |
782 | kfree(aux); | 799 | kfree(aux); |
783 | } | 800 | } |
@@ -862,7 +879,38 @@ static inline void audit_free_context(struct audit_context *context) | |||
862 | printk(KERN_ERR "audit: freed %d contexts\n", count); | 879 | printk(KERN_ERR "audit: freed %d contexts\n", count); |
863 | } | 880 | } |
864 | 881 | ||
865 | static void audit_log_task_info(struct audit_buffer *ab) | 882 | static void audit_log_task_context(struct audit_buffer *ab, gfp_t gfp_mask) |
883 | { | ||
884 | char *ctx = NULL; | ||
885 | ssize_t len = 0; | ||
886 | |||
887 | len = security_getprocattr(current, "current", NULL, 0); | ||
888 | if (len < 0) { | ||
889 | if (len != -EINVAL) | ||
890 | goto error_path; | ||
891 | return; | ||
892 | } | ||
893 | |||
894 | ctx = kmalloc(len, gfp_mask); | ||
895 | if (!ctx) { | ||
896 | goto error_path; | ||
897 | return; | ||
898 | } | ||
899 | |||
900 | len = security_getprocattr(current, "current", ctx, len); | ||
901 | if (len < 0 ) | ||
902 | goto error_path; | ||
903 | |||
904 | audit_log_format(ab, " subj=%s", ctx); | ||
905 | |||
906 | error_path: | ||
907 | if (ctx) | ||
908 | kfree(ctx); | ||
909 | audit_panic("security_getprocattr error in audit_log_task_context"); | ||
910 | return; | ||
911 | } | ||
912 | |||
913 | static void audit_log_task_info(struct audit_buffer *ab, gfp_t gfp_mask) | ||
866 | { | 914 | { |
867 | char name[sizeof(current->comm)]; | 915 | char name[sizeof(current->comm)]; |
868 | struct mm_struct *mm = current->mm; | 916 | struct mm_struct *mm = current->mm; |
@@ -875,6 +923,10 @@ static void audit_log_task_info(struct audit_buffer *ab) | |||
875 | if (!mm) | 923 | if (!mm) |
876 | return; | 924 | return; |
877 | 925 | ||
926 | /* | ||
927 | * this is brittle; all callers that pass GFP_ATOMIC will have | ||
928 | * NULL current->mm and we won't get here. | ||
929 | */ | ||
878 | down_read(&mm->mmap_sem); | 930 | down_read(&mm->mmap_sem); |
879 | vma = mm->mmap; | 931 | vma = mm->mmap; |
880 | while (vma) { | 932 | while (vma) { |
@@ -888,6 +940,7 @@ static void audit_log_task_info(struct audit_buffer *ab) | |||
888 | vma = vma->vm_next; | 940 | vma = vma->vm_next; |
889 | } | 941 | } |
890 | up_read(&mm->mmap_sem); | 942 | up_read(&mm->mmap_sem); |
943 | audit_log_task_context(ab, gfp_mask); | ||
891 | } | 944 | } |
892 | 945 | ||
893 | static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) | 946 | static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) |
@@ -923,7 +976,7 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) | |||
923 | context->gid, | 976 | context->gid, |
924 | context->euid, context->suid, context->fsuid, | 977 | context->euid, context->suid, context->fsuid, |
925 | context->egid, context->sgid, context->fsgid); | 978 | context->egid, context->sgid, context->fsgid); |
926 | audit_log_task_info(ab); | 979 | audit_log_task_info(ab, gfp_mask); |
927 | audit_log_end(ab); | 980 | audit_log_end(ab); |
928 | 981 | ||
929 | for (aux = context->aux; aux; aux = aux->next) { | 982 | for (aux = context->aux; aux; aux = aux->next) { |
@@ -936,8 +989,8 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) | |||
936 | case AUDIT_IPC: { | 989 | case AUDIT_IPC: { |
937 | struct audit_aux_data_ipcctl *axi = (void *)aux; | 990 | struct audit_aux_data_ipcctl *axi = (void *)aux; |
938 | audit_log_format(ab, | 991 | audit_log_format(ab, |
939 | " qbytes=%lx iuid=%u igid=%u mode=%x", | 992 | " qbytes=%lx iuid=%u igid=%u mode=%x obj=%s", |
940 | axi->qbytes, axi->uid, axi->gid, axi->mode); | 993 | axi->qbytes, axi->uid, axi->gid, axi->mode, axi->ctx); |
941 | break; } | 994 | break; } |
942 | 995 | ||
943 | case AUDIT_SOCKETCALL: { | 996 | case AUDIT_SOCKETCALL: { |
@@ -1001,6 +1054,11 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask) | |||
1001 | context->names[i].gid, | 1054 | context->names[i].gid, |
1002 | MAJOR(context->names[i].rdev), | 1055 | MAJOR(context->names[i].rdev), |
1003 | MINOR(context->names[i].rdev)); | 1056 | MINOR(context->names[i].rdev)); |
1057 | if (context->names[i].ctx) { | ||
1058 | audit_log_format(ab, " obj=%s", | ||
1059 | context->names[i].ctx); | ||
1060 | } | ||
1061 | |||
1004 | audit_log_end(ab); | 1062 | audit_log_end(ab); |
1005 | } | 1063 | } |
1006 | } | 1064 | } |
@@ -1243,6 +1301,39 @@ void audit_putname(const char *name) | |||
1243 | #endif | 1301 | #endif |
1244 | } | 1302 | } |
1245 | 1303 | ||
1304 | void audit_inode_context(int idx, const struct inode *inode) | ||
1305 | { | ||
1306 | struct audit_context *context = current->audit_context; | ||
1307 | char *ctx = NULL; | ||
1308 | int len = 0; | ||
1309 | |||
1310 | if (!security_inode_xattr_getsuffix()) | ||
1311 | return; | ||
1312 | |||
1313 | len = security_inode_getsecurity(inode, (char *)security_inode_xattr_getsuffix(), NULL, 0, 0); | ||
1314 | if (len < 0) | ||
1315 | goto error_path; | ||
1316 | |||
1317 | ctx = kmalloc(len, GFP_KERNEL); | ||
1318 | if (!ctx) | ||
1319 | goto error_path; | ||
1320 | |||
1321 | len = security_inode_getsecurity(inode, (char *)security_inode_xattr_getsuffix(), ctx, len, 0); | ||
1322 | if (len < 0) | ||
1323 | goto error_path; | ||
1324 | |||
1325 | kfree(context->names[idx].ctx); | ||
1326 | context->names[idx].ctx = ctx; | ||
1327 | return; | ||
1328 | |||
1329 | error_path: | ||
1330 | if (ctx) | ||
1331 | kfree(ctx); | ||
1332 | audit_panic("error in audit_inode_context"); | ||
1333 | return; | ||
1334 | } | ||
1335 | |||
1336 | |||
1246 | /** | 1337 | /** |
1247 | * audit_inode - store the inode and device from a lookup | 1338 | * audit_inode - store the inode and device from a lookup |
1248 | * @name: name being audited | 1339 | * @name: name being audited |
@@ -1282,6 +1373,7 @@ void __audit_inode(const char *name, const struct inode *inode, unsigned flags) | |||
1282 | context->names[idx].uid = inode->i_uid; | 1373 | context->names[idx].uid = inode->i_uid; |
1283 | context->names[idx].gid = inode->i_gid; | 1374 | context->names[idx].gid = inode->i_gid; |
1284 | context->names[idx].rdev = inode->i_rdev; | 1375 | context->names[idx].rdev = inode->i_rdev; |
1376 | audit_inode_context(idx, inode); | ||
1285 | if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) && | 1377 | if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) && |
1286 | (strcmp(name, ".") != 0)) { | 1378 | (strcmp(name, ".") != 0)) { |
1287 | context->names[idx].ino = (unsigned long)-1; | 1379 | context->names[idx].ino = (unsigned long)-1; |
@@ -1363,6 +1455,7 @@ update_context: | |||
1363 | context->names[idx].uid = inode->i_uid; | 1455 | context->names[idx].uid = inode->i_uid; |
1364 | context->names[idx].gid = inode->i_gid; | 1456 | context->names[idx].gid = inode->i_gid; |
1365 | context->names[idx].rdev = inode->i_rdev; | 1457 | context->names[idx].rdev = inode->i_rdev; |
1458 | audit_inode_context(idx, inode); | ||
1366 | } | 1459 | } |
1367 | } | 1460 | } |
1368 | 1461 | ||
@@ -1423,6 +1516,38 @@ uid_t audit_get_loginuid(struct audit_context *ctx) | |||
1423 | return ctx ? ctx->loginuid : -1; | 1516 | return ctx ? ctx->loginuid : -1; |
1424 | } | 1517 | } |
1425 | 1518 | ||
1519 | static char *audit_ipc_context(struct kern_ipc_perm *ipcp) | ||
1520 | { | ||
1521 | struct audit_context *context = current->audit_context; | ||
1522 | char *ctx = NULL; | ||
1523 | int len = 0; | ||
1524 | |||
1525 | if (likely(!context)) | ||
1526 | return NULL; | ||
1527 | |||
1528 | len = security_ipc_getsecurity(ipcp, NULL, 0); | ||
1529 | if (len == -EOPNOTSUPP) | ||
1530 | goto ret; | ||
1531 | if (len < 0) | ||
1532 | goto error_path; | ||
1533 | |||
1534 | ctx = kmalloc(len, GFP_ATOMIC); | ||
1535 | if (!ctx) | ||
1536 | goto error_path; | ||
1537 | |||
1538 | len = security_ipc_getsecurity(ipcp, ctx, len); | ||
1539 | if (len < 0) | ||
1540 | goto error_path; | ||
1541 | |||
1542 | return ctx; | ||
1543 | |||
1544 | error_path: | ||
1545 | kfree(ctx); | ||
1546 | audit_panic("error in audit_ipc_context"); | ||
1547 | ret: | ||
1548 | return NULL; | ||
1549 | } | ||
1550 | |||
1426 | /** | 1551 | /** |
1427 | * audit_ipc_perms - record audit data for ipc | 1552 | * audit_ipc_perms - record audit data for ipc |
1428 | * @qbytes: msgq bytes | 1553 | * @qbytes: msgq bytes |
@@ -1432,7 +1557,7 @@ uid_t audit_get_loginuid(struct audit_context *ctx) | |||
1432 | * | 1557 | * |
1433 | * Returns 0 for success or NULL context or < 0 on error. | 1558 | * Returns 0 for success or NULL context or < 0 on error. |
1434 | */ | 1559 | */ |
1435 | int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) | 1560 | int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp) |
1436 | { | 1561 | { |
1437 | struct audit_aux_data_ipcctl *ax; | 1562 | struct audit_aux_data_ipcctl *ax; |
1438 | struct audit_context *context = current->audit_context; | 1563 | struct audit_context *context = current->audit_context; |
@@ -1440,7 +1565,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) | |||
1440 | if (likely(!context)) | 1565 | if (likely(!context)) |
1441 | return 0; | 1566 | return 0; |
1442 | 1567 | ||
1443 | ax = kmalloc(sizeof(*ax), GFP_KERNEL); | 1568 | ax = kmalloc(sizeof(*ax), GFP_ATOMIC); |
1444 | if (!ax) | 1569 | if (!ax) |
1445 | return -ENOMEM; | 1570 | return -ENOMEM; |
1446 | 1571 | ||
@@ -1448,6 +1573,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) | |||
1448 | ax->uid = uid; | 1573 | ax->uid = uid; |
1449 | ax->gid = gid; | 1574 | ax->gid = gid; |
1450 | ax->mode = mode; | 1575 | ax->mode = mode; |
1576 | ax->ctx = audit_ipc_context(ipcp); | ||
1451 | 1577 | ||
1452 | ax->d.type = AUDIT_IPC; | 1578 | ax->d.type = AUDIT_IPC; |
1453 | ax->d.next = context->aux; | 1579 | ax->d.next = context->aux; |