[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>
This commit is contained in:
Dustin Kirkland 2005-11-03 17:15:16 +00:00 committed by Al Viro
parent c8edc80c8b
commit 8c8570fb8f
9 changed files with 226 additions and 69 deletions

View file

@ -285,13 +285,14 @@ extern void auditsc_get_stamp(struct audit_context *ctx,
struct timespec *t, unsigned int *serial);
extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid);
extern uid_t audit_get_loginuid(struct audit_context *ctx);
extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode);
extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp);
extern int audit_socketcall(int nargs, unsigned long *args);
extern int audit_sockaddr(int len, void *addr);
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_filter_type(int type);
extern int audit_set_macxattr(const char *name);
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
@ -306,12 +307,13 @@ extern int audit_filter_type(int type);
#define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; })
#define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
#define audit_get_loginuid(c) ({ -1; })
#define audit_ipc_perms(q,u,g,m) ({ 0; })
#define audit_ipc_perms(q,u,g,m,i) ({ 0; })
#define audit_socketcall(n,a) ({ 0; })
#define audit_sockaddr(len, addr) ({ 0; })
#define audit_avc_path(dentry, mnt) ({ 0; })
#define audit_signal_info(s,t) do { ; } while (0)
#define audit_filter_user(cb,t) ({ 1; })
#define audit_set_macxattr(n) do { ; } while (0)
#endif
#ifdef CONFIG_AUDIT
@ -340,6 +342,7 @@ extern void audit_send_reply(int pid, int seq, int type,
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)
@ -350,6 +353,7 @@ extern struct semaphore audit_netlink_sem;
#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)
#define audit_panic(m) do { ; } while (0)
#endif
#endif
#endif

View file

@ -869,6 +869,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:
@ -1168,6 +1173,7 @@ struct security_operations {
int (*inode_getxattr) (struct dentry *dentry, char *name);
int (*inode_listxattr) (struct dentry *dentry);
int (*inode_removexattr) (struct dentry *dentry, char *name);
char *(*inode_xattr_getsuffix) (void);
int (*inode_getsecurity)(struct inode *inode, const char *name, void *buffer, size_t size, int err);
int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, int flags);
int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size);
@ -1217,6 +1223,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);
@ -1674,6 +1681,11 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name)
return security_ops->inode_removexattr (dentry, name);
}
static inline const char *security_inode_xattr_getsuffix(void)
{
return security_ops->inode_xattr_getsuffix();
}
static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
{
if (unlikely (IS_PRIVATE (inode)))
@ -1869,6 +1881,11 @@ static inline int security_ipc_permission (struct kern_ipc_perm *ipcp,
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);
@ -2316,6 +2333,11 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name)
return cap_inode_removexattr(dentry, name);
}
static inline const char *security_inode_xattr_getsuffix (void)
{
return NULL ;
}
static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
{
return -EOPNOTSUPP;
@ -2499,6 +2521,11 @@ static inline int security_ipc_permission (struct kern_ipc_perm *ipcp,
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;

View file

@ -429,8 +429,6 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf)
return -EFAULT;
if (copy_msqid_from_user (&setbuf, buf, version))
return -EFAULT;
if ((err = audit_ipc_perms(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode)))
return err;
break;
case IPC_RMID:
break;
@ -461,6 +459,9 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf)
switch (cmd) {
case IPC_SET:
{
if ((err = audit_ipc_perms(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode, ipcp)))
goto out_unlock_up;
err = -EPERM;
if (setbuf.qbytes > msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
goto out_unlock_up;

View file

@ -809,8 +809,6 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
if(cmd == IPC_SET) {
if(copy_semid_from_user (&setbuf, arg.buf, version))
return -EFAULT;
if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode)))
return err;
}
sma = sem_lock(semid);
if(sma==NULL)
@ -821,7 +819,6 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
goto out_unlock;
}
ipcp = &sma->sem_perm;
if (current->euid != ipcp->cuid &&
current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
err=-EPERM;
@ -838,6 +835,8 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
err = 0;
break;
case IPC_SET:
if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode, ipcp)))
goto out_unlock;
ipcp->uid = setbuf.uid;
ipcp->gid = setbuf.gid;
ipcp->mode = (ipcp->mode & ~S_IRWXUGO)

View file

@ -620,13 +620,13 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
err = -EFAULT;
goto out;
}
if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode)))
return err;
down(&shm_ids.sem);
shp = shm_lock(shmid);
err=-EINVAL;
if(shp==NULL)
goto out_up;
if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode, &(shp->shm_perm))))
goto out_unlock_up;
err = shm_checkid(shp,shmid);
if(err)
goto out_unlock_up;

View file

@ -142,7 +142,7 @@ static void audit_set_pid(struct audit_buffer *ab, pid_t pid)
nlh->nlmsg_pid = pid;
}
static void audit_panic(const char *message)
void audit_panic(const char *message)
{
switch (audit_failure)
{

View file

@ -34,6 +34,9 @@
*
* Modified by Amy Griffis <amy.griffis@hp.com> to collect additional
* filesystem information.
*
* Subject and object context labeling support added by <danjones@us.ibm.com>
* and <dustin.kirkland@us.ibm.com> for LSPP certification compliance.
*/
#include <linux/init.h>
@ -53,6 +56,7 @@
#include <linux/netlink.h>
#include <linux/compiler.h>
#include <asm/unistd.h>
#include <linux/security.h>
/* 0 = no checking
1 = put_count checking
@ -109,6 +113,7 @@ struct audit_names {
uid_t uid;
gid_t gid;
dev_t rdev;
char *ctx;
};
struct audit_aux_data {
@ -125,6 +130,7 @@ struct audit_aux_data_ipcctl {
uid_t uid;
gid_t gid;
mode_t mode;
char *ctx;
};
struct audit_aux_data_socketcall {
@ -743,10 +749,11 @@ static inline void audit_free_names(struct audit_context *context)
context->serial, context->major, context->in_syscall,
context->name_count, context->put_count,
context->ino_count);
for (i = 0; i < context->name_count; i++)
for (i = 0; i < context->name_count; i++) {
printk(KERN_ERR "names[%d] = %p = %s\n", i,
context->names[i].name,
context->names[i].name ?: "(null)");
}
dump_stack();
return;
}
@ -756,9 +763,13 @@ static inline void audit_free_names(struct audit_context *context)
context->ino_count = 0;
#endif
for (i = 0; i < context->name_count; i++)
for (i = 0; i < context->name_count; i++) {
char *p = context->names[i].ctx;
context->names[i].ctx = NULL;
kfree(p);
if (context->names[i].name)
__putname(context->names[i].name);
}
context->name_count = 0;
if (context->pwd)
dput(context->pwd);
@ -778,6 +789,12 @@ static inline void audit_free_aux(struct audit_context *context)
dput(axi->dentry);
mntput(axi->mnt);
}
if ( aux->type == AUDIT_IPC ) {
struct audit_aux_data_ipcctl *axi = (void *)aux;
if (axi->ctx)
kfree(axi->ctx);
}
context->aux = aux->next;
kfree(aux);
}
@ -862,7 +879,38 @@ static inline void audit_free_context(struct audit_context *context)
printk(KERN_ERR "audit: freed %d contexts\n", count);
}
static void audit_log_task_info(struct audit_buffer *ab)
static void audit_log_task_context(struct audit_buffer *ab, gfp_t gfp_mask)
{
char *ctx = NULL;
ssize_t len = 0;
len = security_getprocattr(current, "current", NULL, 0);
if (len < 0) {
if (len != -EINVAL)
goto error_path;
return;
}
ctx = kmalloc(len, gfp_mask);
if (!ctx) {
goto error_path;
return;
}
len = security_getprocattr(current, "current", ctx, len);
if (len < 0 )
goto error_path;
audit_log_format(ab, " subj=%s", ctx);
error_path:
if (ctx)
kfree(ctx);
audit_panic("security_getprocattr error in audit_log_task_context");
return;
}
static void audit_log_task_info(struct audit_buffer *ab, gfp_t gfp_mask)
{
char name[sizeof(current->comm)];
struct mm_struct *mm = current->mm;
@ -875,6 +923,10 @@ static void audit_log_task_info(struct audit_buffer *ab)
if (!mm)
return;
/*
* this is brittle; all callers that pass GFP_ATOMIC will have
* NULL current->mm and we won't get here.
*/
down_read(&mm->mmap_sem);
vma = mm->mmap;
while (vma) {
@ -888,6 +940,7 @@ static void audit_log_task_info(struct audit_buffer *ab)
vma = vma->vm_next;
}
up_read(&mm->mmap_sem);
audit_log_task_context(ab, gfp_mask);
}
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)
context->gid,
context->euid, context->suid, context->fsuid,
context->egid, context->sgid, context->fsgid);
audit_log_task_info(ab);
audit_log_task_info(ab, gfp_mask);
audit_log_end(ab);
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)
case AUDIT_IPC: {
struct audit_aux_data_ipcctl *axi = (void *)aux;
audit_log_format(ab,
" qbytes=%lx iuid=%u igid=%u mode=%x",
axi->qbytes, axi->uid, axi->gid, axi->mode);
" qbytes=%lx iuid=%u igid=%u mode=%x obj=%s",
axi->qbytes, axi->uid, axi->gid, axi->mode, axi->ctx);
break; }
case AUDIT_SOCKETCALL: {
@ -1001,6 +1054,11 @@ static void audit_log_exit(struct audit_context *context, gfp_t gfp_mask)
context->names[i].gid,
MAJOR(context->names[i].rdev),
MINOR(context->names[i].rdev));
if (context->names[i].ctx) {
audit_log_format(ab, " obj=%s",
context->names[i].ctx);
}
audit_log_end(ab);
}
}
@ -1243,6 +1301,39 @@ void audit_putname(const char *name)
#endif
}
void audit_inode_context(int idx, const struct inode *inode)
{
struct audit_context *context = current->audit_context;
char *ctx = NULL;
int len = 0;
if (!security_inode_xattr_getsuffix())
return;
len = security_inode_getsecurity(inode, (char *)security_inode_xattr_getsuffix(), NULL, 0, 0);
if (len < 0)
goto error_path;
ctx = kmalloc(len, GFP_KERNEL);
if (!ctx)
goto error_path;
len = security_inode_getsecurity(inode, (char *)security_inode_xattr_getsuffix(), ctx, len, 0);
if (len < 0)
goto error_path;
kfree(context->names[idx].ctx);
context->names[idx].ctx = ctx;
return;
error_path:
if (ctx)
kfree(ctx);
audit_panic("error in audit_inode_context");
return;
}
/**
* audit_inode - store the inode and device from a lookup
* @name: name being audited
@ -1282,6 +1373,7 @@ void __audit_inode(const char *name, const struct inode *inode, unsigned flags)
context->names[idx].uid = inode->i_uid;
context->names[idx].gid = inode->i_gid;
context->names[idx].rdev = inode->i_rdev;
audit_inode_context(idx, inode);
if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) &&
(strcmp(name, ".") != 0)) {
context->names[idx].ino = (unsigned long)-1;
@ -1363,6 +1455,7 @@ void __audit_inode_child(const char *dname, const struct inode *inode,
context->names[idx].uid = inode->i_uid;
context->names[idx].gid = inode->i_gid;
context->names[idx].rdev = inode->i_rdev;
audit_inode_context(idx, inode);
}
}
@ -1423,6 +1516,38 @@ uid_t audit_get_loginuid(struct audit_context *ctx)
return ctx ? ctx->loginuid : -1;
}
static char *audit_ipc_context(struct kern_ipc_perm *ipcp)
{
struct audit_context *context = current->audit_context;
char *ctx = NULL;
int len = 0;
if (likely(!context))
return NULL;
len = security_ipc_getsecurity(ipcp, NULL, 0);
if (len == -EOPNOTSUPP)
goto ret;
if (len < 0)
goto error_path;
ctx = kmalloc(len, GFP_ATOMIC);
if (!ctx)
goto error_path;
len = security_ipc_getsecurity(ipcp, ctx, len);
if (len < 0)
goto error_path;
return ctx;
error_path:
kfree(ctx);
audit_panic("error in audit_ipc_context");
ret:
return NULL;
}
/**
* audit_ipc_perms - record audit data for ipc
* @qbytes: msgq bytes
@ -1432,7 +1557,7 @@ uid_t audit_get_loginuid(struct audit_context *ctx)
*
* Returns 0 for success or NULL context or < 0 on error.
*/
int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp)
{
struct audit_aux_data_ipcctl *ax;
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)
if (likely(!context))
return 0;
ax = kmalloc(sizeof(*ax), GFP_KERNEL);
ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
if (!ax)
return -ENOMEM;
@ -1448,6 +1573,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
ax->uid = uid;
ax->gid = gid;
ax->mode = mode;
ax->ctx = audit_ipc_context(ipcp);
ax->d.type = AUDIT_IPC;
ax->d.next = context->aux;

View file

@ -558,6 +558,11 @@ static int dummy_ipc_permission (struct kern_ipc_perm *ipcp, short flag)
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;
@ -959,6 +964,7 @@ void security_fixup_ops (struct security_operations *ops)
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);

View file

@ -117,6 +117,32 @@ static struct security_operations *secondary_ops = NULL;
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;
rc = security_sid_to_context(sid, &context, &len);
if (rc)
return rc;
if (!buffer || !size)
goto getsecurity_exit;
if (size < len) {
len = -ERANGE;
goto getsecurity_exit;
}
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)
@ -2209,6 +2235,11 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name)
return -EACCES;
}
static const char *selinux_inode_xattr_getsuffix(void)
{
return XATTR_SELINUX_SUFFIX;
}
/*
* Copy the in-core inode security context value to the user. If the
* getxattr() prior to this succeeded, check to see if we need to
@ -2219,44 +2250,11 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name)
static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err)
{
struct inode_security_struct *isec = inode->i_security;
char *context;
unsigned len;
int rc;
if (strcmp(name, XATTR_SELINUX_SUFFIX)) {
rc = -EOPNOTSUPP;
goto out;
}
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
rc = security_sid_to_context(isec->sid, &context, &len);
if (rc)
goto out;
/* Probe for required buffer size */
if (!buffer || !size) {
rc = len;
goto out_free;
}
if (size < len) {
rc = -ERANGE;
goto out_free;
}
if (err > 0) {
if ((len == err) && !(memcmp(context, buffer, len))) {
/* Don't need to canonicalize value */
rc = err;
goto out_free;
}
memset(buffer, 0, size);
}
memcpy(buffer, context, len);
rc = len;
out_free:
kfree(context);
out:
return rc;
return selinux_getsecurity(isec->sid, buffer, size);
}
static int selinux_inode_setsecurity(struct inode *inode, const char *name,
@ -4022,6 +4020,13 @@ static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
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)
{
@ -4063,8 +4068,7 @@ static int selinux_getprocattr(struct task_struct *p,
char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
u32 sid, len;
char *context;
u32 sid;
int error;
if (current != p) {
@ -4073,9 +4077,6 @@ static int selinux_getprocattr(struct task_struct *p,
return error;
}
if (!size)
return -ERANGE;
tsec = p->security;
if (!strcmp(name, "current"))
@ -4092,16 +4093,7 @@ static int selinux_getprocattr(struct task_struct *p,
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,
@ -4259,6 +4251,7 @@ static struct security_operations selinux_ops = {
.inode_getxattr = selinux_inode_getxattr,
.inode_listxattr = selinux_inode_listxattr,
.inode_removexattr = selinux_inode_removexattr,
.inode_xattr_getsuffix = selinux_inode_xattr_getsuffix,
.inode_getsecurity = selinux_inode_getsecurity,
.inode_setsecurity = selinux_inode_setsecurity,
.inode_listsecurity = selinux_inode_listsecurity,
@ -4296,6 +4289,7 @@ static struct security_operations selinux_ops = {
.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,