]> git.hungrycats.org Git - linux/commitdiff
[PATCH] selinux: Add SO_PEERSEC socket option and getpeersec LSM hook.
authorAndrew Morton <akpm@osdl.org>
Mon, 19 Jan 2004 02:38:13 +0000 (18:38 -0800)
committerLinus Torvalds <torvalds@home.osdl.org>
Mon, 19 Jan 2004 02:38:13 +0000 (18:38 -0800)
From: James Morris <jmorris@redhat.com>

This patch adds a new option for Unix sockets, SO_PEERSEC, and an
associated LSM hook, getpeersec.  The SELinux handler is also included.

The purpose of this is to allow applications to obtain each others security
credentials, analagously to the existing SO_PEERCRED option.

Examples of use are Security Enhanced D-BUS and Security Enhanced X.

This patch was previously approved in principle by David, and has been
updated with feedback from Chris Wright and extended to cover all
architectures.

24 files changed:
include/asm-alpha/socket.h
include/asm-arm/socket.h
include/asm-arm26/socket.h
include/asm-cris/socket.h
include/asm-h8300/socket.h
include/asm-i386/socket.h
include/asm-ia64/socket.h
include/asm-m68k/socket.h
include/asm-mips/socket.h
include/asm-parisc/socket.h
include/asm-ppc/socket.h
include/asm-ppc64/socket.h
include/asm-s390/socket.h
include/asm-sh/socket.h
include/asm-sparc/socket.h
include/asm-sparc64/socket.h
include/asm-v850/socket.h
include/asm-x86_64/socket.h
include/linux/security.h
include/net/sock.h
net/core/sock.c
security/dummy.c
security/selinux/hooks.c
security/selinux/include/objsec.h

index e821ac372464933594a4aeb25e8a79d37619b0ec..88912c4c8931f979e95bbf13729253f9ee89c8a3 100644 (file)
@@ -48,6 +48,8 @@
 #define SO_TIMESTAMP           29
 #define SCM_TIMESTAMP          SO_TIMESTAMP
 
+#define SO_PEERSEC             30
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             19
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       20
index 5c1428e41b065304633cacb746700e564e3aa54e..b05e717397a1c7869a7d2ae69347646cb26e9848 100644 (file)
@@ -45,6 +45,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 /* Nast libc5 fixup - bletch */
 #if defined(__KERNEL__)
 /* Socket types. */
index 5c1428e41b065304633cacb746700e564e3aa54e..b05e717397a1c7869a7d2ae69347646cb26e9848 100644 (file)
@@ -45,6 +45,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 /* Nast libc5 fixup - bletch */
 #if defined(__KERNEL__)
 /* Socket types. */
index 89edfb11650f0afa8849e7aa65291288c599421d..b4421808b6762f29f92f673138938d595718c807 100644 (file)
@@ -47,6 +47,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 #if defined(__KERNEL__)
 /* Socket types. */
 #define SOCK_STREAM     1               /* stream (connection) socket   */
index a328c84b9f2391d4999d8be57b8b818b0a96e446..070d46d2344e5abb22ad78eb9abf8da658f19a38 100644 (file)
@@ -45,6 +45,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 /* Nast libc5 fixup - bletch */
 #if defined(__KERNEL__)
 /* Socket types. */
index 525f83218cd80bb7d1b8d767e39badcc341b6da4..711f906193fd97137f2b9a0cee1ac6a1d770be0c 100644 (file)
@@ -45,6 +45,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 /* Nasty libc5 fixup - bletch */
 #if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
 /* Socket types. */
index 01e3b50760d30be0538502fe60c8754cd5071847..5935d3e7d5aa4891d74f19c70808893b9692f42d 100644 (file)
@@ -52,6 +52,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 /* Nast libc5 fixup - bletch */
 #if defined(__KERNEL__)
 /* Socket types. */
index 1959b608d477fa1a60089f997078c7f3af5c0b0f..68a33bfbae03c4f4f1dc4fad4473266caff760d2 100644 (file)
@@ -45,6 +45,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 /* Nast libc5 fixup - bletch */
 #if defined(__KERNEL__)
 /* Socket types. */
index df9fcb53d71e1d89dae1ba6c463ea45561fc21bb..87134433004701b4a4ab41d2100eec2b9fdb5eaa 100644 (file)
@@ -66,6 +66,8 @@ To add: #define SO_REUSEPORT 0x0200   /* Allow local address and port reuse.  */
 #define SO_TIMESTAMP           29
 #define SCM_TIMESTAMP          SO_TIMESTAMP
 
+#define SO_PEERSEC             30
+
 /* Nast libc5 fixup - bletch */
 #if defined(__KERNEL__)
 /* Socket types. */
index 7833ff68f2e45aab68777a2a3d675929c5c40d02..fd3f0f29eb3cdac47d33bd4258879d4c2c231b90 100644 (file)
@@ -45,6 +45,8 @@
 
 #define SO_ACCEPTCONN          0x401c
 
+#define SO_PEERSEC             0x401d
+
 #if defined(__KERNEL__)
 #define SOCK_STREAM    1       /* stream (connection) socket   */
 #define SOCK_DGRAM     2       /* datagram (conn.less) socket  */
index d513b3238efde92cd0ea89ea9a741081e937b48c..bad94c36f1b8c08fab129dc2941e00c1ea83f0a5 100644 (file)
@@ -51,6 +51,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 /* Nast libc5 fixup - bletch */
 #if defined(__KERNEL__)
 /* Socket types. */
index 9f23b2f3b59b28024d8d0163e18d7f8d6de0062c..1021a5268346870b1ee0960d1cc7deb7e136f114 100644 (file)
@@ -52,6 +52,8 @@
 
 #define SO_ACCEPTCONN           30
 
+#define SO_PEERSEC             31
+
 /* Nast libc5 fixup - bletch */
 #if defined(__KERNEL__)
 /* Socket types. */
index d7b7f5bb6a39739e678a4466a5bf04d37489302e..1855ec7a112b225efb00d77b03e210d6e96d1fab 100644 (file)
@@ -53,6 +53,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 /* Nast libc5 fixup - bletch */
 #if defined(__KERNEL__)
 /* Socket types. */
index 918e2f2259591f2585f81635d58b0bb9f1167c85..2b9469472f76b3b50a3cb035a6ee29f52e3f9d66 100644 (file)
@@ -45,6 +45,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 /* Nast libc5 fixup - bletch */
 #if defined(__KERNEL__)
 /* Socket types. */
index 254d446bdcf90b8bffb5abd6cdb9df73eb84b93d..cfa529d9bb968ce5377de18622e50c884ec3b8b2 100644 (file)
@@ -45,6 +45,8 @@
 #define SO_TIMESTAMP           0x001d
 #define SCM_TIMESTAMP          SO_TIMESTAMP
 
+#define SO_PEERSEC             0x100e
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
index d041e33529702fc8980e0c797d071f7f5e6c54c1..ba2230ba1d7de3870cc6fe31bbfd406943132dd8 100644 (file)
@@ -45,6 +45,8 @@
 #define SO_TIMESTAMP           0x001d
 #define SCM_TIMESTAMP          SO_TIMESTAMP
 
+#define SO_PEERSEC             0x001e
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
index d1dddefa9eaa65da7853a36fa5e725af94d94e20..7d7f80efa8b36bd04b8502f2adc74caeaaff1fff 100644 (file)
@@ -45,6 +45,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 /* Nast libc5 fixup - bletch */
 #if defined(__KERNEL__)
 /* Socket types. */
index 525f83218cd80bb7d1b8d767e39badcc341b6da4..373e7abadfb5728a037adb29e55139af4c91cf3d 100644 (file)
@@ -45,6 +45,8 @@
 
 #define SO_ACCEPTCONN          30
 
+#define SO_PEERSEC             31
+
 /* Nasty libc5 fixup - bletch */
 #if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
 /* Socket types. */
index 9794223e8e178df0da4461cc7876f6b0b84aad7e..06b2c60a48f849e9b5bb166277771324f705bfc1 100644 (file)
@@ -757,6 +757,22 @@ struct swap_info_struct;
  *     incoming sk_buff @skb has been associated with a particular socket, @sk.
  *     @sk contains the sock (not socket) associated with the incoming sk_buff.
  *     @skb contains the incoming network data.
+ * @socket_getpeersec:
+ *     This hook allows the security module to provide peer socket security
+ *     state to userspace via getsockopt SO_GETPEERSEC.
+ *     @sock is the local socket.
+ *     @optval userspace memory where the security state is to be copied.
+ *     @optlen userspace int where the module should copy the actual length
+ *     of the security state.
+ *     @len as input is the maximum length to copy to userspace provided
+ *     by the caller.
+ *     Return 0 if all is well, otherwise, typical getsockopt return
+ *     values.
+ * @sk_alloc_security:
+ *      Allocate and attach a security structure to the sk->sk_security field,
+ *      which is used to copy security attributes between local stream sockets.
+ * @sk_free_security:
+ *     Deallocate security structure.
  *
  * Security hooks affecting all System V IPC operations.
  *
@@ -1183,6 +1199,9 @@ struct security_operations {
        int (*socket_setsockopt) (struct socket * sock, int level, int optname);
        int (*socket_shutdown) (struct socket * sock, int how);
        int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb);
+       int (*socket_getpeersec) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
+       int (*sk_alloc_security) (struct sock *sk, int family, int priority);
+       void (*sk_free_security) (struct sock *sk);
 #endif /* CONFIG_SECURITY_NETWORK */
 };
 
@@ -2564,6 +2583,22 @@ static inline int security_sock_rcv_skb (struct sock * sk,
 {
        return security_ops->socket_sock_rcv_skb (sk, skb);
 }
+
+static inline int security_socket_getpeersec(struct socket *sock, char __user *optval,
+                                            int __user *optlen, unsigned len)
+{
+       return security_ops->socket_getpeersec(sock, optval, optlen, len);
+}
+
+static inline int security_sk_alloc(struct sock *sk, int family, int priority)
+{
+       return security_ops->sk_alloc_security(sk, family, priority);
+}
+
+static inline void security_sk_free(struct sock *sk)
+{
+       return security_ops->sk_free_security(sk);
+}
 #else  /* CONFIG_SECURITY_NETWORK */
 static inline int security_unix_stream_connect(struct socket * sock,
                                               struct socket * other, 
@@ -2664,6 +2699,21 @@ static inline int security_sock_rcv_skb (struct sock * sk,
 {
        return 0;
 }
+
+static inline int security_socket_getpeersec(struct socket *sock, char __user *optval,
+                                            int __user *optlen, unsigned len)
+{
+       return -ENOPROTOOPT;
+}
+
+static inline int security_sk_alloc(struct sock *sk, int family, int priority)
+{
+       return 0;
+}
+
+static inline void security_sk_free(struct sock *sk)
+{
+}
 #endif /* CONFIG_SECURITY_NETWORK */
 
 #endif /* ! __LINUX_SECURITY_H */
index 7b38c08f0f64e319446fa1225a7f75138ad54959..16e129f90426fc1b0fe5f9c2a208bb3c67c6335a 100644 (file)
@@ -246,6 +246,7 @@ struct sock {
        struct socket           *sk_socket;
        void                    *sk_user_data;
        struct module           *sk_owner;
+       void                    *sk_security;
        void                    (*sk_state_change)(struct sock *sk);
        void                    (*sk_data_ready)(struct sock *sk, int bytes);
        void                    (*sk_write_space)(struct sock *sk);
index 27dfe373e59fab18f5b13ebff63a07e55c33e398..007ed8c8888ea2df5773029d2484325a8cb21eb6 100644 (file)
@@ -564,6 +564,9 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                        v.val = sk->sk_state == TCP_LISTEN;
                        break;
 
+               case SO_PEERSEC:
+                       return security_socket_getpeersec(sock, optval, optlen, len);
+
                default:
                        return(-ENOPROTOOPT);
        }
@@ -606,6 +609,11 @@ struct sock *sk_alloc(int family, int priority, int zero_it, kmem_cache_t *slab)
                        sock_lock_init(sk);
                }
                sk->sk_slab = slab;
+               
+               if (security_sk_alloc(sk, family, priority)) {
+                       kmem_cache_free(slab, sk);
+                       sk = NULL;
+               }
        }
        return sk;
 }
@@ -628,6 +636,7 @@ void sk_free(struct sock *sk)
                printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n",
                       __FUNCTION__, atomic_read(&sk->sk_omem_alloc));
 
+       security_sk_free(sk);
        kmem_cache_free(sk->sk_slab, sk);
        module_put(owner);
 }
index 336e92cce44e1808f9a33e4d198e8477262e4f18..58ee41e25f9611a9776fd86af295bd60185da3d7 100644 (file)
@@ -793,6 +793,21 @@ static int dummy_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb)
 {
        return 0;
 }
+
+static int dummy_socket_getpeersec(struct socket *sock, char __user *optval,
+                                  int __user *optlen, unsigned len)
+{
+       return -ENOPROTOOPT;
+}
+
+static inline int dummy_sk_alloc_security (struct sock *sk, int family, int priority)
+{
+       return 0;
+}
+
+static inline void dummy_sk_free_security (struct sock *sk)
+{
+}
 #endif /* CONFIG_SECURITY_NETWORK */
 
 static int dummy_register_security (const char *name, struct security_operations *ops)
@@ -969,6 +984,9 @@ void security_fixup_ops (struct security_operations *ops)
        set_to_dummy_if_null(ops, socket_getsockopt);
        set_to_dummy_if_null(ops, socket_shutdown);
        set_to_dummy_if_null(ops, socket_sock_rcv_skb);
+       set_to_dummy_if_null(ops, socket_getpeersec);
+       set_to_dummy_if_null(ops, sk_alloc_security);
+       set_to_dummy_if_null(ops, sk_free_security);
 #endif /* CONFIG_SECURITY_NETWORK */
 }
 
index 3892af7fc67852eaa2f1d9f5766bedad9409683e..0c799b808af9b4def7031443e66eea273251e8e7 100644 (file)
@@ -244,6 +244,39 @@ static void superblock_free_security(struct super_block *sb)
        kfree(sbsec);
 }
 
+#ifdef CONFIG_SECURITY_NETWORK
+static int sk_alloc_security(struct sock *sk, int family, int priority)
+{
+       struct sk_security_struct *ssec;
+
+       if (family != PF_UNIX)
+               return 0;
+
+       ssec = kmalloc(sizeof(*ssec), priority);
+       if (!ssec)
+               return -ENOMEM;
+
+       memset(ssec, 0, sizeof(*ssec));
+       ssec->magic = SELINUX_MAGIC;
+       ssec->sk = sk;
+       ssec->peer_sid = SECINITSID_UNLABELED;
+       sk->sk_security = ssec;
+
+       return 0;
+}
+
+static void sk_free_security(struct sock *sk)
+{
+       struct task_security_struct *ssec = sk->sk_security;
+
+       if (sk->sk_family != PF_UNIX || ssec->magic != SELINUX_MAGIC)
+               return;
+
+       sk->sk_security = NULL;
+       kfree(ssec);
+}
+#endif /* CONFIG_SECURITY_NETWORK */
+
 /* The security server must be initialized before
    any labeling or access decisions can be provided. */
 extern int ss_initialized;
@@ -2540,6 +2573,7 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
                                              struct socket *other,
                                              struct sock *newsk)
 {
+       struct sk_security_struct *ssec;
        struct inode_security_struct *isec;
        struct inode_security_struct *other_isec;
        struct avc_audit_data ad;
@@ -2558,6 +2592,14 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
        if (err)
                return err;
 
+       /* connecting socket */
+       ssec = sock->sk->sk_security;
+       ssec->peer_sid = other_isec->sid;
+       
+       /* server child socket */
+       ssec = newsk->sk_security;
+       ssec->peer_sid = isec->sid;
+       
        return 0;
 }
 
@@ -2664,6 +2706,54 @@ out:
        return err;
 }
 
+static int selinux_socket_getpeersec(struct socket *sock, char __user *optval,
+                                    int __user *optlen, unsigned len)
+{
+       int err = 0;
+       char *scontext;
+       u32 scontext_len;
+       struct sk_security_struct *ssec;
+       struct inode_security_struct *isec;
+
+       isec = SOCK_INODE(sock)->i_security;
+       if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) {
+               err = -ENOPROTOOPT;
+               goto out;
+       }
+
+       ssec = sock->sk->sk_security;
+       
+       err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len);
+       if (err)
+               goto out;
+
+       if (scontext_len > len) {
+               err = -ERANGE;
+               goto out_len;
+       }
+
+       if (copy_to_user(optval, scontext, scontext_len))
+               err = -EFAULT;
+
+out_len:
+       if (put_user(scontext_len, optlen))
+               err = -EFAULT;
+
+       kfree(scontext);
+out:   
+       return err;
+}
+
+static int selinux_sk_alloc_security(struct sock *sk, int family, int priority)
+{
+       return sk_alloc_security(sk, family, priority);
+}
+
+static void selinux_sk_free_security(struct sock *sk)
+{
+       sk_free_security(sk);
+}
+
 #ifdef CONFIG_NETFILTER
 static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
                                               struct sk_buff **pskb,
@@ -2741,6 +2831,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
 out:
        return err;
 }
+
 #endif /* CONFIG_NETFILTER */
 
 #endif /* CONFIG_SECURITY_NETWORK */
@@ -3479,6 +3570,9 @@ struct security_operations selinux_ops = {
        .socket_setsockopt =            selinux_socket_setsockopt,
        .socket_shutdown =              selinux_socket_shutdown,
        .socket_sock_rcv_skb =          selinux_socket_sock_rcv_skb,
+       .socket_getpeersec =            selinux_socket_getpeersec,
+       .sk_alloc_security =            selinux_sk_alloc_security,
+       .sk_free_security =             selinux_sk_free_security,
 #endif
 };
 
index 37a992ba0468ea0cf55e65fe8823de3721c312e3..28f77f23257ba8bb6bcfe820df0f9e6847e38976 100644 (file)
@@ -100,6 +100,12 @@ struct netif_security_struct {
        struct avc_entry_ref avcr;      /* reference to permissions */
 };
 
+struct sk_security_struct {
+       unsigned long magic;            /* magic number for this module */
+       struct sock *sk;                /* back pointer to sk object */
+       u32 peer_sid;                   /* SID of peer */
+};
+
 extern int inode_security_set_sid(struct inode *inode, u32 sid);
 
 #endif /* _SELINUX_OBJSEC_H_ */