netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC] SO_PEERSEC - security credentials for Unix stream sockets
@ 2003-12-10 16:33 James Morris
  2003-12-10 22:56 ` David S. Miller
  2003-12-13  0:16 ` Chris Wright
  0 siblings, 2 replies; 10+ messages in thread
From: James Morris @ 2003-12-10 16:33 UTC (permalink / raw)
  To: David S. Miller; +Cc: kuznet, linux-security-module, Stephen Smalley, netdev

Below is a patch against 2.6.0-test11 which implements a new socket option
SO_PEERSEC (defined for i386 only at this stage).

The purpose of this option is to allow an application to obtain the 
security credentials of a Unix stream socket peer.  It is analogous to 
SO_PEERCRED (which provides authentication using standard Unix credentials 
of pid, uid and gid), and extends this concept to other security models. 
The getsockopt call is exposed via LSM, so that security modules can 
define their own handlers for SO_PEERSEC.

An SELinux specific handler and demonstration userspace utilities are 
available at:
http://people.redhat.com/jmorris/selinux/peersec/

This functionality is required to support SELinux DBUS and Security
Enhanced X, and should be generally useful to security-aware applications
which need to authenticate other applications.

Three new LSM hooks have been implemented:

- socket_getpeersec() is the getsockopt interface.

- sk_alloc_security() and sk_free_security() facilitate the use of an
  sk_security field, which is used to store the security credentials of 
  the Unix peer.  We can't use an existing security field for this (e.g. 
  inode), as we need the security credentials of the server's child 
  socket.  This follows the same general scheme used for managing existing 
  Unix peer credentials.

Comments?


- James
-- 
James Morris
<jmorris@redhat.com>


diff -urN -X dontdiff linux-2.6.0-test11.orig/include/asm-i386/socket.h linux-2.6.0-test11.w2/include/asm-i386/socket.h
--- linux-2.6.0-test11.orig/include/asm-i386/socket.h	2003-09-27 20:50:09.000000000 -0400
+++ linux-2.6.0-test11.w2/include/asm-i386/socket.h	2003-12-10 09:55:39.371902424 -0500
@@ -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. */
diff -urN -X dontdiff linux-2.6.0-test11.orig/include/linux/security.h linux-2.6.0-test11.w2/include/linux/security.h
--- linux-2.6.0-test11.orig/include/linux/security.h	2003-10-15 08:53:19.000000000 -0400
+++ linux-2.6.0-test11.w2/include/linux/security.h	2003-12-10 09:55:39.375901816 -0500
@@ -757,6 +757,19 @@
  *	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.
+ *	@len is the maximum length to copy to userspace.
+ *	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 +1196,9 @@
 	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, 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 +2580,22 @@
 {
 	return security_ops->socket_sock_rcv_skb (sk, skb);
 }
+
+static inline int security_socket_getpeersec(struct socket *sock,
+                                             char __user *optval, unsigned len)
+{
+	return security_ops->socket_getpeersec(sock, optval, len);
+}
+
+static inline int security_sk_alloc_security(struct sock *sk, int family, int priority)
+{
+	return security_ops->sk_alloc_security(sk, family, priority);
+}
+
+static inline void security_sk_free_security(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 +2696,21 @@
 {
 	return 0;
 }
+
+static inline int security_socket_getpeersec(struct socket *sock,
+                                             char __user *optval, unsigned optlen)
+{
+	return -ENOPROTOOPT;
+}
+
+static inline int security_sk_alloc_security(struct sock *sk, int family, int priority)
+{
+	return 0;
+}
+
+static inline void security_sk_free_security(struct sock *sk)
+{
+}
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #endif /* ! __LINUX_SECURITY_H */
diff -urN -X dontdiff linux-2.6.0-test11.orig/include/net/sock.h linux-2.6.0-test11.w2/include/net/sock.h
--- linux-2.6.0-test11.orig/include/net/sock.h	2003-12-01 15:27:07.000000000 -0500
+++ linux-2.6.0-test11.w2/include/net/sock.h	2003-12-10 09:55:39.376901664 -0500
@@ -246,6 +246,9 @@
 	struct socket		*sk_socket;
 	void			*sk_user_data;
 	struct module		*sk_owner;
+#ifdef CONFIG_SECURITY_NETWORK
+	void			*sk_security;
+#endif
 	void			(*sk_state_change)(struct sock *sk);
 	void			(*sk_data_ready)(struct sock *sk, int bytes);
 	void			(*sk_write_space)(struct sock *sk);
diff -urN -X dontdiff linux-2.6.0-test11.orig/net/core/sock.c linux-2.6.0-test11.w2/net/core/sock.c
--- linux-2.6.0-test11.orig/net/core/sock.c	2003-12-01 15:27:04.000000000 -0500
+++ linux-2.6.0-test11.w2/net/core/sock.c	2003-12-10 09:55:39.378901360 -0500
@@ -564,6 +564,9 @@
 			v.val = sk->sk_state == TCP_LISTEN;
 			break;
 
+		case SO_PEERSEC:
+			return security_socket_getpeersec(sock, optval, len);
+
 		default:
 			return(-ENOPROTOOPT);
 	}
@@ -606,6 +609,11 @@
 			sock_lock_init(sk);
 		}
 		sk->sk_slab = slab;
+		
+		if (security_sk_alloc_security(sk, family, priority)) {
+			kmem_cache_free(sk->sk_slab, sk);
+			sk = NULL;
+		}
 	}
 	return sk;
 }
@@ -628,6 +636,7 @@
 		printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n",
 		       __FUNCTION__, atomic_read(&sk->sk_omem_alloc));
 
+	security_sk_free_security(sk);
 	kmem_cache_free(sk->sk_slab, sk);
 	module_put(owner);
 }
diff -urN -X dontdiff linux-2.6.0-test11.orig/security/dummy.c linux-2.6.0-test11.w2/security/dummy.c
--- linux-2.6.0-test11.orig/security/dummy.c	2003-10-15 08:53:19.000000000 -0400
+++ linux-2.6.0-test11.w2/security/dummy.c	2003-12-10 09:55:39.379901208 -0500
@@ -793,6 +793,21 @@
 {
 	return 0;
 }
+
+static int dummy_socket_getpeersec(struct socket *sock,
+                                   char __user *optval, unsigned optlen)
+{
+	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 @@
 	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 */
 }
 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] SO_PEERSEC - security credentials for Unix stream sockets
  2003-12-10 16:33 [RFC] SO_PEERSEC - security credentials for Unix stream sockets James Morris
@ 2003-12-10 22:56 ` David S. Miller
  2003-12-12 14:25   ` James Morris
  2003-12-13  0:16 ` Chris Wright
  1 sibling, 1 reply; 10+ messages in thread
From: David S. Miller @ 2003-12-10 22:56 UTC (permalink / raw)
  To: James Morris; +Cc: kuznet, linux-security-module, sds, netdev

On Wed, 10 Dec 2003 11:33:53 -0500 (EST)
James Morris <jmorris@redhat.com> wrote:

> Three new LSM hooks have been implemented:
> 
> - socket_getpeersec() is the getsockopt interface.
> 
> - sk_alloc_security() and sk_free_security() facilitate the use of an
>   sk_security field, which is used to store the security credentials of 
>   the Unix peer.  We can't use an existing security field for this (e.g. 
>   inode), as we need the security credentials of the server's child 
>   socket.  This follows the same general scheme used for managing existing 
>   Unix peer credentials.
> 
> Comments?

I'm fine with this conceptually, although the earliest I could put
this into the tree is 2.6.1 although I have a hunch that I'll be
asked to defer something like this to 2.6.2, but who knows.

The one thing I don't like is the ifdef conditionalized member of
the sock struct.  We should move away from config variables changing
structure layouts.  Even a "void *sk_security;" would be better.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] SO_PEERSEC - security credentials for Unix stream sockets
  2003-12-10 22:56 ` David S. Miller
@ 2003-12-12 14:25   ` James Morris
  0 siblings, 0 replies; 10+ messages in thread
From: James Morris @ 2003-12-12 14:25 UTC (permalink / raw)
  To: David S. Miller; +Cc: kuznet, linux-security-module, sds, netdev

On Wed, 10 Dec 2003, David S. Miller wrote:

> I'm fine with this conceptually, although the earliest I could put
> this into the tree is 2.6.1 although I have a hunch that I'll be
> asked to defer something like this to 2.6.2, but who knows.

No problem, it will hopefully get more testing and feedback in the 
meantime.  I'll resubmit it sometime after 2.6.0.

> The one thing I don't like is the ifdef conditionalized member of
> the sock struct.  We should move away from config variables changing
> structure layouts.  Even a "void *sk_security;" would be better.

Ok.


- James
-- 
James Morris
<jmorris@redhat.com>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] SO_PEERSEC - security credentials for Unix stream sockets
  2003-12-10 16:33 [RFC] SO_PEERSEC - security credentials for Unix stream sockets James Morris
  2003-12-10 22:56 ` David S. Miller
@ 2003-12-13  0:16 ` Chris Wright
  2003-12-13  3:44   ` James Morris
  1 sibling, 1 reply; 10+ messages in thread
From: Chris Wright @ 2003-12-13  0:16 UTC (permalink / raw)
  To: James Morris
  Cc: David S. Miller, kuznet, linux-security-module, Stephen Smalley,
	netdev

* James Morris (jmorris@redhat.com) wrote:
> Below is a patch against 2.6.0-test11 which implements a new socket option
> SO_PEERSEC (defined for i386 only at this stage).

Thanks for doing this James.  In your example demonstration, you simply
print the peersec string.  Do you expect to use with simple comparison
against something like data from procattr, or something else?  IOW,
does this introduce any new namespace issues for apps?

> +static inline int security_sk_alloc_security(struct sock *sk, int family, int priority)
> +static inline void security_sk_free_security(struct sock *sk)

minor nit.  these names are inconsistent with the existing analogous ones.
how about simply, security_sk_alloc and security_sk_free?

> +++ linux-2.6.0-test11.w2/net/core/sock.c	2003-12-10 09:55:39.378901360 -0500
> @@ -564,6 +564,9 @@
>  			v.val = sk->sk_state == TCP_LISTEN;
>  			break;
>  
> +		case SO_PEERSEC:
> +			return security_socket_getpeersec(sock, optval, len);
> +

Would it be useful to ask the module to update len as is done in some
other cases.  Perhaps buffer is too small, can len be vector for that info?

thanks,
-chris
-- 
Linux Security Modules     http://lsm.immunix.org     http://lsm.bkbits.net

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] SO_PEERSEC - security credentials for Unix stream sockets
  2003-12-13  0:16 ` Chris Wright
@ 2003-12-13  3:44   ` James Morris
  2003-12-16  1:32     ` Chris Wright
  0 siblings, 1 reply; 10+ messages in thread
From: James Morris @ 2003-12-13  3:44 UTC (permalink / raw)
  To: Chris Wright
  Cc: David S. Miller, kuznet, linux-security-module, Stephen Smalley,
	netdev

On Fri, 12 Dec 2003, Chris Wright wrote:

> * James Morris (jmorris@redhat.com) wrote:
> > Below is a patch against 2.6.0-test11 which implements a new socket option
> > SO_PEERSEC (defined for i386 only at this stage).
> 
> Thanks for doing this James.  In your example demonstration, you simply
> print the peersec string.  Do you expect to use with simple comparison
> against something like data from procattr, or something else?  IOW,
> does this introduce any new namespace issues for apps?

Semantics are application dependent, but with SELinux, the returned
security context would be passed to a userspace SELinux API function.  

I'm not sure how this would be a namespace issue -- do you mean a data 
format issue?

> 
> > +static inline int security_sk_alloc_security(struct sock *sk, int family, int priority)
> > +static inline void security_sk_free_security(struct sock *sk)
> 
> minor nit.  these names are inconsistent with the existing analogous ones.
> how about simply, security_sk_alloc and security_sk_free?

Done.

> 
> > +++ linux-2.6.0-test11.w2/net/core/sock.c	2003-12-10 09:55:39.378901360 -0500
> > @@ -564,6 +564,9 @@
> >  			v.val = sk->sk_state == TCP_LISTEN;
> >  			break;
> >  
> > +		case SO_PEERSEC:
> > +			return security_socket_getpeersec(sock, optval, len);
> > +
> 
> Would it be useful to ask the module to update len as is done in some
> other cases. 

Yep, allowing the security module to update the returned length is now
implemented.

> Perhaps buffer is too small, can len be vector for that info?

I would not advise updating len on error -- it's a bad idea in general to
interpret any returned data from failed syscalls except the error number.

An updated patch is below, which implements the above, as well as previous 
feedback from David.  This one includes the changes to SELinux so it's 
easier to see example usage.


- James
-- 
James Morris
<jmorris@redhat.com>

diff -urN -X dontdiff linux-2.6.0-test11.orig/include/asm-i386/socket.h linux-2.6.0-test11.w1/include/asm-i386/socket.h
--- linux-2.6.0-test11.orig/include/asm-i386/socket.h	2003-09-27 20:50:09.000000000 -0400
+++ linux-2.6.0-test11.w1/include/asm-i386/socket.h	2003-12-12 20:24:34.000000000 -0500
@@ -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. */
diff -urN -X dontdiff linux-2.6.0-test11.orig/include/linux/security.h linux-2.6.0-test11.w1/include/linux/security.h
--- linux-2.6.0-test11.orig/include/linux/security.h	2003-10-15 08:53:19.000000000 -0400
+++ linux-2.6.0-test11.w1/include/linux/security.h	2003-12-12 21:43:53.000000000 -0500
@@ -757,6 +757,21 @@
  *	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.
+ *	@len as input is the maximum length to copy to userspace provided
+ *           by the caller.  The module should change this to the actual
+ *           length copied.
+ *	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 +1198,9 @@
 	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, 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 +2582,22 @@
 {
 	return security_ops->socket_sock_rcv_skb (sk, skb);
 }
+
+static inline int security_socket_getpeersec(struct socket *sock,
+                                             char __user *optval, unsigned *len)
+{
+	return security_ops->socket_getpeersec(sock, optval, 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 +2698,21 @@
 {
 	return 0;
 }
+
+static inline int security_socket_getpeersec(struct socket *sock,
+                                             char __user *optval, 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 */
diff -urN -X dontdiff linux-2.6.0-test11.orig/include/net/sock.h linux-2.6.0-test11.w1/include/net/sock.h
--- linux-2.6.0-test11.orig/include/net/sock.h	2003-12-01 15:27:07.000000000 -0500
+++ linux-2.6.0-test11.w1/include/net/sock.h	2003-12-12 20:44:15.000000000 -0500
@@ -246,6 +246,7 @@
 	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);
diff -urN -X dontdiff linux-2.6.0-test11.orig/net/core/sock.c linux-2.6.0-test11.w1/net/core/sock.c
--- linux-2.6.0-test11.orig/net/core/sock.c	2003-12-01 15:27:04.000000000 -0500
+++ linux-2.6.0-test11.w1/net/core/sock.c	2003-12-12 21:43:54.000000000 -0500
@@ -564,6 +564,13 @@
 			v.val = sk->sk_state == TCP_LISTEN;
 			break;
 
+		case SO_PEERSEC: {
+			int err = security_socket_getpeersec(sock, optval, &len);
+			if (err)
+				return err;
+			goto lenout;
+		}
+
 		default:
 			return(-ENOPROTOOPT);
 	}
@@ -606,6 +613,11 @@
 			sock_lock_init(sk);
 		}
 		sk->sk_slab = slab;
+		
+		if (security_sk_alloc(sk, family, priority)) {
+			kmem_cache_free(sk->sk_slab, sk);
+			sk = NULL;
+		}
 	}
 	return sk;
 }
@@ -628,6 +640,7 @@
 		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);
 }
diff -urN -X dontdiff linux-2.6.0-test11.orig/security/dummy.c linux-2.6.0-test11.w1/security/dummy.c
--- linux-2.6.0-test11.orig/security/dummy.c	2003-10-15 08:53:19.000000000 -0400
+++ linux-2.6.0-test11.w1/security/dummy.c	2003-12-12 21:45:08.000000000 -0500
@@ -793,6 +793,21 @@
 {
 	return 0;
 }
+
+static int dummy_socket_getpeersec(struct socket *sock,
+                                   char __user *optval, 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 @@
 	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 */
 }
 
diff -urN -X dontdiff linux-2.6.0-test11.orig/security/selinux/hooks.c linux-2.6.0-test11.w1/security/selinux/hooks.c
--- linux-2.6.0-test11.orig/security/selinux/hooks.c	2003-10-15 08:53:19.000000000 -0400
+++ linux-2.6.0-test11.w1/security/selinux/hooks.c	2003-12-12 22:06:21.000000000 -0500
@@ -242,6 +242,39 @@
 	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;
@@ -2576,6 +2609,7 @@
 					      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;
@@ -2594,6 +2628,14 @@
 	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;
 }
 
@@ -2621,6 +2663,53 @@
 	return 0;
 }
 
+static int selinux_socket_getpeersec(struct socket *sock,
+				     char __user *optval, 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 = -ENOSPC;
+		goto out_free;
+	}
+	
+	if (copy_to_user(optval, scontext, scontext_len))
+		err = -EFAULT;
+
+	*len = scontext_len;
+
+out_free:	
+	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);
+}
+
 #endif
 
 static int ipc_alloc_security(struct task_struct *task,
@@ -3356,6 +3445,9 @@
 	.socket_getsockopt =		selinux_socket_getsockopt,
 	.socket_setsockopt =		selinux_socket_setsockopt,
 	.socket_shutdown =		selinux_socket_shutdown,
+	.socket_getpeersec =		selinux_socket_getpeersec,
+	.sk_alloc_security =		selinux_sk_alloc_security,
+	.sk_free_security =		selinux_sk_free_security,
 #endif
 };
 
diff -urN -X dontdiff linux-2.6.0-test11.orig/security/selinux/include/objsec.h linux-2.6.0-test11.w1/security/selinux/include/objsec.h
--- linux-2.6.0-test11.orig/security/selinux/include/objsec.h	2003-09-27 20:50:18.000000000 -0400
+++ linux-2.6.0-test11.w1/security/selinux/include/objsec.h	2003-12-12 20:24:39.000000000 -0500
@@ -93,6 +93,12 @@
 	unsigned char set;
 };
 
+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_ */

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] SO_PEERSEC - security credentials for Unix stream sockets
  2003-12-13  3:44   ` James Morris
@ 2003-12-16  1:32     ` Chris Wright
  2003-12-16 13:19       ` James Morris
  0 siblings, 1 reply; 10+ messages in thread
From: Chris Wright @ 2003-12-16  1:32 UTC (permalink / raw)
  To: James Morris
  Cc: David S. Miller, kuznet, linux-security-module, Stephen Smalley,
	netdev

* James Morris (jmorris@redhat.com) wrote:
> I'm not sure how this would be a namespace issue -- do you mean a data 
> format issue?

I just mean, applications are coded for specific security module.

> Yep, allowing the security module to update the returned length is now
> implemented.
> 
> > Perhaps buffer is too small, can len be vector for that info?
> 
> I would not advise updating len on error -- it's a bad idea in general to
> interpret any returned data from failed syscalls except the error number.

Right, in some cases a NULL buffer or 0 buflen is a probe for size.

thanks,
-chris
-- 
Linux Security Modules     http://lsm.immunix.org     http://lsm.bkbits.net

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] SO_PEERSEC - security credentials for Unix stream sockets
  2003-12-16  1:32     ` Chris Wright
@ 2003-12-16 13:19       ` James Morris
  2003-12-16 13:47         ` Stephen Smalley
  2003-12-16 17:49         ` Chris Wright
  0 siblings, 2 replies; 10+ messages in thread
From: James Morris @ 2003-12-16 13:19 UTC (permalink / raw)
  To: Chris Wright
  Cc: David S. Miller, kuznet, linux-security-module, Stephen Smalley,
	netdev

On Mon, 15 Dec 2003, Chris Wright wrote:

> * James Morris (jmorris@redhat.com) wrote:
> > I'm not sure how this would be a namespace issue -- do you mean a data 
> > format issue?
> 
> I just mean, applications are coded for specific security module.

Applications which are security aware (i.e. only a few of them) will need 
to know the semantics of the security model that they are interacting 
with, so I'm not sure that namespace is going to be the biggest challenge.  

A good way to handle this is to use external pluggable modules like PAM.

> 
> > Yep, allowing the security module to update the returned length is now
> > implemented.
> > 
> > > Perhaps buffer is too small, can len be vector for that info?
> > 
> > I would not advise updating len on error -- it's a bad idea in general to
> > interpret any returned data from failed syscalls except the error number.
> 
> Right, in some cases a NULL buffer or 0 buflen is a probe for size.

It's not reliable: the required buffer size could change between calls.  
Do you know of any examples of syscalls which do this?


- James
-- 
James Morris
<jmorris@redhat.com>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] SO_PEERSEC - security credentials for Unix stream sockets
  2003-12-16 13:19       ` James Morris
@ 2003-12-16 13:47         ` Stephen Smalley
  2003-12-16 19:43           ` James Morris
  2003-12-16 17:49         ` Chris Wright
  1 sibling, 1 reply; 10+ messages in thread
From: Stephen Smalley @ 2003-12-16 13:47 UTC (permalink / raw)
  To: James Morris; +Cc: David S. Miller, Chris Wright, lsm, netdev, kuznet

On Tue, 2003-12-16 at 08:19, James Morris wrote:
> It's not reliable: the required buffer size could change between calls.  
> Do you know of any examples of syscalls which do this?

getxattr(2).  From the man page:

       An empty buffer of size zero can be passed into these calls  to  return
       the  current size of the named extended attribute, which can be used to
       estimate the size of a buffer which is sufficiently large to  hold  the
       value associated with the extended attribute.
                                                                                
       The  interface  is  designed to allow guessing of initial buffer sizes,
       and to enlarge buffers when the return value indicates that the  buffer
       provided was too small.

The SELinux getfilecon(3) function (libselinux/src/getfilecon.c) uses
getxattr(2) in this manner.

-- 
Stephen Smalley <sds@epoch.ncsc.mil>
National Security Agency

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] SO_PEERSEC - security credentials for Unix stream sockets
  2003-12-16 13:19       ` James Morris
  2003-12-16 13:47         ` Stephen Smalley
@ 2003-12-16 17:49         ` Chris Wright
  1 sibling, 0 replies; 10+ messages in thread
From: Chris Wright @ 2003-12-16 17:49 UTC (permalink / raw)
  To: James Morris
  Cc: Chris Wright, Stephen Smalley, netdev, David S. Miller, kuznet,
	linux-security-module

* James Morris (jmorris@redhat.com) wrote:
> On Mon, 15 Dec 2003, Chris Wright wrote:
> It's not reliable: the required buffer size could change between calls.  
> Do you know of any examples of syscalls which do this?

Of course, just kludgy best guess.  getgroups, getxattr come to mind
right off.

thanks,
-chris
-- 
Linux Security Modules     http://lsm.immunix.org     http://lsm.bkbits.net

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC] SO_PEERSEC - security credentials for Unix stream sockets
  2003-12-16 13:47         ` Stephen Smalley
@ 2003-12-16 19:43           ` James Morris
  0 siblings, 0 replies; 10+ messages in thread
From: James Morris @ 2003-12-16 19:43 UTC (permalink / raw)
  To: Stephen Smalley; +Cc: David S. Miller, Chris Wright, lsm, netdev, kuznet

On Tue, 16 Dec 2003, Stephen Smalley wrote:

> On Tue, 2003-12-16 at 08:19, James Morris wrote:
> > It's not reliable: the required buffer size could change between calls.  
> > Do you know of any examples of syscalls which do this?
> 
> getxattr(2).  From the man page:

Ok, I've changed the arguments in the LSM hook so that the security module
can allow probing of the data length if it wants (and the SELinux module
now does this).  Security modules are now fully responsible for copying
the getsockopt data length back to userspace.

Please see below.

- James
-- 
James Morris
<jmorris@redhat.com>

diff -urN -X dontdiff linux-2.6.0-test11.orig/include/asm-i386/socket.h linux-2.6.0-test11.w1/include/asm-i386/socket.h
--- linux-2.6.0-test11.orig/include/asm-i386/socket.h	2003-09-27 20:50:09.000000000 -0400
+++ linux-2.6.0-test11.w1/include/asm-i386/socket.h	2003-12-12 20:24:34.000000000 -0500
@@ -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. */
diff -urN -X dontdiff linux-2.6.0-test11.orig/include/linux/security.h linux-2.6.0-test11.w1/include/linux/security.h
--- linux-2.6.0-test11.orig/include/linux/security.h	2003-10-15 08:53:19.000000000 -0400
+++ linux-2.6.0-test11.w1/include/linux/security.h	2003-12-16 11:42:42.000000000 -0500
@@ -757,6 +757,22 @@
  *	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 @@
 	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 @@
 {
 	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 @@
 {
 	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 */
diff -urN -X dontdiff linux-2.6.0-test11.orig/include/net/sock.h linux-2.6.0-test11.w1/include/net/sock.h
--- linux-2.6.0-test11.orig/include/net/sock.h	2003-12-01 15:27:07.000000000 -0500
+++ linux-2.6.0-test11.w1/include/net/sock.h	2003-12-12 20:44:15.000000000 -0500
@@ -246,6 +246,7 @@
 	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);
diff -urN -X dontdiff linux-2.6.0-test11.orig/net/core/sock.c linux-2.6.0-test11.w1/net/core/sock.c
--- linux-2.6.0-test11.orig/net/core/sock.c	2003-12-01 15:27:04.000000000 -0500
+++ linux-2.6.0-test11.w1/net/core/sock.c	2003-12-16 11:51:21.000000000 -0500
@@ -564,6 +564,9 @@
 			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 @@
 			sock_lock_init(sk);
 		}
 		sk->sk_slab = slab;
+		
+		if (security_sk_alloc(sk, family, priority)) {
+			kmem_cache_free(sk->sk_slab, sk);
+			sk = NULL;
+		}
 	}
 	return sk;
 }
@@ -628,6 +636,7 @@
 		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);
 }
diff -urN -X dontdiff linux-2.6.0-test11.orig/security/dummy.c linux-2.6.0-test11.w1/security/dummy.c
--- linux-2.6.0-test11.orig/security/dummy.c	2003-10-15 08:53:19.000000000 -0400
+++ linux-2.6.0-test11.w1/security/dummy.c	2003-12-16 11:43:00.000000000 -0500
@@ -793,6 +793,21 @@
 {
 	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 @@
 	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 */
 }
 
diff -urN -X dontdiff linux-2.6.0-test11.orig/security/selinux/hooks.c linux-2.6.0-test11.w1/security/selinux/hooks.c
--- linux-2.6.0-test11.orig/security/selinux/hooks.c	2003-10-15 08:53:19.000000000 -0400
+++ linux-2.6.0-test11.w1/security/selinux/hooks.c	2003-12-16 11:40:16.000000000 -0500
@@ -242,6 +242,39 @@
 	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;
@@ -2576,6 +2609,7 @@
 					      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;
@@ -2594,6 +2628,14 @@
 	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;
 }
 
@@ -2621,6 +2663,54 @@
 	return 0;
 }
 
+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);
+}
+
 #endif
 
 static int ipc_alloc_security(struct task_struct *task,
@@ -3356,6 +3446,9 @@
 	.socket_getsockopt =		selinux_socket_getsockopt,
 	.socket_setsockopt =		selinux_socket_setsockopt,
 	.socket_shutdown =		selinux_socket_shutdown,
+	.socket_getpeersec =		selinux_socket_getpeersec,
+	.sk_alloc_security =		selinux_sk_alloc_security,
+	.sk_free_security =		selinux_sk_free_security,
 #endif
 };
 
diff -urN -X dontdiff linux-2.6.0-test11.orig/security/selinux/include/objsec.h linux-2.6.0-test11.w1/security/selinux/include/objsec.h
--- linux-2.6.0-test11.orig/security/selinux/include/objsec.h	2003-09-27 20:50:18.000000000 -0400
+++ linux-2.6.0-test11.w1/security/selinux/include/objsec.h	2003-12-12 20:24:39.000000000 -0500
@@ -93,6 +93,12 @@
 	unsigned char set;
 };
 
+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_ */

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2003-12-16 19:43 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-12-10 16:33 [RFC] SO_PEERSEC - security credentials for Unix stream sockets James Morris
2003-12-10 22:56 ` David S. Miller
2003-12-12 14:25   ` James Morris
2003-12-13  0:16 ` Chris Wright
2003-12-13  3:44   ` James Morris
2003-12-16  1:32     ` Chris Wright
2003-12-16 13:19       ` James Morris
2003-12-16 13:47         ` Stephen Smalley
2003-12-16 19:43           ` James Morris
2003-12-16 17:49         ` Chris Wright

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).