The attached patch contains functionality specified by the labeled
security protection profile--basically appending object context and
subject context labels to audit records.
This code was originally written by Dan Jones against a 2.6.9 kernel
tree. I have ported it forward to the 2.6.13-rc3-mm1 tree and tested
it.
I expect at least one section of this code to generate some animated
discussion. This code utilizes "static inline" function definitions
rather than "#define" macros, as per Documentation/SubmittingPatches
lines 328-372 and Documentation/CodingStyle line 358. This is
inconsistent with the rest of the code in include/linux/audit.h, so I
suspect someone will have to give, in the interest of consistency.
Please advise.
Additionally, we've arbitrarily defined AUDIT_SECURITY_CONTEXT = 1350.
We expect this to change to whatever the appropriate value might be.
Signed-off-by: Dustin Kirkland <dustin.kirkland(a)us.ibm.com>
diff -uprN linux-2.6.13-rc3-mm1/include/linux/audit.h
linux-2.6.13-rc3-mm1-lspp_audit/include/linux/audit.h
--- linux-2.6.13-rc3-mm1/include/linux/audit.h 2005-07-18 15:20:34.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/include/linux/audit.h 2005-07-19 11:33:37.000000000
-0500
@@ -68,6 +68,7 @@
#define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */
#define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */
#define AUDIT_CWD 1307 /* Current working directory */
+#define AUDIT_SECURITY_CONTEXT 1350 /* security context */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
@@ -238,6 +239,8 @@ extern int audit_sockaddr(int len, void
extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
extern void audit_signal_info(int sig, struct task_struct *t);
extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
+extern int audit_ipc_security_context(struct kern_ipc_perm *ipcp);
+extern int audit_set_macxattr(const char *name);
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
@@ -255,6 +258,14 @@ extern int audit_filter_user(struct netl
#define audit_avc_path(dentry, mnt) ({ 0; })
#define audit_signal_info(s,t) do { ; } while (0)
#define audit_filter_user(cb,t) ({ 1; })
+static inline int audit_ipc_security_context(struct kern_ipc_perm *ipcp)
+{
+ return 0;
+}
+static inline int audit_set_macxattr(const char *name)
+{
+ return 0;
+}
#endif
#ifdef CONFIG_AUDIT
@@ -283,6 +294,7 @@ extern void audit_send_reply(int pi
int done, int multi,
void *payload, int size);
extern void audit_log_lost(const char *message);
+extern void audit_panic(const char *message);
extern struct semaphore audit_netlink_sem;
#else
#define audit_log(c,g,t,f,...) do { ; } while (0)
@@ -293,6 +305,10 @@ extern struct semaphore audit_netlink_se
#define audit_log_hex(a,b,l) do { ; } while (0)
#define audit_log_untrustedstring(a,s) do { ; } while (0)
#define audit_log_d_path(b,p,d,v) do { ; } while (0)
+static inline void audit_panic(const char *message)
+{
+ return;
+}
#endif
#endif
#endif
diff -uprN linux-2.6.13-rc3-mm1/include/linux/security.h
linux-2.6.13-rc3-mm1-lspp_audit/include/linux/security.h
--- linux-2.6.13-rc3-mm1/include/linux/security.h 2005-07-18 15:20:35.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/include/linux/security.h 2005-07-18 16:35:02.000000000
-0500
@@ -792,6 +792,11 @@ struct swap_info_struct;
* @ipcp contains the kernel IPC permission structure
* @flag contains the desired (requested) permission set
* Return 0 if permission is granted.
+ * @ipc_getsecurity:
+ * Copy the security label associated with the ipc object into
+ * @buffer. @buffer may be NULL to request the size of the buffer
+ * required. @size indicates the size of @buffer in bytes. Return
+ * number of bytes used/required on success.
*
* Security hooks for individual messages held in System V IPC message queues
* @msg_msg_alloc_security:
@@ -1140,6 +1145,7 @@ struct security_operations {
void (*task_to_inode)(struct task_struct *p, struct inode *inode);
int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
+ int (*ipc_getsecurity)(struct kern_ipc_perm *ipcp, void *buffer, size_t size);
int (*msg_msg_alloc_security) (struct msg_msg * msg);
void (*msg_msg_free_security) (struct msg_msg * msg);
@@ -1775,6 +1781,11 @@ static inline int security_ipc_permissio
return security_ops->ipc_permission (ipcp, flag);
}
+static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer,
size_t size)
+{
+ return security_ops->ipc_getsecurity(ipcp, buffer, size);
+}
+
static inline int security_msg_msg_alloc (struct msg_msg * msg)
{
return security_ops->msg_msg_alloc_security (msg);
@@ -2405,6 +2416,11 @@ static inline int security_ipc_permissio
return 0;
}
+static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer,
size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int security_msg_msg_alloc (struct msg_msg * msg)
{
return 0;
diff -uprN linux-2.6.13-rc3-mm1/ipc/msg.c linux-2.6.13-rc3-mm1-lspp_audit/ipc/msg.c
--- linux-2.6.13-rc3-mm1/ipc/msg.c 2005-06-17 14:48:29.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/ipc/msg.c 2005-07-18 16:35:02.000000000 -0500
@@ -441,6 +441,13 @@ asmlinkage long sys_msgctl (int msqid, i
if (msq == NULL)
goto out_up;
+ ipcp = &msq->q_perm;
+
+ if (cmd == IPC_SET) {
+ if ((err = audit_ipc_security_context(ipcp)))
+ goto out_unlock_up;
+ }
+
err = -EIDRM;
if (msg_checkid(msq,msqid))
goto out_unlock_up;
diff -uprN linux-2.6.13-rc3-mm1/ipc/sem.c linux-2.6.13-rc3-mm1-lspp_audit/ipc/sem.c
--- linux-2.6.13-rc3-mm1/ipc/sem.c 2005-07-18 15:20:05.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/ipc/sem.c 2005-07-18 16:35:02.000000000 -0500
@@ -811,12 +811,18 @@ static int semctl_down(int semid, int se
if(sma==NULL)
return -EINVAL;
+ ipcp = &sma->sem_perm;
+
+ if(cmd == IPC_SET) {
+ if ((err = audit_ipc_security_context(ipcp)))
+ goto out_unlock;
+ }
+
if (sem_checkid(sma,semid)) {
err=-EIDRM;
goto out_unlock;
}
- ipcp = &sma->sem_perm;
-
+
if (current->euid != ipcp->cuid &&
current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
err=-EPERM;
diff -uprN linux-2.6.13-rc3-mm1/ipc/shm.c linux-2.6.13-rc3-mm1-lspp_audit/ipc/shm.c
--- linux-2.6.13-rc3-mm1/ipc/shm.c 2005-06-17 14:48:29.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/ipc/shm.c 2005-07-18 16:35:02.000000000 -0500
@@ -610,6 +610,9 @@ asmlinkage long sys_shmctl (int shmid, i
err=-EINVAL;
if(shp==NULL)
goto out_up;
+ err = audit_ipc_security_context(&(shp->shm_perm));
+ if(err)
+ goto out_unlock_up;
err = shm_checkid(shp,shmid);
if(err)
goto out_unlock_up;
diff -uprN linux-2.6.13-rc3-mm1/ipc/util.c linux-2.6.13-rc3-mm1-lspp_audit/ipc/util.c
--- linux-2.6.13-rc3-mm1/ipc/util.c 2005-06-17 14:48:29.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/ipc/util.c 2005-07-18 16:35:02.000000000 -0500
@@ -24,6 +24,7 @@
#include <linux/security.h>
#include <linux/rcupdate.h>
#include <linux/workqueue.h>
+#include <linux/audit.h>
#include <asm/unistd.h>
@@ -420,6 +421,8 @@ int ipcperms (struct kern_ipc_perm *ipcp
{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
int requested_mode, granted_mode;
+ audit_ipc_security_context(ipcp);
+
requested_mode = (flag >> 6) | (flag >> 3) | flag;
granted_mode = ipcp->mode;
if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
diff -uprN linux-2.6.13-rc3-mm1/kernel/audit.c
linux-2.6.13-rc3-mm1-lspp_audit/kernel/audit.c
--- linux-2.6.13-rc3-mm1/kernel/audit.c 2005-07-18 15:20:35.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/kernel/audit.c 2005-07-18 16:35:02.000000000 -0500
@@ -147,7 +147,7 @@ struct audit_entry {
struct audit_rule rule;
};
-static void audit_panic(const char *message)
+void audit_panic(const char *message)
{
switch (audit_failure)
{
@@ -896,3 +896,4 @@ void audit_log(struct audit_context *ctx
audit_log_end(ab);
}
}
+
diff -uprN linux-2.6.13-rc3-mm1/kernel/auditsc.c
linux-2.6.13-rc3-mm1-lspp_audit/kernel/auditsc.c
--- linux-2.6.13-rc3-mm1/kernel/auditsc.c 2005-07-18 15:20:35.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/kernel/auditsc.c 2005-07-19 11:50:15.000000000 -0500
@@ -43,6 +43,7 @@
#include <linux/netlink.h>
#include <linux/compiler.h>
#include <asm/unistd.h>
+#include <linux/security.h>
/* 0 = no checking
1 = put_count checking
@@ -99,6 +100,7 @@ struct audit_names {
gid_t gid;
dev_t rdev;
unsigned flags;
+ char *security_context;
};
struct audit_aux_data {
@@ -108,6 +110,12 @@ struct audit_aux_data {
#define AUDIT_AUX_IPCPERM 0
+struct audit_aux_data_security_context {
+ struct audit_aux_data d;
+ char *security_context;
+ size_t security_context_len;
+};
+
struct audit_aux_data_ipcctl {
struct audit_aux_data d;
struct ipc_perm p;
@@ -167,6 +175,8 @@ struct audit_context {
#endif
};
+static const char *audit_macxattr;
+
/* Public API */
/* There are three lists of rules -- one to search at task creation
* time, one to search at syscall entry time, and another to search at
@@ -650,9 +660,11 @@ static inline void audit_free_names(stru
context->ino_count = 0;
#endif
- for (i = 0; i < context->name_count; i++)
+ for (i = 0; i < context->name_count; i++) {
if (context->names[i].name)
__putname(context->names[i].name);
+ kfree(context->names[i].security_context);
+ }
context->name_count = 0;
if (context->pwd)
dput(context->pwd);
@@ -751,6 +763,37 @@ static inline void audit_free_context(st
printk(KERN_ERR "audit: freed %d contexts\n", count);
}
+static void audit_log_task_security_context(struct audit_buffer *ab)
+{
+ char *security_context;
+ ssize_t len = 0;
+
+ len = security_getprocattr(current, "current", NULL, 0);
+ if (len < 0) {
+ if (len != -EINVAL)
+ audit_panic("security_getprocattr error in
audit_log_task_security_context");
+ return;
+ }
+
+ security_context = kmalloc(len, GFP_KERNEL);
+ if (!security_context) {
+ audit_panic("memory allocation error in audit_log_task_security_context");
+ return;
+ }
+
+ len = security_getprocattr(current, "current", security_context, len);
+ if (len < 0 ) {
+ audit_panic("security_getprocattr error in
audit_log_task_security_context");
+ goto out;
+ }
+
+ audit_log_format(ab, " scontext=%s", security_context);
+
+out:
+ kfree(security_context);
+ return;
+}
+
static void audit_log_task_info(struct audit_buffer *ab)
{
char name[sizeof(current->comm)];
@@ -777,6 +820,7 @@ static void audit_log_task_info(struct a
vma = vma->vm_next;
}
up_read(&mm->mmap_sem);
+ audit_log_task_security_context(ab);
}
static void audit_log_exit(struct audit_context *context, unsigned int gfp_mask)
@@ -848,6 +892,11 @@ static void audit_log_exit(struct audit_
struct audit_aux_data_path *axi = (void *)aux;
audit_log_d_path(ab, "path=", axi->dentry, axi->mnt);
break; }
+ case AUDIT_SECURITY_CONTEXT: {
+ struct audit_aux_data_security_context *axi = (void *)aux;
+ audit_log_format(ab, " ocontext=%s", axi->security_context);
+ kfree(axi->security_context);
+ break; }
}
audit_log_end(ab);
@@ -883,6 +932,10 @@ static void audit_log_exit(struct audit_
context->names[i].gid,
MAJOR(context->names[i].rdev),
MINOR(context->names[i].rdev));
+ if (context->names[i].security_context) {
+ audit_log_format(ab, " ocontext=%s",
+ context->names[i].security_context);
+ }
audit_log_end(ab);
}
}
@@ -1098,6 +1151,37 @@ void audit_putname(const char *name)
#endif
}
+void audit_inode_security_context(int idx, const struct inode *inode)
+{
+ struct audit_context *context = current->audit_context;
+ int len = 0;
+
+ if (!audit_macxattr)
+ return;
+
+ len = security_inode_getsecurity((struct inode *)inode, audit_macxattr, NULL, 0);
+ if (len < 0) {
+ if (len != -EOPNOTSUPP)
+ audit_panic("security_inode_getsecurity error in
audit_inode_security_context");
+ return;
+ }
+
+ context->names[idx].security_context = kmalloc(len, GFP_KERNEL);
+ if (!(context->names[idx].security_context)) {
+ audit_panic("memory allocation error in audit_inode_security_context");
+ return;
+ }
+
+ len = security_inode_getsecurity((struct inode *)inode, audit_macxattr,
+ context->names[idx].security_context, len);
+ if (len < 0) {
+ kfree(context->names[idx].security_context);
+ audit_panic("security_inode_getsecurity error in
audit_inode_security_context");
+ }
+
+ return;
+}
+
/* Store the inode and device from a lookup. Called from
* fs/namei.c:path_lookup(). */
void audit_inode(const char *name, const struct inode *inode, unsigned flags)
@@ -1133,6 +1217,8 @@ void audit_inode(const char *name, const
context->names[idx].uid = inode->i_uid;
context->names[idx].gid = inode->i_gid;
context->names[idx].rdev = inode->i_rdev;
+
+ audit_inode_security_context(idx, inode);
}
void auditsc_get_stamp(struct audit_context *ctx,
@@ -1270,3 +1356,63 @@ void audit_signal_info(int sig, struct t
}
}
+int audit_ipc_security_context(struct kern_ipc_perm *ipcp)
+{
+ struct audit_aux_data_security_context *ax;
+ struct audit_context *context = current->audit_context;
+ int len = 0;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+ if (!ax) {
+ audit_panic("memory allocation error in audit_ipc_security_context");
+ return -ENOMEM;
+ }
+
+ len = security_ipc_getsecurity(ipcp, NULL, 0);
+ if (len < 0) {
+ if (len != -EOPNOTSUPP)
+ audit_panic("security_ipc_getsecurity error in
audit_ipc_security_context");
+ return len;
+ }
+
+ ax->security_context = kmalloc(len, GFP_ATOMIC);
+ if (!ax->security_context) {
+ audit_panic("memory allocation error in audit_ipc_security_context");
+ kfree(ax);
+ return -ENOMEM;
+ }
+
+ len = security_ipc_getsecurity(ipcp, ax->security_context, len);
+ if (len < 0) {
+ audit_panic("security_ipc_getsecurity error in audit_ipc_security_context");
+ kfree(ax->security_context);
+ kfree(ax);
+ return len;
+ }
+
+ ax->security_context_len = len;
+
+ ax->d.type = AUDIT_SECURITY_CONTEXT;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+/* Set the security XATTR name. This is needed for audit functions
+ * to obtain the security context of file system objects. */
+int audit_set_macxattr(const char *name)
+{
+ size_t len = strlen(name)+1;
+
+ if (audit_macxattr)
+ return -EINVAL;
+
+ audit_macxattr = kmalloc(len, GFP_KERNEL);
+ if (!audit_macxattr)
+ return -ENOMEM;
+ memcpy((void *)audit_macxattr, (const void *)name, len);
+ return 0;
+}
diff -uprN linux-2.6.13-rc3-mm1/security/dummy.c
linux-2.6.13-rc3-mm1-lspp_audit/security/dummy.c
--- linux-2.6.13-rc3-mm1/security/dummy.c 2005-07-18 15:20:36.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/security/dummy.c 2005-07-18 16:35:02.000000000 -0500
@@ -557,6 +557,11 @@ static int dummy_ipc_permission (struct
return 0;
}
+static int dummy_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
static int dummy_msg_msg_alloc_security (struct msg_msg *msg)
{
return 0;
@@ -907,6 +912,7 @@ void security_fixup_ops (struct security
set_to_dummy_if_null(ops, task_reparent_to_init);
set_to_dummy_if_null(ops, task_to_inode);
set_to_dummy_if_null(ops, ipc_permission);
+ set_to_dummy_if_null(ops, ipc_getsecurity);
set_to_dummy_if_null(ops, msg_msg_alloc_security);
set_to_dummy_if_null(ops, msg_msg_free_security);
set_to_dummy_if_null(ops, msg_queue_alloc_security);
diff -uprN linux-2.6.13-rc3-mm1/security/selinux/hooks.c
linux-2.6.13-rc3-mm1-lspp_audit/security/selinux/hooks.c
--- linux-2.6.13-rc3-mm1/security/selinux/hooks.c 2005-07-18 15:20:36.000000000 -0500
+++ linux-2.6.13-rc3-mm1-lspp_audit/security/selinux/hooks.c 2005-07-19 10:56:44.000000000
-0500
@@ -116,6 +116,35 @@ static struct security_operations *secon
static LIST_HEAD(superblock_security_head);
static DEFINE_SPINLOCK(sb_security_lock);
+/* Return security context for a given sid or just the context
+ length if the buffer is null or length is 0 */
+static int selinux_getsecurity(u32 sid, void *buffer, size_t size)
+{
+ char *context;
+ unsigned len;
+ int rc;
+
+ if (buffer && !size)
+ return -ERANGE;
+
+ rc = security_sid_to_context(sid, &context, &len);
+ if (rc)
+ return rc;
+
+ if (!buffer || !size)
+ goto getsecurity_exit;
+
+ if (size < len) {
+ kfree(context);
+ return -ERANGE;
+ }
+ memcpy(buffer, context, len);
+
+getsecurity_exit:
+ kfree(context);
+ return len;
+}
+
/* Allocate and free functions for each kind of security blob. */
static int task_alloc_security(struct task_struct *task)
@@ -2232,30 +2261,13 @@ static int selinux_inode_removexattr (st
static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer,
size_t size)
{
struct inode_security_struct *isec = inode->i_security;
- char *context;
- unsigned len;
- int rc;
/* Permission check handled by selinux_inode_getxattr hook.*/
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
- rc = security_sid_to_context(isec->sid, &context, &len);
- if (rc)
- return rc;
-
- if (!buffer || !size) {
- kfree(context);
- return len;
- }
- if (size < len) {
- kfree(context);
- return -ERANGE;
- }
- memcpy(buffer, context, len);
- kfree(context);
- return len;
+ return(selinux_getsecurity(isec->sid, buffer, size));
}
static int selinux_inode_setsecurity(struct inode *inode, const char *name,
@@ -3360,6 +3372,13 @@ out:
return err;
}
+static int selinux_socket_getsecurity(struct socket *sock, void *buffer, size_t size)
+{
+ struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+
+ return(selinux_getsecurity(isec->sid, buffer, size));
+}
+
static int selinux_sk_alloc_security(struct sock *sk, int family, int priority)
{
return sk_alloc_security(sk, family, priority);
@@ -4025,6 +4044,13 @@ static int selinux_ipc_permission(struct
return ipc_has_perm(ipcp, av);
}
+static int selinux_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t
size)
+{
+ struct ipc_security_struct *isec = ipcp->security;
+
+ return(selinux_getsecurity(isec->sid, buffer, size));
+}
+
/* module stacking operations */
static int selinux_register_security (const char *name, struct security_operations *ops)
{
@@ -4066,8 +4092,7 @@ static int selinux_getprocattr(struct ta
char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
- u32 sid, len;
- char *context;
+ u32 sid;
int error;
if (current != p) {
@@ -4076,9 +4101,6 @@ static int selinux_getprocattr(struct ta
return error;
}
- if (!size)
- return -ERANGE;
-
tsec = p->security;
if (!strcmp(name, "current"))
@@ -4095,16 +4117,7 @@ static int selinux_getprocattr(struct ta
if (!sid)
return 0;
- error = security_sid_to_context(sid, &context, &len);
- if (error)
- return error;
- if (len > size) {
- kfree(context);
- return -ERANGE;
- }
- memcpy(value, context, len);
- kfree(context);
- return len;
+ return(selinux_getsecurity(sid, value, size));
}
static int selinux_setprocattr(struct task_struct *p,
@@ -4299,6 +4312,7 @@ static struct security_operations selinu
.task_to_inode = selinux_task_to_inode,
.ipc_permission = selinux_ipc_permission,
+ .ipc_getsecurity = selinux_ipc_getsecurity,
.msg_msg_alloc_security = selinux_msg_msg_alloc_security,
.msg_msg_free_security = selinux_msg_msg_free_security,
@@ -4379,6 +4393,10 @@ static __init int selinux_init(void)
if (register_security (&selinux_ops))
panic("SELinux: Unable to register with kernel.\n");
+ if (audit_set_macxattr(XATTR_SELINUX_SUFFIX)) {
+ printk(KERN_WARNING "SELinux: Unable to register xattr name with
audit.\n");
+ }
+
if (selinux_enforcing) {
printk(KERN_INFO "SELinux: Starting in enforcing mode\n");
} else {