[PATCH] Capture selinux subject/object context information.
authorDustin Kirkland <dustin.kirkland@us.ibm.com>
Thu, 3 Nov 2005 17:15:16 +0000 (17:15 +0000)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 20 Mar 2006 19:08:54 +0000 (14:08 -0500)
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>
include/linux/audit.h
include/linux/security.h
ipc/msg.c
ipc/sem.c
ipc/shm.c
kernel/audit.c
kernel/auditsc.c
security/dummy.c
security/selinux/hooks.c

index 8fa1a8fbc04d49e601d6237437d26df17a733429..1912d8e8ae907189fd15a199a14a6fde094a6fee 100644 (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
index 7cbef482e13aac4b74d66e86afa65ba1ece22d8d..ec0bbbc3ffc26b04034be738b6036c74ad1b0dcf 100644 (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;
index fbf757064a328a11ea100b2975a6ef180430502c..8c30ec2f6e34aaab47f08dc90b7a560725043fff 100644 (file)
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -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;
index 31fd4027d2b5bd7dbda1ad88a69a6460a6af920c..59696a840be1107d83ef9ae15513583e8f8077f8 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -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)
index 9162123a7b23c348aca8669c56ea56bfbeacadb6..a88c8a02e7f3479378238f51e779e93c19774b3a 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -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;
index 1c3eb1b12bfc873d5bb743762aa777d7d4d0c21b..45c123ef77a72d9442cecd05ec6051c69f0aee6a 100644 (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)
        {
index 31917ac730aff2603ecaf1b4376d241d726fdccd..4e2256ec7cf30ff23db37d9017a964fe3baa63db 100644 (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 @@ update_context:
                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;
index f1a5bd98bf10c3ed56d82ddea1ce33cf5a0bc905..6febe7d39fa077a4a6a7f7fabe360fa8a50410a4 100644 (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);
index b65c201e9ff50386ea6cb43ed2c0c91f63a4cfe7..9c08a19cc81b74081429dcd7d3067333671a61b3 100644 (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;
-       }
-
-       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 (strcmp(name, XATTR_SELINUX_SUFFIX))
+               return -EOPNOTSUPP;
 
-       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,