netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC v4 01/11] lsm: add security_socket_closed()
       [not found] <1304603961-2517-1-git-send-email-y>
  2011-05-05 13:59 ` [RFC v4 01/11] lsm: add security_socket_closed() y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (41 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

Allow a module to update security informations when a socket is closed.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 include/linux/security.h |   10 ++++++++++
 net/socket.c             |    1 +
 security/capability.c    |    5 +++++
 security/security.c      |    5 +++++
 4 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index ca02f17..da0d59e 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -918,6 +918,9 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	@sock contains the socket structure.
  *	@how contains the flag indicating how future sends and receives are handled.
  *	Return 0 if permission is granted.
+ * @socket_close:
+ *	Allow a module to update security informations when a socket is closed
+ *	@sock is closed.
  * @socket_sock_rcv_skb:
  *	Check permissions on incoming network packets.  This hook is distinct
  *	from Netfilter's IP input hooks since it is the first time that the
@@ -1593,6 +1596,7 @@ struct security_operations {
 	int (*socket_getsockopt) (struct socket *sock, int level, int optname);
 	int (*socket_setsockopt) (struct socket *sock, int level, int optname);
 	int (*socket_shutdown) (struct socket *sock, int how);
+	void (*socket_close) (struct socket *sock);
 	int (*socket_sock_rcv_skb) (struct sock *sk, struct sk_buff *skb);
 	int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
 	int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
@@ -2559,6 +2563,7 @@ int security_socket_getpeername(struct socket *sock);
 int security_socket_getsockopt(struct socket *sock, int level, int optname);
 int security_socket_setsockopt(struct socket *sock, int level, int optname);
 int security_socket_shutdown(struct socket *sock, int how);
+void security_socket_close(struct socket *sock);
 int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
 int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
 				      int __user *optlen, unsigned len);
@@ -2674,6 +2679,11 @@ static inline int security_socket_shutdown(struct socket *sock, int how)
 {
 	return 0;
 }
+
+static inline void security_socket_close(struct socket *sock)
+{
+}
+
 static inline int security_sock_rcv_skb(struct sock *sk,
 					struct sk_buff *skb)
 {
diff --git a/net/socket.c b/net/socket.c
index 310d16b..d588e9e 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1093,6 +1093,7 @@ static int sock_close(struct inode *inode, struct file *filp)
 		printk(KERN_DEBUG "sock_close: NULL inode\n");
 		return 0;
 	}
+	security_socket_close(SOCKET_I(inode));
 	sock_release(SOCKET_I(inode));
 	return 0;
 }
diff --git a/security/capability.c b/security/capability.c
index 2984ea4..1f8bbe2 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -629,6 +629,10 @@ static int cap_socket_shutdown(struct socket *sock, int how)
 	return 0;
 }
 
+static void cap_socket_close(struct socket *sock)
+{
+}
+
 static int cap_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	return 0;
@@ -1025,6 +1029,7 @@ void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, socket_setsockopt);
 	set_to_cap_if_null(ops, socket_getsockopt);
 	set_to_cap_if_null(ops, socket_shutdown);
+	set_to_cap_if_null(ops, socket_close);
 	set_to_cap_if_null(ops, socket_sock_rcv_skb);
 	set_to_cap_if_null(ops, socket_getpeersec_stream);
 	set_to_cap_if_null(ops, socket_getpeersec_dgram);
diff --git a/security/security.c b/security/security.c
index 1011423..84187d8 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1074,6 +1074,11 @@ int security_socket_shutdown(struct socket *sock, int how)
 	return security_ops->socket_shutdown(sock, how);
 }
 
+void security_socket_close(struct socket *sock)
+{
+	return security_ops->socket_close(sock);
+}
+
 int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	return security_ops->socket_sock_rcv_skb(sk, skb);
-- 
1.7.4.1


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

* [RFC v4 01/11] lsm: add security_socket_closed()
       [not found] <1304603961-2517-1-git-send-email-y>
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (42 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

Allow a module to update security informations when a socket is closed.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 include/linux/security.h |   10 ++++++++++
 net/socket.c             |    1 +
 security/capability.c    |    5 +++++
 security/security.c      |    5 +++++
 4 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index ca02f17..da0d59e 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -918,6 +918,9 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	@sock contains the socket structure.
  *	@how contains the flag indicating how future sends and receives are handled.
  *	Return 0 if permission is granted.
+ * @socket_close:
+ *	Allow a module to update security informations when a socket is closed
+ *	@sock is closed.
  * @socket_sock_rcv_skb:
  *	Check permissions on incoming network packets.  This hook is distinct
  *	from Netfilter's IP input hooks since it is the first time that the
@@ -1593,6 +1596,7 @@ struct security_operations {
 	int (*socket_getsockopt) (struct socket *sock, int level, int optname);
 	int (*socket_setsockopt) (struct socket *sock, int level, int optname);
 	int (*socket_shutdown) (struct socket *sock, int how);
+	void (*socket_close) (struct socket *sock);
 	int (*socket_sock_rcv_skb) (struct sock *sk, struct sk_buff *skb);
 	int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
 	int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
@@ -2559,6 +2563,7 @@ int security_socket_getpeername(struct socket *sock);
 int security_socket_getsockopt(struct socket *sock, int level, int optname);
 int security_socket_setsockopt(struct socket *sock, int level, int optname);
 int security_socket_shutdown(struct socket *sock, int how);
+void security_socket_close(struct socket *sock);
 int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
 int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
 				      int __user *optlen, unsigned len);
@@ -2674,6 +2679,11 @@ static inline int security_socket_shutdown(struct socket *sock, int how)
 {
 	return 0;
 }
+
+static inline void security_socket_close(struct socket *sock)
+{
+}
+
 static inline int security_sock_rcv_skb(struct sock *sk,
 					struct sk_buff *skb)
 {
diff --git a/net/socket.c b/net/socket.c
index 310d16b..d588e9e 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1093,6 +1093,7 @@ static int sock_close(struct inode *inode, struct file *filp)
 		printk(KERN_DEBUG "sock_close: NULL inode\n");
 		return 0;
 	}
+	security_socket_close(SOCKET_I(inode));
 	sock_release(SOCKET_I(inode));
 	return 0;
 }
diff --git a/security/capability.c b/security/capability.c
index 2984ea4..1f8bbe2 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -629,6 +629,10 @@ static int cap_socket_shutdown(struct socket *sock, int how)
 	return 0;
 }
 
+static void cap_socket_close(struct socket *sock)
+{
+}
+
 static int cap_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	return 0;
@@ -1025,6 +1029,7 @@ void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, socket_setsockopt);
 	set_to_cap_if_null(ops, socket_getsockopt);
 	set_to_cap_if_null(ops, socket_shutdown);
+	set_to_cap_if_null(ops, socket_close);
 	set_to_cap_if_null(ops, socket_sock_rcv_skb);
 	set_to_cap_if_null(ops, socket_getpeersec_stream);
 	set_to_cap_if_null(ops, socket_getpeersec_dgram);
diff --git a/security/security.c b/security/security.c
index 1011423..84187d8 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1074,6 +1074,11 @@ int security_socket_shutdown(struct socket *sock, int how)
 	return security_ops->socket_shutdown(sock, how);
 }
 
+void security_socket_close(struct socket *sock)
+{
+	return security_ops->socket_close(sock);
+}
+
 int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	return security_ops->socket_sock_rcv_skb(sk, skb);
-- 
1.7.4.1

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

* [RFC v4 01/11] lsm: add security_socket_closed()
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (2 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` [RFC v4 02/11] lsm: reintroduce security_socket_post_accept() y
                   ` (39 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

Allow a module to update security informations when a socket is closed.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 include/linux/security.h |   10 ++++++++++
 net/socket.c             |    1 +
 security/capability.c    |    5 +++++
 security/security.c      |    5 +++++
 4 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index ca02f17..da0d59e 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -918,6 +918,9 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	@sock contains the socket structure.
  *	@how contains the flag indicating how future sends and receives are handled.
  *	Return 0 if permission is granted.
+ * @socket_close:
+ *	Allow a module to update security informations when a socket is closed
+ *	@sock is closed.
  * @socket_sock_rcv_skb:
  *	Check permissions on incoming network packets.  This hook is distinct
  *	from Netfilter's IP input hooks since it is the first time that the
@@ -1593,6 +1596,7 @@ struct security_operations {
 	int (*socket_getsockopt) (struct socket *sock, int level, int optname);
 	int (*socket_setsockopt) (struct socket *sock, int level, int optname);
 	int (*socket_shutdown) (struct socket *sock, int how);
+	void (*socket_close) (struct socket *sock);
 	int (*socket_sock_rcv_skb) (struct sock *sk, struct sk_buff *skb);
 	int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
 	int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
@@ -2559,6 +2563,7 @@ int security_socket_getpeername(struct socket *sock);
 int security_socket_getsockopt(struct socket *sock, int level, int optname);
 int security_socket_setsockopt(struct socket *sock, int level, int optname);
 int security_socket_shutdown(struct socket *sock, int how);
+void security_socket_close(struct socket *sock);
 int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
 int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
 				      int __user *optlen, unsigned len);
@@ -2674,6 +2679,11 @@ static inline int security_socket_shutdown(struct socket *sock, int how)
 {
 	return 0;
 }
+
+static inline void security_socket_close(struct socket *sock)
+{
+}
+
 static inline int security_sock_rcv_skb(struct sock *sk,
 					struct sk_buff *skb)
 {
diff --git a/net/socket.c b/net/socket.c
index 310d16b..d588e9e 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1093,6 +1093,7 @@ static int sock_close(struct inode *inode, struct file *filp)
 		printk(KERN_DEBUG "sock_close: NULL inode\n");
 		return 0;
 	}
+	security_socket_close(SOCKET_I(inode));
 	sock_release(SOCKET_I(inode));
 	return 0;
 }
diff --git a/security/capability.c b/security/capability.c
index 2984ea4..1f8bbe2 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -629,6 +629,10 @@ static int cap_socket_shutdown(struct socket *sock, int how)
 	return 0;
 }
 
+static void cap_socket_close(struct socket *sock)
+{
+}
+
 static int cap_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	return 0;
@@ -1025,6 +1029,7 @@ void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, socket_setsockopt);
 	set_to_cap_if_null(ops, socket_getsockopt);
 	set_to_cap_if_null(ops, socket_shutdown);
+	set_to_cap_if_null(ops, socket_close);
 	set_to_cap_if_null(ops, socket_sock_rcv_skb);
 	set_to_cap_if_null(ops, socket_getpeersec_stream);
 	set_to_cap_if_null(ops, socket_getpeersec_dgram);
diff --git a/security/security.c b/security/security.c
index 1011423..84187d8 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1074,6 +1074,11 @@ int security_socket_shutdown(struct socket *sock, int how)
 	return security_ops->socket_shutdown(sock, how);
 }
 
+void security_socket_close(struct socket *sock)
+{
+	return security_ops->socket_close(sock);
+}
+
 int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	return security_ops->socket_sock_rcv_skb(sk, skb);
-- 
1.7.4.1


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

* [RFC v4 01/11] lsm: add security_socket_closed()
       [not found] <1304603961-2517-1-git-send-email-y>
  2011-05-05 13:59 ` [RFC v4 01/11] lsm: add security_socket_closed() y
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (40 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

Allow a module to update security informations when a socket is closed.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 include/linux/security.h |   10 ++++++++++
 net/socket.c             |    1 +
 security/capability.c    |    5 +++++
 security/security.c      |    5 +++++
 4 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index ca02f17..da0d59e 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -918,6 +918,9 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	@sock contains the socket structure.
  *	@how contains the flag indicating how future sends and receives are handled.
  *	Return 0 if permission is granted.
+ * @socket_close:
+ *	Allow a module to update security informations when a socket is closed
+ *	@sock is closed.
  * @socket_sock_rcv_skb:
  *	Check permissions on incoming network packets.  This hook is distinct
  *	from Netfilter's IP input hooks since it is the first time that the
@@ -1593,6 +1596,7 @@ struct security_operations {
 	int (*socket_getsockopt) (struct socket *sock, int level, int optname);
 	int (*socket_setsockopt) (struct socket *sock, int level, int optname);
 	int (*socket_shutdown) (struct socket *sock, int how);
+	void (*socket_close) (struct socket *sock);
 	int (*socket_sock_rcv_skb) (struct sock *sk, struct sk_buff *skb);
 	int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
 	int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
@@ -2559,6 +2563,7 @@ int security_socket_getpeername(struct socket *sock);
 int security_socket_getsockopt(struct socket *sock, int level, int optname);
 int security_socket_setsockopt(struct socket *sock, int level, int optname);
 int security_socket_shutdown(struct socket *sock, int how);
+void security_socket_close(struct socket *sock);
 int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
 int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
 				      int __user *optlen, unsigned len);
@@ -2674,6 +2679,11 @@ static inline int security_socket_shutdown(struct socket *sock, int how)
 {
 	return 0;
 }
+
+static inline void security_socket_close(struct socket *sock)
+{
+}
+
 static inline int security_sock_rcv_skb(struct sock *sk,
 					struct sk_buff *skb)
 {
diff --git a/net/socket.c b/net/socket.c
index 310d16b..d588e9e 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1093,6 +1093,7 @@ static int sock_close(struct inode *inode, struct file *filp)
 		printk(KERN_DEBUG "sock_close: NULL inode\n");
 		return 0;
 	}
+	security_socket_close(SOCKET_I(inode));
 	sock_release(SOCKET_I(inode));
 	return 0;
 }
diff --git a/security/capability.c b/security/capability.c
index 2984ea4..1f8bbe2 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -629,6 +629,10 @@ static int cap_socket_shutdown(struct socket *sock, int how)
 	return 0;
 }
 
+static void cap_socket_close(struct socket *sock)
+{
+}
+
 static int cap_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	return 0;
@@ -1025,6 +1029,7 @@ void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, socket_setsockopt);
 	set_to_cap_if_null(ops, socket_getsockopt);
 	set_to_cap_if_null(ops, socket_shutdown);
+	set_to_cap_if_null(ops, socket_close);
 	set_to_cap_if_null(ops, socket_sock_rcv_skb);
 	set_to_cap_if_null(ops, socket_getpeersec_stream);
 	set_to_cap_if_null(ops, socket_getpeersec_dgram);
diff --git a/security/security.c b/security/security.c
index 1011423..84187d8 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1074,6 +1074,11 @@ int security_socket_shutdown(struct socket *sock, int how)
 	return security_ops->socket_shutdown(sock, how);
 }
 
+void security_socket_close(struct socket *sock)
+{
+	return security_ops->socket_close(sock);
+}
+
 int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	return security_ops->socket_sock_rcv_skb(sk, skb);
-- 
1.7.4.1


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

* [RFC v4 02/11] lsm: reintroduce security_socket_post_accept()
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (6 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` [RFC v4 03/11] snet: introduce snet_core y
                   ` (35 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

snet needs to reintroduce this hook, as it was designed to be: a hook for
updating security informations on objects.

Originally, This was a direct revert of commit
8651d5c0b1f874c5b8307ae2b858bc40f9f02482.

But from the comment of Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> :

> Please move security_socket_post_accept() to before fd_install().
> Otherwise, other threads which share fd tables can use
> security-informations-not-yet-updated accept()ed sockets.

Signed-off-by: Samir Bellabes <sam@synack.fr>
Acked-by: Serge Hallyn <serue@us.ibm.com>
---
 include/linux/security.h |   13 +++++++++++++
 net/socket.c             |    2 ++
 security/capability.c    |    5 +++++
 security/security.c      |    5 +++++
 4 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index da0d59e..02effe5 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -875,6 +875,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	@sock contains the listening socket structure.
  *	@newsock contains the newly created server socket for connection.
  *	Return 0 if permission is granted.
+ * @socket_post_accept:
+ *	This hook allows a security module to copy security
+ *	information into the newly created socket's inode.
+ *	@sock contains the listening socket structure.
+ *	@newsock contains the newly created server socket for connection.
  * @socket_sendmsg:
  *	Check permission before transmitting a message to another socket.
  *	@sock contains the socket structure.
@@ -1587,6 +1592,8 @@ struct security_operations {
 			       struct sockaddr *address, int addrlen);
 	int (*socket_listen) (struct socket *sock, int backlog);
 	int (*socket_accept) (struct socket *sock, struct socket *newsock);
+	void (*socket_post_accept) (struct socket *sock,
+				    struct socket *newsock);
 	int (*socket_sendmsg) (struct socket *sock,
 			       struct msghdr *msg, int size);
 	int (*socket_recvmsg) (struct socket *sock,
@@ -2555,6 +2562,7 @@ int security_socket_bind(struct socket *sock, struct sockaddr *address, int addr
 int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen);
 int security_socket_listen(struct socket *sock, int backlog);
 int security_socket_accept(struct socket *sock, struct socket *newsock);
+void security_socket_post_accept(struct socket *sock, struct socket *newsock);
 int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size);
 int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
 			    int size, int flags);
@@ -2640,6 +2648,11 @@ static inline int security_socket_accept(struct socket *sock,
 	return 0;
 }
 
+static inline void security_socket_post_accept(struct socket *sock,
+					       struct socket *newsock)
+{
+}
+
 static inline int security_socket_sendmsg(struct socket *sock,
 					  struct msghdr *msg, int size)
 {
diff --git a/net/socket.c b/net/socket.c
index d588e9e..7807904 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1535,6 +1535,8 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
 			goto out_fd;
 	}
 
+	security_socket_post_accept(sock, newsock);
+
 	/* File flags are not inherited via accept() unlike another OSes. */
 
 	fd_install(newfd, newfile);
diff --git a/security/capability.c b/security/capability.c
index 1f8bbe2..da68c60 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -593,6 +593,10 @@ static int cap_socket_accept(struct socket *sock, struct socket *newsock)
 	return 0;
 }
 
+static void cap_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+}
+
 static int cap_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
 {
 	return 0;
@@ -1022,6 +1026,7 @@ void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, socket_connect);
 	set_to_cap_if_null(ops, socket_listen);
 	set_to_cap_if_null(ops, socket_accept);
+	set_to_cap_if_null(ops, socket_post_accept);
 	set_to_cap_if_null(ops, socket_sendmsg);
 	set_to_cap_if_null(ops, socket_recvmsg);
 	set_to_cap_if_null(ops, socket_getsockname);
diff --git a/security/security.c b/security/security.c
index 84187d8..eda2b75 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1038,6 +1038,11 @@ int security_socket_accept(struct socket *sock, struct socket *newsock)
 	return security_ops->socket_accept(sock, newsock);
 }
 
+void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+	security_ops->socket_post_accept(sock, newsock);
+}
+
 int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
 {
 	return security_ops->socket_sendmsg(sock, msg, size);
-- 
1.7.4.1


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

* [RFC v4 02/11] lsm: reintroduce security_socket_post_accept()
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (4 preceding siblings ...)
  2011-05-05 13:59 ` [RFC v4 02/11] lsm: reintroduce security_socket_post_accept() y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (37 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

snet needs to reintroduce this hook, as it was designed to be: a hook for
updating security informations on objects.

Originally, This was a direct revert of commit
8651d5c0b1f874c5b8307ae2b858bc40f9f02482.

But from the comment of Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> :

> Please move security_socket_post_accept() to before fd_install().
> Otherwise, other threads which share fd tables can use
> security-informations-not-yet-updated accept()ed sockets.

Signed-off-by: Samir Bellabes <sam@synack.fr>
Acked-by: Serge Hallyn <serue@us.ibm.com>
---
 include/linux/security.h |   13 +++++++++++++
 net/socket.c             |    2 ++
 security/capability.c    |    5 +++++
 security/security.c      |    5 +++++
 4 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index da0d59e..02effe5 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -875,6 +875,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	@sock contains the listening socket structure.
  *	@newsock contains the newly created server socket for connection.
  *	Return 0 if permission is granted.
+ * @socket_post_accept:
+ *	This hook allows a security module to copy security
+ *	information into the newly created socket's inode.
+ *	@sock contains the listening socket structure.
+ *	@newsock contains the newly created server socket for connection.
  * @socket_sendmsg:
  *	Check permission before transmitting a message to another socket.
  *	@sock contains the socket structure.
@@ -1587,6 +1592,8 @@ struct security_operations {
 			       struct sockaddr *address, int addrlen);
 	int (*socket_listen) (struct socket *sock, int backlog);
 	int (*socket_accept) (struct socket *sock, struct socket *newsock);
+	void (*socket_post_accept) (struct socket *sock,
+				    struct socket *newsock);
 	int (*socket_sendmsg) (struct socket *sock,
 			       struct msghdr *msg, int size);
 	int (*socket_recvmsg) (struct socket *sock,
@@ -2555,6 +2562,7 @@ int security_socket_bind(struct socket *sock, struct sockaddr *address, int addr
 int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen);
 int security_socket_listen(struct socket *sock, int backlog);
 int security_socket_accept(struct socket *sock, struct socket *newsock);
+void security_socket_post_accept(struct socket *sock, struct socket *newsock);
 int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size);
 int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
 			    int size, int flags);
@@ -2640,6 +2648,11 @@ static inline int security_socket_accept(struct socket *sock,
 	return 0;
 }
 
+static inline void security_socket_post_accept(struct socket *sock,
+					       struct socket *newsock)
+{
+}
+
 static inline int security_socket_sendmsg(struct socket *sock,
 					  struct msghdr *msg, int size)
 {
diff --git a/net/socket.c b/net/socket.c
index d588e9e..7807904 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1535,6 +1535,8 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
 			goto out_fd;
 	}
 
+	security_socket_post_accept(sock, newsock);
+
 	/* File flags are not inherited via accept() unlike another OSes. */
 
 	fd_install(newfd, newfile);
diff --git a/security/capability.c b/security/capability.c
index 1f8bbe2..da68c60 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -593,6 +593,10 @@ static int cap_socket_accept(struct socket *sock, struct socket *newsock)
 	return 0;
 }
 
+static void cap_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+}
+
 static int cap_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
 {
 	return 0;
@@ -1022,6 +1026,7 @@ void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, socket_connect);
 	set_to_cap_if_null(ops, socket_listen);
 	set_to_cap_if_null(ops, socket_accept);
+	set_to_cap_if_null(ops, socket_post_accept);
 	set_to_cap_if_null(ops, socket_sendmsg);
 	set_to_cap_if_null(ops, socket_recvmsg);
 	set_to_cap_if_null(ops, socket_getsockname);
diff --git a/security/security.c b/security/security.c
index 84187d8..eda2b75 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1038,6 +1038,11 @@ int security_socket_accept(struct socket *sock, struct socket *newsock)
 	return security_ops->socket_accept(sock, newsock);
 }
 
+void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+	security_ops->socket_post_accept(sock, newsock);
+}
+
 int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
 {
 	return security_ops->socket_sendmsg(sock, msg, size);
-- 
1.7.4.1

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

* [RFC v4 02/11] lsm: reintroduce security_socket_post_accept()
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (5 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (36 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

snet needs to reintroduce this hook, as it was designed to be: a hook for
updating security informations on objects.

Originally, This was a direct revert of commit
8651d5c0b1f874c5b8307ae2b858bc40f9f02482.

But from the comment of Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> :

> Please move security_socket_post_accept() to before fd_install().
> Otherwise, other threads which share fd tables can use
> security-informations-not-yet-updated accept()ed sockets.

Signed-off-by: Samir Bellabes <sam@synack.fr>
Acked-by: Serge Hallyn <serue@us.ibm.com>
---
 include/linux/security.h |   13 +++++++++++++
 net/socket.c             |    2 ++
 security/capability.c    |    5 +++++
 security/security.c      |    5 +++++
 4 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index da0d59e..02effe5 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -875,6 +875,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	@sock contains the listening socket structure.
  *	@newsock contains the newly created server socket for connection.
  *	Return 0 if permission is granted.
+ * @socket_post_accept:
+ *	This hook allows a security module to copy security
+ *	information into the newly created socket's inode.
+ *	@sock contains the listening socket structure.
+ *	@newsock contains the newly created server socket for connection.
  * @socket_sendmsg:
  *	Check permission before transmitting a message to another socket.
  *	@sock contains the socket structure.
@@ -1587,6 +1592,8 @@ struct security_operations {
 			       struct sockaddr *address, int addrlen);
 	int (*socket_listen) (struct socket *sock, int backlog);
 	int (*socket_accept) (struct socket *sock, struct socket *newsock);
+	void (*socket_post_accept) (struct socket *sock,
+				    struct socket *newsock);
 	int (*socket_sendmsg) (struct socket *sock,
 			       struct msghdr *msg, int size);
 	int (*socket_recvmsg) (struct socket *sock,
@@ -2555,6 +2562,7 @@ int security_socket_bind(struct socket *sock, struct sockaddr *address, int addr
 int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen);
 int security_socket_listen(struct socket *sock, int backlog);
 int security_socket_accept(struct socket *sock, struct socket *newsock);
+void security_socket_post_accept(struct socket *sock, struct socket *newsock);
 int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size);
 int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
 			    int size, int flags);
@@ -2640,6 +2648,11 @@ static inline int security_socket_accept(struct socket *sock,
 	return 0;
 }
 
+static inline void security_socket_post_accept(struct socket *sock,
+					       struct socket *newsock)
+{
+}
+
 static inline int security_socket_sendmsg(struct socket *sock,
 					  struct msghdr *msg, int size)
 {
diff --git a/net/socket.c b/net/socket.c
index d588e9e..7807904 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1535,6 +1535,8 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
 			goto out_fd;
 	}
 
+	security_socket_post_accept(sock, newsock);
+
 	/* File flags are not inherited via accept() unlike another OSes. */
 
 	fd_install(newfd, newfile);
diff --git a/security/capability.c b/security/capability.c
index 1f8bbe2..da68c60 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -593,6 +593,10 @@ static int cap_socket_accept(struct socket *sock, struct socket *newsock)
 	return 0;
 }
 
+static void cap_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+}
+
 static int cap_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
 {
 	return 0;
@@ -1022,6 +1026,7 @@ void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, socket_connect);
 	set_to_cap_if_null(ops, socket_listen);
 	set_to_cap_if_null(ops, socket_accept);
+	set_to_cap_if_null(ops, socket_post_accept);
 	set_to_cap_if_null(ops, socket_sendmsg);
 	set_to_cap_if_null(ops, socket_recvmsg);
 	set_to_cap_if_null(ops, socket_getsockname);
diff --git a/security/security.c b/security/security.c
index 84187d8..eda2b75 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1038,6 +1038,11 @@ int security_socket_accept(struct socket *sock, struct socket *newsock)
 	return security_ops->socket_accept(sock, newsock);
 }
 
+void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+	security_ops->socket_post_accept(sock, newsock);
+}
+
 int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
 {
 	return security_ops->socket_sendmsg(sock, msg, size);
-- 
1.7.4.1


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

* [RFC v4 02/11] lsm: reintroduce security_socket_post_accept()
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (3 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (38 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

snet needs to reintroduce this hook, as it was designed to be: a hook for
updating security informations on objects.

Originally, This was a direct revert of commit
8651d5c0b1f874c5b8307ae2b858bc40f9f02482.

But from the comment of Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> :

> Please move security_socket_post_accept() to before fd_install().
> Otherwise, other threads which share fd tables can use
> security-informations-not-yet-updated accept()ed sockets.

Signed-off-by: Samir Bellabes <sam@synack.fr>
Acked-by: Serge Hallyn <serue@us.ibm.com>
---
 include/linux/security.h |   13 +++++++++++++
 net/socket.c             |    2 ++
 security/capability.c    |    5 +++++
 security/security.c      |    5 +++++
 4 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index da0d59e..02effe5 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -875,6 +875,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	@sock contains the listening socket structure.
  *	@newsock contains the newly created server socket for connection.
  *	Return 0 if permission is granted.
+ * @socket_post_accept:
+ *	This hook allows a security module to copy security
+ *	information into the newly created socket's inode.
+ *	@sock contains the listening socket structure.
+ *	@newsock contains the newly created server socket for connection.
  * @socket_sendmsg:
  *	Check permission before transmitting a message to another socket.
  *	@sock contains the socket structure.
@@ -1587,6 +1592,8 @@ struct security_operations {
 			       struct sockaddr *address, int addrlen);
 	int (*socket_listen) (struct socket *sock, int backlog);
 	int (*socket_accept) (struct socket *sock, struct socket *newsock);
+	void (*socket_post_accept) (struct socket *sock,
+				    struct socket *newsock);
 	int (*socket_sendmsg) (struct socket *sock,
 			       struct msghdr *msg, int size);
 	int (*socket_recvmsg) (struct socket *sock,
@@ -2555,6 +2562,7 @@ int security_socket_bind(struct socket *sock, struct sockaddr *address, int addr
 int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen);
 int security_socket_listen(struct socket *sock, int backlog);
 int security_socket_accept(struct socket *sock, struct socket *newsock);
+void security_socket_post_accept(struct socket *sock, struct socket *newsock);
 int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size);
 int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
 			    int size, int flags);
@@ -2640,6 +2648,11 @@ static inline int security_socket_accept(struct socket *sock,
 	return 0;
 }
 
+static inline void security_socket_post_accept(struct socket *sock,
+					       struct socket *newsock)
+{
+}
+
 static inline int security_socket_sendmsg(struct socket *sock,
 					  struct msghdr *msg, int size)
 {
diff --git a/net/socket.c b/net/socket.c
index d588e9e..7807904 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1535,6 +1535,8 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
 			goto out_fd;
 	}
 
+	security_socket_post_accept(sock, newsock);
+
 	/* File flags are not inherited via accept() unlike another OSes. */
 
 	fd_install(newfd, newfile);
diff --git a/security/capability.c b/security/capability.c
index 1f8bbe2..da68c60 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -593,6 +593,10 @@ static int cap_socket_accept(struct socket *sock, struct socket *newsock)
 	return 0;
 }
 
+static void cap_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+}
+
 static int cap_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
 {
 	return 0;
@@ -1022,6 +1026,7 @@ void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, socket_connect);
 	set_to_cap_if_null(ops, socket_listen);
 	set_to_cap_if_null(ops, socket_accept);
+	set_to_cap_if_null(ops, socket_post_accept);
 	set_to_cap_if_null(ops, socket_sendmsg);
 	set_to_cap_if_null(ops, socket_recvmsg);
 	set_to_cap_if_null(ops, socket_getsockname);
diff --git a/security/security.c b/security/security.c
index 84187d8..eda2b75 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1038,6 +1038,11 @@ int security_socket_accept(struct socket *sock, struct socket *newsock)
 	return security_ops->socket_accept(sock, newsock);
 }
 
+void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+	security_ops->socket_post_accept(sock, newsock);
+}
+
 int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
 {
 	return security_ops->socket_sendmsg(sock, msg, size);
-- 
1.7.4.1


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

* [RFC v4 03/11] snet: introduce snet_core
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (7 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (34 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch introduce snet_core.c, which provides functions to start and stop
snet's subsystems, and include/linux/snet.h, which provides interface with
userspace.

subsytems are:
    - snet_hooks    : LSM hooks
    - snet_netlink  : kernel-user communication (genetlink)
    - snet_event    : manages the list of protected syscalls
    - snet_verdict  : provides a waitqueue for syscalls and manage verdicts
                      from userspace
    - snet_ticket   : provides a granted-access ticket mecanism

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 include/linux/snet.h      |  117 +++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_core.c |   84 ++++++++++++++++++++++++++++++++
 2 files changed, 201 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/snet.h
 create mode 100644 security/snet/snet_core.c

diff --git a/include/linux/snet.h b/include/linux/snet.h
new file mode 100644
index 0000000..580b6b6
--- /dev/null
+++ b/include/linux/snet.h
@@ -0,0 +1,117 @@
+#ifndef _LINUX_SNET_H
+#define _LINUX_SNET_H
+
+#include <linux/in6.h>
+
+#define SNET_VERSION	0x1
+#define SNET_NAME	"snet"
+
+enum snet_syscall {
+	SNET_SOCKET_CREATE = 0,
+	SNET_SOCKET_BIND,
+	SNET_SOCKET_CONNECT,
+	SNET_SOCKET_LISTEN,
+	SNET_SOCKET_ACCEPT,
+	SNET_SOCKET_POST_ACCEPT,
+	SNET_SOCKET_SENDMSG,
+	SNET_SOCKET_RECVMSG,
+	SNET_SOCKET_SOCK_RCV_SKB,
+	SNET_SOCKET_CLOSE,
+	SNET_SOCKET_INVALID,
+};
+
+#define SNET_NR_SOCKET_TYPES SNET_SOCKET_INVALID
+
+struct snet_event {
+	enum snet_syscall syscall;
+	u8 protocol;
+};
+
+enum snet_verdict {
+	SNET_VERDICT_GRANT = 0,	/* grant the syscall */
+	SNET_VERDICT_DENY,	/* deny the syscall */
+	SNET_VERDICT_PENDING,	/* waiting for a decision */
+	SNET_VERDICT_NONE,	/* no decision can be set */
+	SNET_VERDICT_INVALID,
+};
+
+#define SNET_NR_VERDICT_TYPES SNET_VERDICT_INVALID
+
+enum snet_ticket_mode {
+	SNET_TICKET_OFF = 0,
+	SNET_TICKET_FIX,
+	SNET_TICKET_EXTEND,
+	SNET_TICKET_INVALID,
+};
+
+/* genetlink commands */
+enum {
+	SNET_C_UNSPEC,
+	SNET_C_VERSION,
+	SNET_C_REGISTER,
+	SNET_C_UNREGISTER,
+	SNET_C_INSERT,
+	SNET_C_REMOVE,
+	SNET_C_FLUSH,
+	SNET_C_LIST,
+	SNET_C_VERDICT,
+	SNET_C_CONFIG,
+	__SNET_C_MAX,
+};
+
+#define SNET_C_MAX (__SNET_C_MAX - 1)
+
+/* genetlink attributes */
+enum {
+	SNET_A_UNSPEC,
+	SNET_A_VERSION,		/* (NLA_U32) the snet protocol version	*/
+	SNET_A_VERDICT_ID,
+	SNET_A_FAMILY,
+	SNET_A_SYSCALL,		/* (NLA_U8)  a syscall identifier	*/
+	SNET_A_PROTOCOL,	/* (NLA_U8)  a protocol identifier	*/
+	SNET_A_UID,
+	SNET_A_PID,
+	SNET_A_TYPE,
+	SNET_A_IPV4SADDR,
+	SNET_A_IPV6SADDR,
+	SNET_A_IPV4DADDR,
+	SNET_A_IPV6DADDR,
+	SNET_A_SPORT,
+	SNET_A_DPORT,
+	SNET_A_VERDICT,
+	SNET_A_VERDICT_DELAY,
+	SNET_A_TICKET_DELAY,
+	SNET_A_TICKET_MODE,
+	__SNET_A_MAX,
+};
+
+#define SNET_A_MAX (__SNET_A_MAX - 1)
+
+#define SNET_GENL_NAME		"SNET"
+#define SNET_GENL_VERSION	SNET_VERSION
+
+struct snet_sock_half {
+	struct {
+		union {
+			__be32 ip;
+			struct in6_addr ip6;
+		};
+	} u3;
+	struct {
+		__be16 port;
+	} u;
+};
+
+struct snet_info {
+	u32 verdict_id;
+
+	enum snet_syscall syscall;
+	u8 protocol;
+	u8 family;
+
+	int type;
+	struct snet_sock_half src;
+	struct snet_sock_half dst;
+};
+
+#endif /* _LINUX_SNET_H */
diff --git a/security/snet/snet_core.c b/security/snet/snet_core.c
new file mode 100644
index 0000000..c8bc435
--- /dev/null
+++ b/security/snet/snet_core.c
@@ -0,0 +1,84 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include <linux/snet.h>
+#include "snet_hooks.h"
+#include "snet_event.h"
+#include "snet_verdict.h"
+#include "snet_ticket.h"
+#include "snet_utils.h"
+#include "snet_stats.h"
+
+unsigned int snet_evh_size = 16;
+module_param(snet_evh_size, uint, 0400);
+MODULE_PARM_DESC(snet_evh_size, "Set the size of the event hash table");
+
+unsigned int snet_vdh_size = 16;
+module_param(snet_vdh_size, uint, 0400);
+MODULE_PARM_DESC(snet_vdh_size, "Set the size of the verdict hash table");
+
+unsigned int snet_verdict_delay = 5;
+module_param(snet_verdict_delay, uint, 0600);
+MODULE_PARM_DESC(snet_verdict_delay, "Set the timeout for verdicts in secs");
+
+unsigned int snet_verdict_policy = SNET_VERDICT_GRANT;	/* permissive by default */
+module_param(snet_verdict_policy, uint, 0400);
+MODULE_PARM_DESC(snet_verdict_policy, "Set the default verdict");
+
+unsigned int snet_ticket_delay = 15;
+module_param(snet_ticket_delay, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_delay, "Set the timeout for tickets in secs");
+
+unsigned int snet_ticket_mode = SNET_TICKET_FIX;
+module_param(snet_ticket_mode, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_mode, "Set the mode for tickets");
+
+static __init int snet_init(void)
+{
+	int ret;
+
+	pr_debug("initializing: event_hash_size=%u "
+		 "verdict_hash_size=%u verdict_delay=%usecs "
+		 "default_policy=%s\n",
+		 snet_evh_size, snet_vdh_size, snet_verdict_delay,
+		 snet_verdict_name(snet_verdict_policy));
+
+	if (snet_verdict_policy >= SNET_VERDICT_INVALID) {
+		printk(KERN_ERR "snet: bad snet_verdict_policy\n");
+		ret = -EINVAL;
+		goto event_failed;
+	}
+
+	ret = snet_event_init();
+	if (ret < 0)
+		goto event_failed;
+
+	ret = snet_verdict_init();
+	if (ret < 0)
+		goto verdict_failed;
+
+	ret = snet_ticket_init();
+	if (ret < 0)
+		goto ticket_failed;
+
+	snet_stats_init();
+	/* snet_hooks_init() returns 0 or execute panic() */
+	snet_hooks_init();
+
+	pr_debug("started\n");
+	return 0;
+
+ticket_failed:
+	snet_verdict_exit();
+verdict_failed:
+	snet_event_exit();
+event_failed:
+	pr_debug("stopped\n");
+	return ret;
+}
+
+security_initcall(snet_init);
+
+MODULE_DESCRIPTION("snet - Security for NETwork syscalls");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samir Bellabes <sam@synack.fr>");
-- 
1.7.4.1


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

* [RFC v4 03/11] snet: introduce snet_core
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (10 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` [RFC v4 04/11] snet: introduce snet_event y
                   ` (31 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch introduce snet_core.c, which provides functions to start and stop
snet's subsystems, and include/linux/snet.h, which provides interface with
userspace.

subsytems are:
    - snet_hooks    : LSM hooks
    - snet_netlink  : kernel-user communication (genetlink)
    - snet_event    : manages the list of protected syscalls
    - snet_verdict  : provides a waitqueue for syscalls and manage verdicts
                      from userspace
    - snet_ticket   : provides a granted-access ticket mecanism

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 include/linux/snet.h      |  117 +++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_core.c |   84 ++++++++++++++++++++++++++++++++
 2 files changed, 201 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/snet.h
 create mode 100644 security/snet/snet_core.c

diff --git a/include/linux/snet.h b/include/linux/snet.h
new file mode 100644
index 0000000..580b6b6
--- /dev/null
+++ b/include/linux/snet.h
@@ -0,0 +1,117 @@
+#ifndef _LINUX_SNET_H
+#define _LINUX_SNET_H
+
+#include <linux/in6.h>
+
+#define SNET_VERSION	0x1
+#define SNET_NAME	"snet"
+
+enum snet_syscall {
+	SNET_SOCKET_CREATE = 0,
+	SNET_SOCKET_BIND,
+	SNET_SOCKET_CONNECT,
+	SNET_SOCKET_LISTEN,
+	SNET_SOCKET_ACCEPT,
+	SNET_SOCKET_POST_ACCEPT,
+	SNET_SOCKET_SENDMSG,
+	SNET_SOCKET_RECVMSG,
+	SNET_SOCKET_SOCK_RCV_SKB,
+	SNET_SOCKET_CLOSE,
+	SNET_SOCKET_INVALID,
+};
+
+#define SNET_NR_SOCKET_TYPES SNET_SOCKET_INVALID
+
+struct snet_event {
+	enum snet_syscall syscall;
+	u8 protocol;
+};
+
+enum snet_verdict {
+	SNET_VERDICT_GRANT = 0,	/* grant the syscall */
+	SNET_VERDICT_DENY,	/* deny the syscall */
+	SNET_VERDICT_PENDING,	/* waiting for a decision */
+	SNET_VERDICT_NONE,	/* no decision can be set */
+	SNET_VERDICT_INVALID,
+};
+
+#define SNET_NR_VERDICT_TYPES SNET_VERDICT_INVALID
+
+enum snet_ticket_mode {
+	SNET_TICKET_OFF = 0,
+	SNET_TICKET_FIX,
+	SNET_TICKET_EXTEND,
+	SNET_TICKET_INVALID,
+};
+
+/* genetlink commands */
+enum {
+	SNET_C_UNSPEC,
+	SNET_C_VERSION,
+	SNET_C_REGISTER,
+	SNET_C_UNREGISTER,
+	SNET_C_INSERT,
+	SNET_C_REMOVE,
+	SNET_C_FLUSH,
+	SNET_C_LIST,
+	SNET_C_VERDICT,
+	SNET_C_CONFIG,
+	__SNET_C_MAX,
+};
+
+#define SNET_C_MAX (__SNET_C_MAX - 1)
+
+/* genetlink attributes */
+enum {
+	SNET_A_UNSPEC,
+	SNET_A_VERSION,		/* (NLA_U32) the snet protocol version	*/
+	SNET_A_VERDICT_ID,
+	SNET_A_FAMILY,
+	SNET_A_SYSCALL,		/* (NLA_U8)  a syscall identifier	*/
+	SNET_A_PROTOCOL,	/* (NLA_U8)  a protocol identifier	*/
+	SNET_A_UID,
+	SNET_A_PID,
+	SNET_A_TYPE,
+	SNET_A_IPV4SADDR,
+	SNET_A_IPV6SADDR,
+	SNET_A_IPV4DADDR,
+	SNET_A_IPV6DADDR,
+	SNET_A_SPORT,
+	SNET_A_DPORT,
+	SNET_A_VERDICT,
+	SNET_A_VERDICT_DELAY,
+	SNET_A_TICKET_DELAY,
+	SNET_A_TICKET_MODE,
+	__SNET_A_MAX,
+};
+
+#define SNET_A_MAX (__SNET_A_MAX - 1)
+
+#define SNET_GENL_NAME		"SNET"
+#define SNET_GENL_VERSION	SNET_VERSION
+
+struct snet_sock_half {
+	struct {
+		union {
+			__be32 ip;
+			struct in6_addr ip6;
+		};
+	} u3;
+	struct {
+		__be16 port;
+	} u;
+};
+
+struct snet_info {
+	u32 verdict_id;
+
+	enum snet_syscall syscall;
+	u8 protocol;
+	u8 family;
+
+	int type;
+	struct snet_sock_half src;
+	struct snet_sock_half dst;
+};
+
+#endif /* _LINUX_SNET_H */
diff --git a/security/snet/snet_core.c b/security/snet/snet_core.c
new file mode 100644
index 0000000..c8bc435
--- /dev/null
+++ b/security/snet/snet_core.c
@@ -0,0 +1,84 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include <linux/snet.h>
+#include "snet_hooks.h"
+#include "snet_event.h"
+#include "snet_verdict.h"
+#include "snet_ticket.h"
+#include "snet_utils.h"
+#include "snet_stats.h"
+
+unsigned int snet_evh_size = 16;
+module_param(snet_evh_size, uint, 0400);
+MODULE_PARM_DESC(snet_evh_size, "Set the size of the event hash table");
+
+unsigned int snet_vdh_size = 16;
+module_param(snet_vdh_size, uint, 0400);
+MODULE_PARM_DESC(snet_vdh_size, "Set the size of the verdict hash table");
+
+unsigned int snet_verdict_delay = 5;
+module_param(snet_verdict_delay, uint, 0600);
+MODULE_PARM_DESC(snet_verdict_delay, "Set the timeout for verdicts in secs");
+
+unsigned int snet_verdict_policy = SNET_VERDICT_GRANT;	/* permissive by default */
+module_param(snet_verdict_policy, uint, 0400);
+MODULE_PARM_DESC(snet_verdict_policy, "Set the default verdict");
+
+unsigned int snet_ticket_delay = 15;
+module_param(snet_ticket_delay, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_delay, "Set the timeout for tickets in secs");
+
+unsigned int snet_ticket_mode = SNET_TICKET_FIX;
+module_param(snet_ticket_mode, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_mode, "Set the mode for tickets");
+
+static __init int snet_init(void)
+{
+	int ret;
+
+	pr_debug("initializing: event_hash_size=%u "
+		 "verdict_hash_size=%u verdict_delay=%usecs "
+		 "default_policy=%s\n",
+		 snet_evh_size, snet_vdh_size, snet_verdict_delay,
+		 snet_verdict_name(snet_verdict_policy));
+
+	if (snet_verdict_policy >= SNET_VERDICT_INVALID) {
+		printk(KERN_ERR "snet: bad snet_verdict_policy\n");
+		ret = -EINVAL;
+		goto event_failed;
+	}
+
+	ret = snet_event_init();
+	if (ret < 0)
+		goto event_failed;
+
+	ret = snet_verdict_init();
+	if (ret < 0)
+		goto verdict_failed;
+
+	ret = snet_ticket_init();
+	if (ret < 0)
+		goto ticket_failed;
+
+	snet_stats_init();
+	/* snet_hooks_init() returns 0 or execute panic() */
+	snet_hooks_init();
+
+	pr_debug("started\n");
+	return 0;
+
+ticket_failed:
+	snet_verdict_exit();
+verdict_failed:
+	snet_event_exit();
+event_failed:
+	pr_debug("stopped\n");
+	return ret;
+}
+
+security_initcall(snet_init);
+
+MODULE_DESCRIPTION("snet - Security for NETwork syscalls");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samir Bellabes <sam@synack.fr>");
-- 
1.7.4.1

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

* [RFC v4 03/11] snet: introduce snet_core
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (8 preceding siblings ...)
  2011-05-05 13:59 ` [RFC v4 03/11] snet: introduce snet_core y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (33 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch introduce snet_core.c, which provides functions to start and stop
snet's subsystems, and include/linux/snet.h, which provides interface with
userspace.

subsytems are:
    - snet_hooks    : LSM hooks
    - snet_netlink  : kernel-user communication (genetlink)
    - snet_event    : manages the list of protected syscalls
    - snet_verdict  : provides a waitqueue for syscalls and manage verdicts
                      from userspace
    - snet_ticket   : provides a granted-access ticket mecanism

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 include/linux/snet.h      |  117 +++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_core.c |   84 ++++++++++++++++++++++++++++++++
 2 files changed, 201 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/snet.h
 create mode 100644 security/snet/snet_core.c

diff --git a/include/linux/snet.h b/include/linux/snet.h
new file mode 100644
index 0000000..580b6b6
--- /dev/null
+++ b/include/linux/snet.h
@@ -0,0 +1,117 @@
+#ifndef _LINUX_SNET_H
+#define _LINUX_SNET_H
+
+#include <linux/in6.h>
+
+#define SNET_VERSION	0x1
+#define SNET_NAME	"snet"
+
+enum snet_syscall {
+	SNET_SOCKET_CREATE = 0,
+	SNET_SOCKET_BIND,
+	SNET_SOCKET_CONNECT,
+	SNET_SOCKET_LISTEN,
+	SNET_SOCKET_ACCEPT,
+	SNET_SOCKET_POST_ACCEPT,
+	SNET_SOCKET_SENDMSG,
+	SNET_SOCKET_RECVMSG,
+	SNET_SOCKET_SOCK_RCV_SKB,
+	SNET_SOCKET_CLOSE,
+	SNET_SOCKET_INVALID,
+};
+
+#define SNET_NR_SOCKET_TYPES SNET_SOCKET_INVALID
+
+struct snet_event {
+	enum snet_syscall syscall;
+	u8 protocol;
+};
+
+enum snet_verdict {
+	SNET_VERDICT_GRANT = 0,	/* grant the syscall */
+	SNET_VERDICT_DENY,	/* deny the syscall */
+	SNET_VERDICT_PENDING,	/* waiting for a decision */
+	SNET_VERDICT_NONE,	/* no decision can be set */
+	SNET_VERDICT_INVALID,
+};
+
+#define SNET_NR_VERDICT_TYPES SNET_VERDICT_INVALID
+
+enum snet_ticket_mode {
+	SNET_TICKET_OFF = 0,
+	SNET_TICKET_FIX,
+	SNET_TICKET_EXTEND,
+	SNET_TICKET_INVALID,
+};
+
+/* genetlink commands */
+enum {
+	SNET_C_UNSPEC,
+	SNET_C_VERSION,
+	SNET_C_REGISTER,
+	SNET_C_UNREGISTER,
+	SNET_C_INSERT,
+	SNET_C_REMOVE,
+	SNET_C_FLUSH,
+	SNET_C_LIST,
+	SNET_C_VERDICT,
+	SNET_C_CONFIG,
+	__SNET_C_MAX,
+};
+
+#define SNET_C_MAX (__SNET_C_MAX - 1)
+
+/* genetlink attributes */
+enum {
+	SNET_A_UNSPEC,
+	SNET_A_VERSION,		/* (NLA_U32) the snet protocol version	*/
+	SNET_A_VERDICT_ID,
+	SNET_A_FAMILY,
+	SNET_A_SYSCALL,		/* (NLA_U8)  a syscall identifier	*/
+	SNET_A_PROTOCOL,	/* (NLA_U8)  a protocol identifier	*/
+	SNET_A_UID,
+	SNET_A_PID,
+	SNET_A_TYPE,
+	SNET_A_IPV4SADDR,
+	SNET_A_IPV6SADDR,
+	SNET_A_IPV4DADDR,
+	SNET_A_IPV6DADDR,
+	SNET_A_SPORT,
+	SNET_A_DPORT,
+	SNET_A_VERDICT,
+	SNET_A_VERDICT_DELAY,
+	SNET_A_TICKET_DELAY,
+	SNET_A_TICKET_MODE,
+	__SNET_A_MAX,
+};
+
+#define SNET_A_MAX (__SNET_A_MAX - 1)
+
+#define SNET_GENL_NAME		"SNET"
+#define SNET_GENL_VERSION	SNET_VERSION
+
+struct snet_sock_half {
+	struct {
+		union {
+			__be32 ip;
+			struct in6_addr ip6;
+		};
+	} u3;
+	struct {
+		__be16 port;
+	} u;
+};
+
+struct snet_info {
+	u32 verdict_id;
+
+	enum snet_syscall syscall;
+	u8 protocol;
+	u8 family;
+
+	int type;
+	struct snet_sock_half src;
+	struct snet_sock_half dst;
+};
+
+#endif /* _LINUX_SNET_H */
diff --git a/security/snet/snet_core.c b/security/snet/snet_core.c
new file mode 100644
index 0000000..c8bc435
--- /dev/null
+++ b/security/snet/snet_core.c
@@ -0,0 +1,84 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include <linux/snet.h>
+#include "snet_hooks.h"
+#include "snet_event.h"
+#include "snet_verdict.h"
+#include "snet_ticket.h"
+#include "snet_utils.h"
+#include "snet_stats.h"
+
+unsigned int snet_evh_size = 16;
+module_param(snet_evh_size, uint, 0400);
+MODULE_PARM_DESC(snet_evh_size, "Set the size of the event hash table");
+
+unsigned int snet_vdh_size = 16;
+module_param(snet_vdh_size, uint, 0400);
+MODULE_PARM_DESC(snet_vdh_size, "Set the size of the verdict hash table");
+
+unsigned int snet_verdict_delay = 5;
+module_param(snet_verdict_delay, uint, 0600);
+MODULE_PARM_DESC(snet_verdict_delay, "Set the timeout for verdicts in secs");
+
+unsigned int snet_verdict_policy = SNET_VERDICT_GRANT;	/* permissive by default */
+module_param(snet_verdict_policy, uint, 0400);
+MODULE_PARM_DESC(snet_verdict_policy, "Set the default verdict");
+
+unsigned int snet_ticket_delay = 15;
+module_param(snet_ticket_delay, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_delay, "Set the timeout for tickets in secs");
+
+unsigned int snet_ticket_mode = SNET_TICKET_FIX;
+module_param(snet_ticket_mode, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_mode, "Set the mode for tickets");
+
+static __init int snet_init(void)
+{
+	int ret;
+
+	pr_debug("initializing: event_hash_size=%u "
+		 "verdict_hash_size=%u verdict_delay=%usecs "
+		 "default_policy=%s\n",
+		 snet_evh_size, snet_vdh_size, snet_verdict_delay,
+		 snet_verdict_name(snet_verdict_policy));
+
+	if (snet_verdict_policy >= SNET_VERDICT_INVALID) {
+		printk(KERN_ERR "snet: bad snet_verdict_policy\n");
+		ret = -EINVAL;
+		goto event_failed;
+	}
+
+	ret = snet_event_init();
+	if (ret < 0)
+		goto event_failed;
+
+	ret = snet_verdict_init();
+	if (ret < 0)
+		goto verdict_failed;
+
+	ret = snet_ticket_init();
+	if (ret < 0)
+		goto ticket_failed;
+
+	snet_stats_init();
+	/* snet_hooks_init() returns 0 or execute panic() */
+	snet_hooks_init();
+
+	pr_debug("started\n");
+	return 0;
+
+ticket_failed:
+	snet_verdict_exit();
+verdict_failed:
+	snet_event_exit();
+event_failed:
+	pr_debug("stopped\n");
+	return ret;
+}
+
+security_initcall(snet_init);
+
+MODULE_DESCRIPTION("snet - Security for NETwork syscalls");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samir Bellabes <sam@synack.fr>");
-- 
1.7.4.1


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

* [RFC v4 03/11] snet: introduce snet_core
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (9 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (32 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch introduce snet_core.c, which provides functions to start and stop
snet's subsystems, and include/linux/snet.h, which provides interface with
userspace.

subsytems are:
    - snet_hooks    : LSM hooks
    - snet_netlink  : kernel-user communication (genetlink)
    - snet_event    : manages the list of protected syscalls
    - snet_verdict  : provides a waitqueue for syscalls and manage verdicts
                      from userspace
    - snet_ticket   : provides a granted-access ticket mecanism

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 include/linux/snet.h      |  117 +++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_core.c |   84 ++++++++++++++++++++++++++++++++
 2 files changed, 201 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/snet.h
 create mode 100644 security/snet/snet_core.c

diff --git a/include/linux/snet.h b/include/linux/snet.h
new file mode 100644
index 0000000..580b6b6
--- /dev/null
+++ b/include/linux/snet.h
@@ -0,0 +1,117 @@
+#ifndef _LINUX_SNET_H
+#define _LINUX_SNET_H
+
+#include <linux/in6.h>
+
+#define SNET_VERSION	0x1
+#define SNET_NAME	"snet"
+
+enum snet_syscall {
+	SNET_SOCKET_CREATE = 0,
+	SNET_SOCKET_BIND,
+	SNET_SOCKET_CONNECT,
+	SNET_SOCKET_LISTEN,
+	SNET_SOCKET_ACCEPT,
+	SNET_SOCKET_POST_ACCEPT,
+	SNET_SOCKET_SENDMSG,
+	SNET_SOCKET_RECVMSG,
+	SNET_SOCKET_SOCK_RCV_SKB,
+	SNET_SOCKET_CLOSE,
+	SNET_SOCKET_INVALID,
+};
+
+#define SNET_NR_SOCKET_TYPES SNET_SOCKET_INVALID
+
+struct snet_event {
+	enum snet_syscall syscall;
+	u8 protocol;
+};
+
+enum snet_verdict {
+	SNET_VERDICT_GRANT = 0,	/* grant the syscall */
+	SNET_VERDICT_DENY,	/* deny the syscall */
+	SNET_VERDICT_PENDING,	/* waiting for a decision */
+	SNET_VERDICT_NONE,	/* no decision can be set */
+	SNET_VERDICT_INVALID,
+};
+
+#define SNET_NR_VERDICT_TYPES SNET_VERDICT_INVALID
+
+enum snet_ticket_mode {
+	SNET_TICKET_OFF = 0,
+	SNET_TICKET_FIX,
+	SNET_TICKET_EXTEND,
+	SNET_TICKET_INVALID,
+};
+
+/* genetlink commands */
+enum {
+	SNET_C_UNSPEC,
+	SNET_C_VERSION,
+	SNET_C_REGISTER,
+	SNET_C_UNREGISTER,
+	SNET_C_INSERT,
+	SNET_C_REMOVE,
+	SNET_C_FLUSH,
+	SNET_C_LIST,
+	SNET_C_VERDICT,
+	SNET_C_CONFIG,
+	__SNET_C_MAX,
+};
+
+#define SNET_C_MAX (__SNET_C_MAX - 1)
+
+/* genetlink attributes */
+enum {
+	SNET_A_UNSPEC,
+	SNET_A_VERSION,		/* (NLA_U32) the snet protocol version	*/
+	SNET_A_VERDICT_ID,
+	SNET_A_FAMILY,
+	SNET_A_SYSCALL,		/* (NLA_U8)  a syscall identifier	*/
+	SNET_A_PROTOCOL,	/* (NLA_U8)  a protocol identifier	*/
+	SNET_A_UID,
+	SNET_A_PID,
+	SNET_A_TYPE,
+	SNET_A_IPV4SADDR,
+	SNET_A_IPV6SADDR,
+	SNET_A_IPV4DADDR,
+	SNET_A_IPV6DADDR,
+	SNET_A_SPORT,
+	SNET_A_DPORT,
+	SNET_A_VERDICT,
+	SNET_A_VERDICT_DELAY,
+	SNET_A_TICKET_DELAY,
+	SNET_A_TICKET_MODE,
+	__SNET_A_MAX,
+};
+
+#define SNET_A_MAX (__SNET_A_MAX - 1)
+
+#define SNET_GENL_NAME		"SNET"
+#define SNET_GENL_VERSION	SNET_VERSION
+
+struct snet_sock_half {
+	struct {
+		union {
+			__be32 ip;
+			struct in6_addr ip6;
+		};
+	} u3;
+	struct {
+		__be16 port;
+	} u;
+};
+
+struct snet_info {
+	u32 verdict_id;
+
+	enum snet_syscall syscall;
+	u8 protocol;
+	u8 family;
+
+	int type;
+	struct snet_sock_half src;
+	struct snet_sock_half dst;
+};
+
+#endif /* _LINUX_SNET_H */
diff --git a/security/snet/snet_core.c b/security/snet/snet_core.c
new file mode 100644
index 0000000..c8bc435
--- /dev/null
+++ b/security/snet/snet_core.c
@@ -0,0 +1,84 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include <linux/snet.h>
+#include "snet_hooks.h"
+#include "snet_event.h"
+#include "snet_verdict.h"
+#include "snet_ticket.h"
+#include "snet_utils.h"
+#include "snet_stats.h"
+
+unsigned int snet_evh_size = 16;
+module_param(snet_evh_size, uint, 0400);
+MODULE_PARM_DESC(snet_evh_size, "Set the size of the event hash table");
+
+unsigned int snet_vdh_size = 16;
+module_param(snet_vdh_size, uint, 0400);
+MODULE_PARM_DESC(snet_vdh_size, "Set the size of the verdict hash table");
+
+unsigned int snet_verdict_delay = 5;
+module_param(snet_verdict_delay, uint, 0600);
+MODULE_PARM_DESC(snet_verdict_delay, "Set the timeout for verdicts in secs");
+
+unsigned int snet_verdict_policy = SNET_VERDICT_GRANT;	/* permissive by default */
+module_param(snet_verdict_policy, uint, 0400);
+MODULE_PARM_DESC(snet_verdict_policy, "Set the default verdict");
+
+unsigned int snet_ticket_delay = 15;
+module_param(snet_ticket_delay, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_delay, "Set the timeout for tickets in secs");
+
+unsigned int snet_ticket_mode = SNET_TICKET_FIX;
+module_param(snet_ticket_mode, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_mode, "Set the mode for tickets");
+
+static __init int snet_init(void)
+{
+	int ret;
+
+	pr_debug("initializing: event_hash_size=%u "
+		 "verdict_hash_size=%u verdict_delay=%usecs "
+		 "default_policy=%s\n",
+		 snet_evh_size, snet_vdh_size, snet_verdict_delay,
+		 snet_verdict_name(snet_verdict_policy));
+
+	if (snet_verdict_policy >= SNET_VERDICT_INVALID) {
+		printk(KERN_ERR "snet: bad snet_verdict_policy\n");
+		ret = -EINVAL;
+		goto event_failed;
+	}
+
+	ret = snet_event_init();
+	if (ret < 0)
+		goto event_failed;
+
+	ret = snet_verdict_init();
+	if (ret < 0)
+		goto verdict_failed;
+
+	ret = snet_ticket_init();
+	if (ret < 0)
+		goto ticket_failed;
+
+	snet_stats_init();
+	/* snet_hooks_init() returns 0 or execute panic() */
+	snet_hooks_init();
+
+	pr_debug("started\n");
+	return 0;
+
+ticket_failed:
+	snet_verdict_exit();
+verdict_failed:
+	snet_event_exit();
+event_failed:
+	pr_debug("stopped\n");
+	return ret;
+}
+
+security_initcall(snet_init);
+
+MODULE_DESCRIPTION("snet - Security for NETwork syscalls");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samir Bellabes <sam@synack.fr>");
-- 
1.7.4.1


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

* [RFC v4 04/11] snet: introduce snet_event
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (12 preceding siblings ...)
  2011-05-05 13:59 ` [RFC v4 04/11] snet: introduce snet_event y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (29 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet's subsystem responsive of managing events

snet is using the word 'event' for a couple of values [syscall, protocol].
For example, [listen, tcp] or [sendmsg, dccp] are events.

This patch introduces a hastable 'event_hash' and operations (add/remove/search..)
in order to manage which events have to be protected.
With the help of the communication's subsystem, managing orders are coming from
userspace.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_event.c |  201 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_event.h |   21 +++++
 2 files changed, 222 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_event.c
 create mode 100644 security/snet/snet_event.h

diff --git a/security/snet/snet_event.c b/security/snet/snet_event.c
new file mode 100644
index 0000000..7146d5a
--- /dev/null
+++ b/security/snet/snet_event.c
@@ -0,0 +1,201 @@
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/slab.h>
+#include <linux/netlink.h>
+#include <linux/snet.h>
+#include "snet_event.h"
+#include "snet_netlink.h"
+#include "snet_utils.h"
+
+static struct list_head *snet_evh;
+static DEFINE_RWLOCK(snet_evh_lock);
+
+struct snet_event_entry {
+	struct list_head list;
+	struct snet_event se;
+};
+
+static struct kmem_cache *snet_event_entry_cachep;
+
+/* lookup for a snet_evh - before using this function, lock snet_evh_lock */
+static struct snet_event_entry *__snet_event_lookup(const enum snet_syscall syscall,
+						    const u8 protocol)
+{
+	unsigned int h = 0;
+	struct list_head *l;
+	struct snet_event_entry *s;
+
+	/* computing its hash value */
+	h = jhash_2words(syscall, protocol, 0) % snet_evh_size;
+	l = &snet_evh[h];
+
+	list_for_each_entry(s, l, list) {
+		if ((s->se.protocol == protocol) &&
+		    (s->se.syscall == syscall)) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	unsigned int i = 0, n = 0;
+	int ret = -1;
+	unsigned hashs_to_skip = cb->args[0];
+	unsigned events_to_skip = cb->args[1];
+	struct list_head *l;
+	struct snet_event_entry *s;
+
+	read_lock_bh(&snet_evh_lock);
+
+	for (i = 0; i < snet_evh_size; i++) {
+		if (i < hashs_to_skip)
+			continue;
+		l = &snet_evh[i];
+		n = 0;
+		list_for_each_entry(s, l, list) {
+			if (++n < events_to_skip)
+				continue;
+			ret = snet_nl_list_fill_info(skb,
+						     NETLINK_CB(cb->skb).pid,
+						     cb->nlh->nlmsg_seq,
+						     NLM_F_MULTI,
+						     s->se.protocol,
+						     s->se.syscall);
+			if (ret < 0)
+				goto errout;
+		}
+	}
+
+errout:
+	read_unlock_bh(&snet_evh_lock);
+
+	cb->args[0] = i;
+	cb->args[1] = n;
+	return skb->len;
+}
+
+/*
+ * check if a event is registered or not
+ * return 1 if event is registered, 0 if not
+ */
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol)
+{
+	int ret = 0;
+
+	read_lock_bh(&snet_evh_lock);
+	if (__snet_event_lookup(syscall, protocol) != NULL)
+		ret = 1;
+	read_unlock_bh(&snet_evh_lock);
+	return ret;
+}
+
+/* adding a event */
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol)
+{
+	struct snet_event_entry *data = NULL;
+	unsigned int h = 0;
+	int err = 0;
+
+	data = kmem_cache_zalloc(snet_event_entry_cachep, GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	write_lock_bh(&snet_evh_lock);
+	/* check if event is already registered */
+	if (__snet_event_lookup(syscall, protocol) != NULL) {
+		write_unlock_bh(&snet_evh_lock);
+		kmem_cache_free(snet_event_entry_cachep, data);
+		err = -EINVAL;
+		goto out;
+	}
+
+	data->se.syscall = syscall;
+	data->se.protocol = protocol;
+	INIT_LIST_HEAD(&(data->list));
+	h = jhash_2words(data->se.syscall, data->se.protocol, 0) % snet_evh_size;
+	list_add_tail(&data->list, &snet_evh[h]);
+	write_unlock_bh(&snet_evh_lock);
+	pr_debug("[%u]=(syscall=%s, protocol=%u)\n",
+		 h, snet_syscall_name(syscall), protocol);
+out:
+	return err;
+}
+
+/* removing a event */
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol)
+{
+	struct snet_event_entry *data = NULL;
+
+	write_lock_bh(&snet_evh_lock);
+	data = __snet_event_lookup(syscall, protocol);
+	if (data == NULL) {
+		write_unlock_bh(&snet_evh_lock);
+		return -EINVAL;
+	}
+	pr_debug("(syscall=%s, protocol=%u)\n",
+		 snet_syscall_name(syscall), protocol);
+	list_del(&data->list);
+	write_unlock_bh(&snet_evh_lock);
+	kmem_cache_free(snet_event_entry_cachep, data);
+	return 0;
+}
+
+/* flushing all events */
+void snet_event_flush(void)
+{
+	unsigned int i = 0;
+
+	write_lock_bh(&snet_evh_lock);
+	for (i = 0; i < snet_evh_size; i++) {
+		struct snet_event_entry *data, *tmp;
+		list_for_each_entry_safe(data, tmp, &snet_evh[i], list) {
+			list_del(&data->list);
+			kmem_cache_free(snet_event_entry_cachep, data);
+		}
+	}
+	write_unlock_bh(&snet_evh_lock);
+	return;
+}
+
+/* init function */
+int snet_event_init(void)
+{
+	int err = 0, i = 0;
+
+	if (snet_evh_size == 0) {
+		printk(KERN_ERR "snet: bad snet_evh_size value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	snet_evh = kzalloc(sizeof(struct list_head) * snet_evh_size,
+			     GFP_KERNEL);
+	if (!snet_evh) {
+		printk(KERN_WARNING
+		       "snet: can't alloc memory for snet_evh\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < snet_evh_size; i++)
+		INIT_LIST_HEAD(&snet_evh[i]);
+
+	/* snet_event_entry_cachep is not destroyed */
+	snet_event_entry_cachep = kmem_cache_create("snet_event_entry",
+						    sizeof(struct snet_event_entry),
+						    0, SLAB_PANIC, NULL);
+out:
+	return err;
+}
+
+/* exit function */
+void snet_event_exit(void)
+{
+	kfree(snet_evh);
+	snet_evh = NULL;
+}
diff --git a/security/snet/snet_event.h b/security/snet/snet_event.h
new file mode 100644
index 0000000..fa991c7
--- /dev/null
+++ b/security/snet/snet_event.h
@@ -0,0 +1,21 @@
+#ifndef _SNET_EVENT_H
+#define _SNET_EVENT_H
+
+#include <linux/skbuff.h>
+
+extern unsigned int snet_evh_size;
+
+/* manipulate the events hash table */
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb);
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol);
+void snet_event_flush(void);
+void snet_event_dumpall(void);
+
+/* init function */
+int snet_event_init(void);
+/* exit funtion */
+void snet_event_exit(void);
+
+#endif /* _SNET_EVENT_H */
-- 
1.7.4.1


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

* [RFC v4 04/11] snet: introduce snet_event
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (14 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` [RFC v4 05/11] snet: introduce snet_hooks y
                   ` (27 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet's subsystem responsive of managing events

snet is using the word 'event' for a couple of values [syscall, protocol].
For example, [listen, tcp] or [sendmsg, dccp] are events.

This patch introduces a hastable 'event_hash' and operations (add/remove/search..)
in order to manage which events have to be protected.
With the help of the communication's subsystem, managing orders are coming from
userspace.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_event.c |  201 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_event.h |   21 +++++
 2 files changed, 222 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_event.c
 create mode 100644 security/snet/snet_event.h

diff --git a/security/snet/snet_event.c b/security/snet/snet_event.c
new file mode 100644
index 0000000..7146d5a
--- /dev/null
+++ b/security/snet/snet_event.c
@@ -0,0 +1,201 @@
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/slab.h>
+#include <linux/netlink.h>
+#include <linux/snet.h>
+#include "snet_event.h"
+#include "snet_netlink.h"
+#include "snet_utils.h"
+
+static struct list_head *snet_evh;
+static DEFINE_RWLOCK(snet_evh_lock);
+
+struct snet_event_entry {
+	struct list_head list;
+	struct snet_event se;
+};
+
+static struct kmem_cache *snet_event_entry_cachep;
+
+/* lookup for a snet_evh - before using this function, lock snet_evh_lock */
+static struct snet_event_entry *__snet_event_lookup(const enum snet_syscall syscall,
+						    const u8 protocol)
+{
+	unsigned int h = 0;
+	struct list_head *l;
+	struct snet_event_entry *s;
+
+	/* computing its hash value */
+	h = jhash_2words(syscall, protocol, 0) % snet_evh_size;
+	l = &snet_evh[h];
+
+	list_for_each_entry(s, l, list) {
+		if ((s->se.protocol == protocol) &&
+		    (s->se.syscall == syscall)) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	unsigned int i = 0, n = 0;
+	int ret = -1;
+	unsigned hashs_to_skip = cb->args[0];
+	unsigned events_to_skip = cb->args[1];
+	struct list_head *l;
+	struct snet_event_entry *s;
+
+	read_lock_bh(&snet_evh_lock);
+
+	for (i = 0; i < snet_evh_size; i++) {
+		if (i < hashs_to_skip)
+			continue;
+		l = &snet_evh[i];
+		n = 0;
+		list_for_each_entry(s, l, list) {
+			if (++n < events_to_skip)
+				continue;
+			ret = snet_nl_list_fill_info(skb,
+						     NETLINK_CB(cb->skb).pid,
+						     cb->nlh->nlmsg_seq,
+						     NLM_F_MULTI,
+						     s->se.protocol,
+						     s->se.syscall);
+			if (ret < 0)
+				goto errout;
+		}
+	}
+
+errout:
+	read_unlock_bh(&snet_evh_lock);
+
+	cb->args[0] = i;
+	cb->args[1] = n;
+	return skb->len;
+}
+
+/*
+ * check if a event is registered or not
+ * return 1 if event is registered, 0 if not
+ */
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol)
+{
+	int ret = 0;
+
+	read_lock_bh(&snet_evh_lock);
+	if (__snet_event_lookup(syscall, protocol) != NULL)
+		ret = 1;
+	read_unlock_bh(&snet_evh_lock);
+	return ret;
+}
+
+/* adding a event */
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol)
+{
+	struct snet_event_entry *data = NULL;
+	unsigned int h = 0;
+	int err = 0;
+
+	data = kmem_cache_zalloc(snet_event_entry_cachep, GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	write_lock_bh(&snet_evh_lock);
+	/* check if event is already registered */
+	if (__snet_event_lookup(syscall, protocol) != NULL) {
+		write_unlock_bh(&snet_evh_lock);
+		kmem_cache_free(snet_event_entry_cachep, data);
+		err = -EINVAL;
+		goto out;
+	}
+
+	data->se.syscall = syscall;
+	data->se.protocol = protocol;
+	INIT_LIST_HEAD(&(data->list));
+	h = jhash_2words(data->se.syscall, data->se.protocol, 0) % snet_evh_size;
+	list_add_tail(&data->list, &snet_evh[h]);
+	write_unlock_bh(&snet_evh_lock);
+	pr_debug("[%u]=(syscall=%s, protocol=%u)\n",
+		 h, snet_syscall_name(syscall), protocol);
+out:
+	return err;
+}
+
+/* removing a event */
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol)
+{
+	struct snet_event_entry *data = NULL;
+
+	write_lock_bh(&snet_evh_lock);
+	data = __snet_event_lookup(syscall, protocol);
+	if (data == NULL) {
+		write_unlock_bh(&snet_evh_lock);
+		return -EINVAL;
+	}
+	pr_debug("(syscall=%s, protocol=%u)\n",
+		 snet_syscall_name(syscall), protocol);
+	list_del(&data->list);
+	write_unlock_bh(&snet_evh_lock);
+	kmem_cache_free(snet_event_entry_cachep, data);
+	return 0;
+}
+
+/* flushing all events */
+void snet_event_flush(void)
+{
+	unsigned int i = 0;
+
+	write_lock_bh(&snet_evh_lock);
+	for (i = 0; i < snet_evh_size; i++) {
+		struct snet_event_entry *data, *tmp;
+		list_for_each_entry_safe(data, tmp, &snet_evh[i], list) {
+			list_del(&data->list);
+			kmem_cache_free(snet_event_entry_cachep, data);
+		}
+	}
+	write_unlock_bh(&snet_evh_lock);
+	return;
+}
+
+/* init function */
+int snet_event_init(void)
+{
+	int err = 0, i = 0;
+
+	if (snet_evh_size == 0) {
+		printk(KERN_ERR "snet: bad snet_evh_size value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	snet_evh = kzalloc(sizeof(struct list_head) * snet_evh_size,
+			     GFP_KERNEL);
+	if (!snet_evh) {
+		printk(KERN_WARNING
+		       "snet: can't alloc memory for snet_evh\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < snet_evh_size; i++)
+		INIT_LIST_HEAD(&snet_evh[i]);
+
+	/* snet_event_entry_cachep is not destroyed */
+	snet_event_entry_cachep = kmem_cache_create("snet_event_entry",
+						    sizeof(struct snet_event_entry),
+						    0, SLAB_PANIC, NULL);
+out:
+	return err;
+}
+
+/* exit function */
+void snet_event_exit(void)
+{
+	kfree(snet_evh);
+	snet_evh = NULL;
+}
diff --git a/security/snet/snet_event.h b/security/snet/snet_event.h
new file mode 100644
index 0000000..fa991c7
--- /dev/null
+++ b/security/snet/snet_event.h
@@ -0,0 +1,21 @@
+#ifndef _SNET_EVENT_H
+#define _SNET_EVENT_H
+
+#include <linux/skbuff.h>
+
+extern unsigned int snet_evh_size;
+
+/* manipulate the events hash table */
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb);
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol);
+void snet_event_flush(void);
+void snet_event_dumpall(void);
+
+/* init function */
+int snet_event_init(void);
+/* exit funtion */
+void snet_event_exit(void);
+
+#endif /* _SNET_EVENT_H */
-- 
1.7.4.1

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

* [RFC v4 04/11] snet: introduce snet_event
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (11 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (30 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet's subsystem responsive of managing events

snet is using the word 'event' for a couple of values [syscall, protocol].
For example, [listen, tcp] or [sendmsg, dccp] are events.

This patch introduces a hastable 'event_hash' and operations (add/remove/search..)
in order to manage which events have to be protected.
With the help of the communication's subsystem, managing orders are coming from
userspace.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_event.c |  201 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_event.h |   21 +++++
 2 files changed, 222 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_event.c
 create mode 100644 security/snet/snet_event.h

diff --git a/security/snet/snet_event.c b/security/snet/snet_event.c
new file mode 100644
index 0000000..7146d5a
--- /dev/null
+++ b/security/snet/snet_event.c
@@ -0,0 +1,201 @@
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/slab.h>
+#include <linux/netlink.h>
+#include <linux/snet.h>
+#include "snet_event.h"
+#include "snet_netlink.h"
+#include "snet_utils.h"
+
+static struct list_head *snet_evh;
+static DEFINE_RWLOCK(snet_evh_lock);
+
+struct snet_event_entry {
+	struct list_head list;
+	struct snet_event se;
+};
+
+static struct kmem_cache *snet_event_entry_cachep;
+
+/* lookup for a snet_evh - before using this function, lock snet_evh_lock */
+static struct snet_event_entry *__snet_event_lookup(const enum snet_syscall syscall,
+						    const u8 protocol)
+{
+	unsigned int h = 0;
+	struct list_head *l;
+	struct snet_event_entry *s;
+
+	/* computing its hash value */
+	h = jhash_2words(syscall, protocol, 0) % snet_evh_size;
+	l = &snet_evh[h];
+
+	list_for_each_entry(s, l, list) {
+		if ((s->se.protocol == protocol) &&
+		    (s->se.syscall == syscall)) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	unsigned int i = 0, n = 0;
+	int ret = -1;
+	unsigned hashs_to_skip = cb->args[0];
+	unsigned events_to_skip = cb->args[1];
+	struct list_head *l;
+	struct snet_event_entry *s;
+
+	read_lock_bh(&snet_evh_lock);
+
+	for (i = 0; i < snet_evh_size; i++) {
+		if (i < hashs_to_skip)
+			continue;
+		l = &snet_evh[i];
+		n = 0;
+		list_for_each_entry(s, l, list) {
+			if (++n < events_to_skip)
+				continue;
+			ret = snet_nl_list_fill_info(skb,
+						     NETLINK_CB(cb->skb).pid,
+						     cb->nlh->nlmsg_seq,
+						     NLM_F_MULTI,
+						     s->se.protocol,
+						     s->se.syscall);
+			if (ret < 0)
+				goto errout;
+		}
+	}
+
+errout:
+	read_unlock_bh(&snet_evh_lock);
+
+	cb->args[0] = i;
+	cb->args[1] = n;
+	return skb->len;
+}
+
+/*
+ * check if a event is registered or not
+ * return 1 if event is registered, 0 if not
+ */
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol)
+{
+	int ret = 0;
+
+	read_lock_bh(&snet_evh_lock);
+	if (__snet_event_lookup(syscall, protocol) != NULL)
+		ret = 1;
+	read_unlock_bh(&snet_evh_lock);
+	return ret;
+}
+
+/* adding a event */
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol)
+{
+	struct snet_event_entry *data = NULL;
+	unsigned int h = 0;
+	int err = 0;
+
+	data = kmem_cache_zalloc(snet_event_entry_cachep, GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	write_lock_bh(&snet_evh_lock);
+	/* check if event is already registered */
+	if (__snet_event_lookup(syscall, protocol) != NULL) {
+		write_unlock_bh(&snet_evh_lock);
+		kmem_cache_free(snet_event_entry_cachep, data);
+		err = -EINVAL;
+		goto out;
+	}
+
+	data->se.syscall = syscall;
+	data->se.protocol = protocol;
+	INIT_LIST_HEAD(&(data->list));
+	h = jhash_2words(data->se.syscall, data->se.protocol, 0) % snet_evh_size;
+	list_add_tail(&data->list, &snet_evh[h]);
+	write_unlock_bh(&snet_evh_lock);
+	pr_debug("[%u]=(syscall=%s, protocol=%u)\n",
+		 h, snet_syscall_name(syscall), protocol);
+out:
+	return err;
+}
+
+/* removing a event */
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol)
+{
+	struct snet_event_entry *data = NULL;
+
+	write_lock_bh(&snet_evh_lock);
+	data = __snet_event_lookup(syscall, protocol);
+	if (data == NULL) {
+		write_unlock_bh(&snet_evh_lock);
+		return -EINVAL;
+	}
+	pr_debug("(syscall=%s, protocol=%u)\n",
+		 snet_syscall_name(syscall), protocol);
+	list_del(&data->list);
+	write_unlock_bh(&snet_evh_lock);
+	kmem_cache_free(snet_event_entry_cachep, data);
+	return 0;
+}
+
+/* flushing all events */
+void snet_event_flush(void)
+{
+	unsigned int i = 0;
+
+	write_lock_bh(&snet_evh_lock);
+	for (i = 0; i < snet_evh_size; i++) {
+		struct snet_event_entry *data, *tmp;
+		list_for_each_entry_safe(data, tmp, &snet_evh[i], list) {
+			list_del(&data->list);
+			kmem_cache_free(snet_event_entry_cachep, data);
+		}
+	}
+	write_unlock_bh(&snet_evh_lock);
+	return;
+}
+
+/* init function */
+int snet_event_init(void)
+{
+	int err = 0, i = 0;
+
+	if (snet_evh_size == 0) {
+		printk(KERN_ERR "snet: bad snet_evh_size value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	snet_evh = kzalloc(sizeof(struct list_head) * snet_evh_size,
+			     GFP_KERNEL);
+	if (!snet_evh) {
+		printk(KERN_WARNING
+		       "snet: can't alloc memory for snet_evh\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < snet_evh_size; i++)
+		INIT_LIST_HEAD(&snet_evh[i]);
+
+	/* snet_event_entry_cachep is not destroyed */
+	snet_event_entry_cachep = kmem_cache_create("snet_event_entry",
+						    sizeof(struct snet_event_entry),
+						    0, SLAB_PANIC, NULL);
+out:
+	return err;
+}
+
+/* exit function */
+void snet_event_exit(void)
+{
+	kfree(snet_evh);
+	snet_evh = NULL;
+}
diff --git a/security/snet/snet_event.h b/security/snet/snet_event.h
new file mode 100644
index 0000000..fa991c7
--- /dev/null
+++ b/security/snet/snet_event.h
@@ -0,0 +1,21 @@
+#ifndef _SNET_EVENT_H
+#define _SNET_EVENT_H
+
+#include <linux/skbuff.h>
+
+extern unsigned int snet_evh_size;
+
+/* manipulate the events hash table */
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb);
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol);
+void snet_event_flush(void);
+void snet_event_dumpall(void);
+
+/* init function */
+int snet_event_init(void);
+/* exit funtion */
+void snet_event_exit(void);
+
+#endif /* _SNET_EVENT_H */
-- 
1.7.4.1


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

* [RFC v4 04/11] snet: introduce snet_event
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (13 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (28 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet's subsystem responsive of managing events

snet is using the word 'event' for a couple of values [syscall, protocol].
For example, [listen, tcp] or [sendmsg, dccp] are events.

This patch introduces a hastable 'event_hash' and operations (add/remove/search..)
in order to manage which events have to be protected.
With the help of the communication's subsystem, managing orders are coming from
userspace.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_event.c |  201 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_event.h |   21 +++++
 2 files changed, 222 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_event.c
 create mode 100644 security/snet/snet_event.h

diff --git a/security/snet/snet_event.c b/security/snet/snet_event.c
new file mode 100644
index 0000000..7146d5a
--- /dev/null
+++ b/security/snet/snet_event.c
@@ -0,0 +1,201 @@
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/slab.h>
+#include <linux/netlink.h>
+#include <linux/snet.h>
+#include "snet_event.h"
+#include "snet_netlink.h"
+#include "snet_utils.h"
+
+static struct list_head *snet_evh;
+static DEFINE_RWLOCK(snet_evh_lock);
+
+struct snet_event_entry {
+	struct list_head list;
+	struct snet_event se;
+};
+
+static struct kmem_cache *snet_event_entry_cachep;
+
+/* lookup for a snet_evh - before using this function, lock snet_evh_lock */
+static struct snet_event_entry *__snet_event_lookup(const enum snet_syscall syscall,
+						    const u8 protocol)
+{
+	unsigned int h = 0;
+	struct list_head *l;
+	struct snet_event_entry *s;
+
+	/* computing its hash value */
+	h = jhash_2words(syscall, protocol, 0) % snet_evh_size;
+	l = &snet_evh[h];
+
+	list_for_each_entry(s, l, list) {
+		if ((s->se.protocol == protocol) &&
+		    (s->se.syscall == syscall)) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	unsigned int i = 0, n = 0;
+	int ret = -1;
+	unsigned hashs_to_skip = cb->args[0];
+	unsigned events_to_skip = cb->args[1];
+	struct list_head *l;
+	struct snet_event_entry *s;
+
+	read_lock_bh(&snet_evh_lock);
+
+	for (i = 0; i < snet_evh_size; i++) {
+		if (i < hashs_to_skip)
+			continue;
+		l = &snet_evh[i];
+		n = 0;
+		list_for_each_entry(s, l, list) {
+			if (++n < events_to_skip)
+				continue;
+			ret = snet_nl_list_fill_info(skb,
+						     NETLINK_CB(cb->skb).pid,
+						     cb->nlh->nlmsg_seq,
+						     NLM_F_MULTI,
+						     s->se.protocol,
+						     s->se.syscall);
+			if (ret < 0)
+				goto errout;
+		}
+	}
+
+errout:
+	read_unlock_bh(&snet_evh_lock);
+
+	cb->args[0] = i;
+	cb->args[1] = n;
+	return skb->len;
+}
+
+/*
+ * check if a event is registered or not
+ * return 1 if event is registered, 0 if not
+ */
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol)
+{
+	int ret = 0;
+
+	read_lock_bh(&snet_evh_lock);
+	if (__snet_event_lookup(syscall, protocol) != NULL)
+		ret = 1;
+	read_unlock_bh(&snet_evh_lock);
+	return ret;
+}
+
+/* adding a event */
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol)
+{
+	struct snet_event_entry *data = NULL;
+	unsigned int h = 0;
+	int err = 0;
+
+	data = kmem_cache_zalloc(snet_event_entry_cachep, GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	write_lock_bh(&snet_evh_lock);
+	/* check if event is already registered */
+	if (__snet_event_lookup(syscall, protocol) != NULL) {
+		write_unlock_bh(&snet_evh_lock);
+		kmem_cache_free(snet_event_entry_cachep, data);
+		err = -EINVAL;
+		goto out;
+	}
+
+	data->se.syscall = syscall;
+	data->se.protocol = protocol;
+	INIT_LIST_HEAD(&(data->list));
+	h = jhash_2words(data->se.syscall, data->se.protocol, 0) % snet_evh_size;
+	list_add_tail(&data->list, &snet_evh[h]);
+	write_unlock_bh(&snet_evh_lock);
+	pr_debug("[%u]=(syscall=%s, protocol=%u)\n",
+		 h, snet_syscall_name(syscall), protocol);
+out:
+	return err;
+}
+
+/* removing a event */
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol)
+{
+	struct snet_event_entry *data = NULL;
+
+	write_lock_bh(&snet_evh_lock);
+	data = __snet_event_lookup(syscall, protocol);
+	if (data == NULL) {
+		write_unlock_bh(&snet_evh_lock);
+		return -EINVAL;
+	}
+	pr_debug("(syscall=%s, protocol=%u)\n",
+		 snet_syscall_name(syscall), protocol);
+	list_del(&data->list);
+	write_unlock_bh(&snet_evh_lock);
+	kmem_cache_free(snet_event_entry_cachep, data);
+	return 0;
+}
+
+/* flushing all events */
+void snet_event_flush(void)
+{
+	unsigned int i = 0;
+
+	write_lock_bh(&snet_evh_lock);
+	for (i = 0; i < snet_evh_size; i++) {
+		struct snet_event_entry *data, *tmp;
+		list_for_each_entry_safe(data, tmp, &snet_evh[i], list) {
+			list_del(&data->list);
+			kmem_cache_free(snet_event_entry_cachep, data);
+		}
+	}
+	write_unlock_bh(&snet_evh_lock);
+	return;
+}
+
+/* init function */
+int snet_event_init(void)
+{
+	int err = 0, i = 0;
+
+	if (snet_evh_size == 0) {
+		printk(KERN_ERR "snet: bad snet_evh_size value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	snet_evh = kzalloc(sizeof(struct list_head) * snet_evh_size,
+			     GFP_KERNEL);
+	if (!snet_evh) {
+		printk(KERN_WARNING
+		       "snet: can't alloc memory for snet_evh\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < snet_evh_size; i++)
+		INIT_LIST_HEAD(&snet_evh[i]);
+
+	/* snet_event_entry_cachep is not destroyed */
+	snet_event_entry_cachep = kmem_cache_create("snet_event_entry",
+						    sizeof(struct snet_event_entry),
+						    0, SLAB_PANIC, NULL);
+out:
+	return err;
+}
+
+/* exit function */
+void snet_event_exit(void)
+{
+	kfree(snet_evh);
+	snet_evh = NULL;
+}
diff --git a/security/snet/snet_event.h b/security/snet/snet_event.h
new file mode 100644
index 0000000..fa991c7
--- /dev/null
+++ b/security/snet/snet_event.h
@@ -0,0 +1,21 @@
+#ifndef _SNET_EVENT_H
+#define _SNET_EVENT_H
+
+#include <linux/skbuff.h>
+
+extern unsigned int snet_evh_size;
+
+/* manipulate the events hash table */
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb);
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol);
+void snet_event_flush(void);
+void snet_event_dumpall(void);
+
+/* init function */
+int snet_event_init(void);
+/* exit funtion */
+void snet_event_exit(void);
+
+#endif /* _SNET_EVENT_H */
-- 
1.7.4.1


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

* [RFC v4 05/11] snet: introduce snet_hooks
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (16 preceding siblings ...)
  2011-05-05 13:59 ` [RFC v4 05/11] snet: introduce snet_hooks y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (25 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet LSM's subsystem

snet_hooks provides the security hook's functions and the security_operations
structure. Currently hook functions are only related to network stack.

For each hook function, there is a generic mecanism:
 0. check if the event [syscall, protocol] is registered
 1. prepare informations for userspace
 2. send informations to userspace (snet_netlink)
 3. wait for verdict from userspace (snet_verdict)
 4. apply verdict for the syscall

steps 3 and 4 are only valid for LSM hooks which are returning a value (a way to
'filter' the syscall). For hooks returning 'void', steps 3 and 4 don't exist,
but snet sends security informations to userspace (step 2) to update the global
security policy.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_hooks.c |  764 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_hooks.h |   10 +
 2 files changed, 774 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_hooks.c
 create mode 100644 security/snet/snet_hooks.h

diff --git a/security/snet/snet_hooks.c b/security/snet/snet_hooks.c
new file mode 100644
index 0000000..1d2f9b1
--- /dev/null
+++ b/security/snet/snet_hooks.c
@@ -0,0 +1,764 @@
+/*
+ * snet_hook.c
+ *
+ * here are interesting informations which can be picked up from hooks.
+ *
+ *
+ * SOCKET_CREATE:
+ *	family, type, protocol
+ * SOCKET_BIND:
+ *	family, protocol, saddr, sport
+ * SOCKET_CONNECT:
+ *	family, protocol, saddr, sport, daddr, dport
+ * SOCKET_LISTEN:
+ *	family, protocol, saddr, sport
+ * SOCKET_ACCEPT:
+ *	family, protocol, saddr, sport
+ *
+ * SOCKET_SENDMSG:
+ * SOCKET_RECVMSG:
+ * SOCKET_SOCK_RCV_SKB:
+ *
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/security.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <net/inet_sock.h>
+#include <linux/ipv6.h>
+#include <linux/snet.h>
+#include <linux/uio.h>
+#include "snet_hooks.h"
+#include "snet_verdict.h"
+#include "snet_netlink.h"
+#include "snet_event.h"
+#include "snet_ticket.h"
+#include "snet_stats.h"
+
+static inline void  snet_pr_tuple(struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		pr_debug("%pI4:%u->%pI4:%u\n",
+			 &info->src.u3.ip, info->src.u.port,
+			 &info->dst.u3.ip, info->dst.u.port);
+		break;
+	case AF_INET6:
+		pr_debug("%pI6:%u->%pI6:%u\n",
+			 &info->src.u3.ip6, info->src.u.port,
+			 &info->dst.u3.ip6, info->dst.u.port);
+		break;
+	default:
+		break;
+	}
+	return;
+}
+
+static inline int snet_check_listeners(enum snet_verdict *verdict)
+{
+	if (snet_nl_pid == 0 || snet_nl_pid == current->pid ) {
+		if (verdict != NULL)
+			*verdict = SNET_VERDICT_GRANT;
+		return -1;
+	}
+	return 0;
+}
+
+static void snet_do_verdict(enum snet_verdict *verdict, struct snet_info *info)
+{
+	if (info->verdict_id == 0)
+		return;
+	/* sending networking informations to userspace */
+	if (snet_nl_send_event(info) == 0)
+		/* waiting for userspace reply or timeout */
+		*verdict = snet_verdict_wait(info->verdict_id);
+	/* removing verdict */
+	snet_verdict_remove(info->verdict_id);
+	return;
+}
+
+static int snet_do_send_event(struct snet_info *info)
+{
+	return snet_nl_send_event(info);
+}
+
+/*
+ * security operations helper functions
+ */
+
+/*
+ * security operations functions members
+ */
+
+static int snet_socket_create(int family, int type, int protocol, int kern)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+
+	/* if (kern) */
+	/*	;		/\* do something smart *\/ */
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CREATE);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	if (snet_event_is_registered(SNET_SOCKET_CREATE, protocol)) {
+		struct snet_info info;
+
+		/* inserting verdict PENDING */
+		info.verdict_id = snet_verdict_insert();
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_CREATE;
+		info.protocol = protocol;
+		info.family = family;
+		info.type = type;
+
+		pr_debug("family=%u type=%u protocol=%u kern=%u\n",
+			 family, type, protocol, kern);
+
+		snet_do_verdict(&verdict, &info);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_CREATE);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_CREATE);
+	}
+
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+
+out:
+	return verdict;
+}
+
+static int snet_socket_bind(struct socket *sock,
+			    struct sockaddr *address, int addrlen)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_BIND);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_BIND, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+		struct sockaddr_in *a = (struct sockaddr_in *) address;
+		struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_BIND;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = a->sin_addr.s_addr;
+			info.dst.u3.ip = inet->inet_daddr;
+			info.src.u.port = ntohs(a->sin_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info) ;
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&a6->sin6_addr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			info.src.u.port = ntohs(a6->sin6_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_BIND);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_BIND);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_connect(struct socket *sock,
+			       struct sockaddr *address, int addrlen)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CONNECT);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_CONNECT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+		struct sockaddr_in *a = (struct sockaddr_in *) address;
+		struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+		/* prepare networking informations for userspace */
+		memset(&info, 0, sizeof(struct  snet_info));
+		info.syscall = SNET_SOCKET_CONNECT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = a->sin_addr.s_addr;
+			info.dst.u.port = ntohs(a->sin_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&a6->sin6_addr,
+			       sizeof(info.dst.u3.ip6));
+			info.dst.u.port = ntohs(a6->sin6_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_CONNECT);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_CONNECT);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_listen(struct socket *sock, int backlog)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_LISTEN);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_LISTEN, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		memset(&info, 0, sizeof(struct  snet_info));
+		info.syscall = SNET_SOCKET_LISTEN;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_LISTEN);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_LISTEN);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_accept(struct socket *sock, struct socket *newsock)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_ACCEPT);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_ACCEPT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_ACCEPT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_ACCEPT);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_ACCEPT);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static void snet_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_POST_ACCEPT);
+
+	if (snet_check_listeners(NULL)  < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_POST_ACCEPT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(newsock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_POST_ACCEPT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.verdict_id = 0;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			goto out;
+			break;
+		}
+		snet_pr_tuple(&info);
+		if (snet_do_send_event(&info) < 0)
+			SNET_STATS_INC(SNET_STATS_REG_ERROR,
+				       SNET_SOCKET_POST_ACCEPT);
+		else
+			SNET_STATS_INC(SNET_STATS_REG_GRANT,
+				       SNET_SOCKET_POST_ACCEPT);
+	}
+out:
+	return;
+}
+
+static int snet_socket_sendmsg(struct socket *sock,
+			       struct msghdr *msg, int size)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_SENDMSG);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_SENDMSG, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_SENDMSG;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_SENDMSG);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_SENDMSG);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_recvmsg(struct socket *sock,
+			       struct msghdr *msg, int size, int flags)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_RECVMSG);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_RECVMSG, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_RECVMSG;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_RECVMSG);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_RECVMSG);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_SOCK_RCV_SKB);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_SOCK_RCV_SKB, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_SOCK_RCV_SKB;
+		info.protocol = protocol;
+		info.family = sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sk->sk_family) {
+		case PF_INET:
+			/* inserting verdict PENDING  */
+			/* info.verdict_id = snet_verdict_insert(); */
+
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			/* inserting verdict PENDING  */
+			/* info.verdict_id = snet_verdict_insert(); */
+
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		/* SNET_DOC_VERDICT(info); */
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_SOCK_RCV_SKB);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static void snet_socket_close(struct socket *sock)
+{
+	u8 protocol = 0;
+
+	if (sock == NULL || sock->sk == NULL) {
+		goto out;
+	}
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CLOSE);
+
+	if (snet_check_listeners(NULL)  < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_CLOSE, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_CLOSE;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.verdict_id = 0;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			goto out;
+			break;
+		}
+		snet_pr_tuple(&info);
+		if (snet_do_send_event(&info) < 0)
+			SNET_STATS_INC(SNET_STATS_REG_ERROR, SNET_SOCKET_CLOSE);
+		else
+			SNET_STATS_INC(SNET_STATS_REG_GRANT, SNET_SOCKET_CLOSE);
+	}
+out:
+	return;
+}
+
+static struct security_operations snet_security_ops = {
+	.name			= "snet",
+
+	.socket_create		= snet_socket_create,
+	.socket_bind		= snet_socket_bind,
+	.socket_connect		= snet_socket_connect,
+	.socket_listen		= snet_socket_listen,
+	.socket_accept		= snet_socket_accept,
+	.socket_post_accept	= snet_socket_post_accept,
+	.socket_sendmsg		= snet_socket_sendmsg,
+	.socket_recvmsg		= snet_socket_recvmsg,
+	.socket_sock_rcv_skb	= snet_socket_sock_rcv_skb,
+	.socket_close		= snet_socket_close,
+
+	.cred_prepare		= snet_prepare_creds,
+	.cred_free		= snet_cred_free,
+};
+
+int snet_hooks_init(void)
+{
+	if (!security_module_enable(&snet_security_ops))
+		return 0;
+
+	if (register_security(&snet_security_ops))
+		panic("snet: failed to register security_ops\n");
+
+	return 0;
+}
diff --git a/security/snet/snet_hooks.h b/security/snet/snet_hooks.h
new file mode 100644
index 0000000..05fe5e8
--- /dev/null
+++ b/security/snet/snet_hooks.h
@@ -0,0 +1,10 @@
+#ifndef _SNET_HOOKS_H
+#define _SNET_HOOKS_H
+
+extern uint32_t snet_nl_pid;
+extern unsigned int snet_verdict_policy;
+
+/* init function */
+int snet_hooks_init(void);
+
+#endif /* _SNET_HOOK_H */
-- 
1.7.4.1


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

* [RFC v4 05/11] snet: introduce snet_hooks
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (18 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` [RFC v4 06/11] snet: introduce snet_netlink y
                   ` (23 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet LSM's subsystem

snet_hooks provides the security hook's functions and the security_operations
structure. Currently hook functions are only related to network stack.

For each hook function, there is a generic mecanism:
 0. check if the event [syscall, protocol] is registered
 1. prepare informations for userspace
 2. send informations to userspace (snet_netlink)
 3. wait for verdict from userspace (snet_verdict)
 4. apply verdict for the syscall

steps 3 and 4 are only valid for LSM hooks which are returning a value (a way to
'filter' the syscall). For hooks returning 'void', steps 3 and 4 don't exist,
but snet sends security informations to userspace (step 2) to update the global
security policy.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_hooks.c |  764 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_hooks.h |   10 +
 2 files changed, 774 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_hooks.c
 create mode 100644 security/snet/snet_hooks.h

diff --git a/security/snet/snet_hooks.c b/security/snet/snet_hooks.c
new file mode 100644
index 0000000..1d2f9b1
--- /dev/null
+++ b/security/snet/snet_hooks.c
@@ -0,0 +1,764 @@
+/*
+ * snet_hook.c
+ *
+ * here are interesting informations which can be picked up from hooks.
+ *
+ *
+ * SOCKET_CREATE:
+ *	family, type, protocol
+ * SOCKET_BIND:
+ *	family, protocol, saddr, sport
+ * SOCKET_CONNECT:
+ *	family, protocol, saddr, sport, daddr, dport
+ * SOCKET_LISTEN:
+ *	family, protocol, saddr, sport
+ * SOCKET_ACCEPT:
+ *	family, protocol, saddr, sport
+ *
+ * SOCKET_SENDMSG:
+ * SOCKET_RECVMSG:
+ * SOCKET_SOCK_RCV_SKB:
+ *
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/security.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <net/inet_sock.h>
+#include <linux/ipv6.h>
+#include <linux/snet.h>
+#include <linux/uio.h>
+#include "snet_hooks.h"
+#include "snet_verdict.h"
+#include "snet_netlink.h"
+#include "snet_event.h"
+#include "snet_ticket.h"
+#include "snet_stats.h"
+
+static inline void  snet_pr_tuple(struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		pr_debug("%pI4:%u->%pI4:%u\n",
+			 &info->src.u3.ip, info->src.u.port,
+			 &info->dst.u3.ip, info->dst.u.port);
+		break;
+	case AF_INET6:
+		pr_debug("%pI6:%u->%pI6:%u\n",
+			 &info->src.u3.ip6, info->src.u.port,
+			 &info->dst.u3.ip6, info->dst.u.port);
+		break;
+	default:
+		break;
+	}
+	return;
+}
+
+static inline int snet_check_listeners(enum snet_verdict *verdict)
+{
+	if (snet_nl_pid == 0 || snet_nl_pid == current->pid ) {
+		if (verdict != NULL)
+			*verdict = SNET_VERDICT_GRANT;
+		return -1;
+	}
+	return 0;
+}
+
+static void snet_do_verdict(enum snet_verdict *verdict, struct snet_info *info)
+{
+	if (info->verdict_id == 0)
+		return;
+	/* sending networking informations to userspace */
+	if (snet_nl_send_event(info) == 0)
+		/* waiting for userspace reply or timeout */
+		*verdict = snet_verdict_wait(info->verdict_id);
+	/* removing verdict */
+	snet_verdict_remove(info->verdict_id);
+	return;
+}
+
+static int snet_do_send_event(struct snet_info *info)
+{
+	return snet_nl_send_event(info);
+}
+
+/*
+ * security operations helper functions
+ */
+
+/*
+ * security operations functions members
+ */
+
+static int snet_socket_create(int family, int type, int protocol, int kern)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+
+	/* if (kern) */
+	/*	;		/\* do something smart *\/ */
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CREATE);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	if (snet_event_is_registered(SNET_SOCKET_CREATE, protocol)) {
+		struct snet_info info;
+
+		/* inserting verdict PENDING */
+		info.verdict_id = snet_verdict_insert();
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_CREATE;
+		info.protocol = protocol;
+		info.family = family;
+		info.type = type;
+
+		pr_debug("family=%u type=%u protocol=%u kern=%u\n",
+			 family, type, protocol, kern);
+
+		snet_do_verdict(&verdict, &info);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_CREATE);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_CREATE);
+	}
+
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+
+out:
+	return verdict;
+}
+
+static int snet_socket_bind(struct socket *sock,
+			    struct sockaddr *address, int addrlen)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_BIND);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_BIND, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+		struct sockaddr_in *a = (struct sockaddr_in *) address;
+		struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_BIND;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = a->sin_addr.s_addr;
+			info.dst.u3.ip = inet->inet_daddr;
+			info.src.u.port = ntohs(a->sin_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info) ;
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&a6->sin6_addr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			info.src.u.port = ntohs(a6->sin6_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_BIND);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_BIND);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_connect(struct socket *sock,
+			       struct sockaddr *address, int addrlen)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CONNECT);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_CONNECT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+		struct sockaddr_in *a = (struct sockaddr_in *) address;
+		struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+		/* prepare networking informations for userspace */
+		memset(&info, 0, sizeof(struct  snet_info));
+		info.syscall = SNET_SOCKET_CONNECT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = a->sin_addr.s_addr;
+			info.dst.u.port = ntohs(a->sin_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&a6->sin6_addr,
+			       sizeof(info.dst.u3.ip6));
+			info.dst.u.port = ntohs(a6->sin6_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_CONNECT);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_CONNECT);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_listen(struct socket *sock, int backlog)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_LISTEN);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_LISTEN, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		memset(&info, 0, sizeof(struct  snet_info));
+		info.syscall = SNET_SOCKET_LISTEN;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_LISTEN);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_LISTEN);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_accept(struct socket *sock, struct socket *newsock)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_ACCEPT);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_ACCEPT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_ACCEPT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_ACCEPT);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_ACCEPT);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static void snet_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_POST_ACCEPT);
+
+	if (snet_check_listeners(NULL)  < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_POST_ACCEPT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(newsock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_POST_ACCEPT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.verdict_id = 0;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			goto out;
+			break;
+		}
+		snet_pr_tuple(&info);
+		if (snet_do_send_event(&info) < 0)
+			SNET_STATS_INC(SNET_STATS_REG_ERROR,
+				       SNET_SOCKET_POST_ACCEPT);
+		else
+			SNET_STATS_INC(SNET_STATS_REG_GRANT,
+				       SNET_SOCKET_POST_ACCEPT);
+	}
+out:
+	return;
+}
+
+static int snet_socket_sendmsg(struct socket *sock,
+			       struct msghdr *msg, int size)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_SENDMSG);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_SENDMSG, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_SENDMSG;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_SENDMSG);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_SENDMSG);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_recvmsg(struct socket *sock,
+			       struct msghdr *msg, int size, int flags)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_RECVMSG);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_RECVMSG, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_RECVMSG;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_RECVMSG);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_RECVMSG);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_SOCK_RCV_SKB);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_SOCK_RCV_SKB, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_SOCK_RCV_SKB;
+		info.protocol = protocol;
+		info.family = sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sk->sk_family) {
+		case PF_INET:
+			/* inserting verdict PENDING  */
+			/* info.verdict_id = snet_verdict_insert(); */
+
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			/* inserting verdict PENDING  */
+			/* info.verdict_id = snet_verdict_insert(); */
+
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		/* SNET_DOC_VERDICT(info); */
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_SOCK_RCV_SKB);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static void snet_socket_close(struct socket *sock)
+{
+	u8 protocol = 0;
+
+	if (sock == NULL || sock->sk == NULL) {
+		goto out;
+	}
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CLOSE);
+
+	if (snet_check_listeners(NULL)  < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_CLOSE, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_CLOSE;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.verdict_id = 0;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			goto out;
+			break;
+		}
+		snet_pr_tuple(&info);
+		if (snet_do_send_event(&info) < 0)
+			SNET_STATS_INC(SNET_STATS_REG_ERROR, SNET_SOCKET_CLOSE);
+		else
+			SNET_STATS_INC(SNET_STATS_REG_GRANT, SNET_SOCKET_CLOSE);
+	}
+out:
+	return;
+}
+
+static struct security_operations snet_security_ops = {
+	.name			= "snet",
+
+	.socket_create		= snet_socket_create,
+	.socket_bind		= snet_socket_bind,
+	.socket_connect		= snet_socket_connect,
+	.socket_listen		= snet_socket_listen,
+	.socket_accept		= snet_socket_accept,
+	.socket_post_accept	= snet_socket_post_accept,
+	.socket_sendmsg		= snet_socket_sendmsg,
+	.socket_recvmsg		= snet_socket_recvmsg,
+	.socket_sock_rcv_skb	= snet_socket_sock_rcv_skb,
+	.socket_close		= snet_socket_close,
+
+	.cred_prepare		= snet_prepare_creds,
+	.cred_free		= snet_cred_free,
+};
+
+int snet_hooks_init(void)
+{
+	if (!security_module_enable(&snet_security_ops))
+		return 0;
+
+	if (register_security(&snet_security_ops))
+		panic("snet: failed to register security_ops\n");
+
+	return 0;
+}
diff --git a/security/snet/snet_hooks.h b/security/snet/snet_hooks.h
new file mode 100644
index 0000000..05fe5e8
--- /dev/null
+++ b/security/snet/snet_hooks.h
@@ -0,0 +1,10 @@
+#ifndef _SNET_HOOKS_H
+#define _SNET_HOOKS_H
+
+extern uint32_t snet_nl_pid;
+extern unsigned int snet_verdict_policy;
+
+/* init function */
+int snet_hooks_init(void);
+
+#endif /* _SNET_HOOK_H */
-- 
1.7.4.1

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

* [RFC v4 05/11] snet: introduce snet_hooks
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (15 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (26 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet LSM's subsystem

snet_hooks provides the security hook's functions and the security_operations
structure. Currently hook functions are only related to network stack.

For each hook function, there is a generic mecanism:
 0. check if the event [syscall, protocol] is registered
 1. prepare informations for userspace
 2. send informations to userspace (snet_netlink)
 3. wait for verdict from userspace (snet_verdict)
 4. apply verdict for the syscall

steps 3 and 4 are only valid for LSM hooks which are returning a value (a way to
'filter' the syscall). For hooks returning 'void', steps 3 and 4 don't exist,
but snet sends security informations to userspace (step 2) to update the global
security policy.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_hooks.c |  764 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_hooks.h |   10 +
 2 files changed, 774 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_hooks.c
 create mode 100644 security/snet/snet_hooks.h

diff --git a/security/snet/snet_hooks.c b/security/snet/snet_hooks.c
new file mode 100644
index 0000000..1d2f9b1
--- /dev/null
+++ b/security/snet/snet_hooks.c
@@ -0,0 +1,764 @@
+/*
+ * snet_hook.c
+ *
+ * here are interesting informations which can be picked up from hooks.
+ *
+ *
+ * SOCKET_CREATE:
+ *	family, type, protocol
+ * SOCKET_BIND:
+ *	family, protocol, saddr, sport
+ * SOCKET_CONNECT:
+ *	family, protocol, saddr, sport, daddr, dport
+ * SOCKET_LISTEN:
+ *	family, protocol, saddr, sport
+ * SOCKET_ACCEPT:
+ *	family, protocol, saddr, sport
+ *
+ * SOCKET_SENDMSG:
+ * SOCKET_RECVMSG:
+ * SOCKET_SOCK_RCV_SKB:
+ *
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/security.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <net/inet_sock.h>
+#include <linux/ipv6.h>
+#include <linux/snet.h>
+#include <linux/uio.h>
+#include "snet_hooks.h"
+#include "snet_verdict.h"
+#include "snet_netlink.h"
+#include "snet_event.h"
+#include "snet_ticket.h"
+#include "snet_stats.h"
+
+static inline void  snet_pr_tuple(struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		pr_debug("%pI4:%u->%pI4:%u\n",
+			 &info->src.u3.ip, info->src.u.port,
+			 &info->dst.u3.ip, info->dst.u.port);
+		break;
+	case AF_INET6:
+		pr_debug("%pI6:%u->%pI6:%u\n",
+			 &info->src.u3.ip6, info->src.u.port,
+			 &info->dst.u3.ip6, info->dst.u.port);
+		break;
+	default:
+		break;
+	}
+	return;
+}
+
+static inline int snet_check_listeners(enum snet_verdict *verdict)
+{
+	if (snet_nl_pid == 0 || snet_nl_pid == current->pid ) {
+		if (verdict != NULL)
+			*verdict = SNET_VERDICT_GRANT;
+		return -1;
+	}
+	return 0;
+}
+
+static void snet_do_verdict(enum snet_verdict *verdict, struct snet_info *info)
+{
+	if (info->verdict_id == 0)
+		return;
+	/* sending networking informations to userspace */
+	if (snet_nl_send_event(info) == 0)
+		/* waiting for userspace reply or timeout */
+		*verdict = snet_verdict_wait(info->verdict_id);
+	/* removing verdict */
+	snet_verdict_remove(info->verdict_id);
+	return;
+}
+
+static int snet_do_send_event(struct snet_info *info)
+{
+	return snet_nl_send_event(info);
+}
+
+/*
+ * security operations helper functions
+ */
+
+/*
+ * security operations functions members
+ */
+
+static int snet_socket_create(int family, int type, int protocol, int kern)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+
+	/* if (kern) */
+	/*	;		/\* do something smart *\/ */
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CREATE);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	if (snet_event_is_registered(SNET_SOCKET_CREATE, protocol)) {
+		struct snet_info info;
+
+		/* inserting verdict PENDING */
+		info.verdict_id = snet_verdict_insert();
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_CREATE;
+		info.protocol = protocol;
+		info.family = family;
+		info.type = type;
+
+		pr_debug("family=%u type=%u protocol=%u kern=%u\n",
+			 family, type, protocol, kern);
+
+		snet_do_verdict(&verdict, &info);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_CREATE);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_CREATE);
+	}
+
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+
+out:
+	return verdict;
+}
+
+static int snet_socket_bind(struct socket *sock,
+			    struct sockaddr *address, int addrlen)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_BIND);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_BIND, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+		struct sockaddr_in *a = (struct sockaddr_in *) address;
+		struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_BIND;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = a->sin_addr.s_addr;
+			info.dst.u3.ip = inet->inet_daddr;
+			info.src.u.port = ntohs(a->sin_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info) ;
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&a6->sin6_addr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			info.src.u.port = ntohs(a6->sin6_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_BIND);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_BIND);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_connect(struct socket *sock,
+			       struct sockaddr *address, int addrlen)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CONNECT);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_CONNECT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+		struct sockaddr_in *a = (struct sockaddr_in *) address;
+		struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+		/* prepare networking informations for userspace */
+		memset(&info, 0, sizeof(struct  snet_info));
+		info.syscall = SNET_SOCKET_CONNECT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = a->sin_addr.s_addr;
+			info.dst.u.port = ntohs(a->sin_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&a6->sin6_addr,
+			       sizeof(info.dst.u3.ip6));
+			info.dst.u.port = ntohs(a6->sin6_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_CONNECT);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_CONNECT);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_listen(struct socket *sock, int backlog)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_LISTEN);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_LISTEN, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		memset(&info, 0, sizeof(struct  snet_info));
+		info.syscall = SNET_SOCKET_LISTEN;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_LISTEN);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_LISTEN);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_accept(struct socket *sock, struct socket *newsock)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_ACCEPT);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_ACCEPT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_ACCEPT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_ACCEPT);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_ACCEPT);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static void snet_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_POST_ACCEPT);
+
+	if (snet_check_listeners(NULL)  < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_POST_ACCEPT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(newsock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_POST_ACCEPT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.verdict_id = 0;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			goto out;
+			break;
+		}
+		snet_pr_tuple(&info);
+		if (snet_do_send_event(&info) < 0)
+			SNET_STATS_INC(SNET_STATS_REG_ERROR,
+				       SNET_SOCKET_POST_ACCEPT);
+		else
+			SNET_STATS_INC(SNET_STATS_REG_GRANT,
+				       SNET_SOCKET_POST_ACCEPT);
+	}
+out:
+	return;
+}
+
+static int snet_socket_sendmsg(struct socket *sock,
+			       struct msghdr *msg, int size)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_SENDMSG);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_SENDMSG, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_SENDMSG;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_SENDMSG);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_SENDMSG);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_recvmsg(struct socket *sock,
+			       struct msghdr *msg, int size, int flags)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_RECVMSG);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_RECVMSG, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_RECVMSG;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_RECVMSG);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_RECVMSG);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_SOCK_RCV_SKB);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_SOCK_RCV_SKB, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_SOCK_RCV_SKB;
+		info.protocol = protocol;
+		info.family = sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sk->sk_family) {
+		case PF_INET:
+			/* inserting verdict PENDING  */
+			/* info.verdict_id = snet_verdict_insert(); */
+
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			/* inserting verdict PENDING  */
+			/* info.verdict_id = snet_verdict_insert(); */
+
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		/* SNET_DOC_VERDICT(info); */
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_SOCK_RCV_SKB);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static void snet_socket_close(struct socket *sock)
+{
+	u8 protocol = 0;
+
+	if (sock == NULL || sock->sk == NULL) {
+		goto out;
+	}
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CLOSE);
+
+	if (snet_check_listeners(NULL)  < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_CLOSE, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_CLOSE;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.verdict_id = 0;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			goto out;
+			break;
+		}
+		snet_pr_tuple(&info);
+		if (snet_do_send_event(&info) < 0)
+			SNET_STATS_INC(SNET_STATS_REG_ERROR, SNET_SOCKET_CLOSE);
+		else
+			SNET_STATS_INC(SNET_STATS_REG_GRANT, SNET_SOCKET_CLOSE);
+	}
+out:
+	return;
+}
+
+static struct security_operations snet_security_ops = {
+	.name			= "snet",
+
+	.socket_create		= snet_socket_create,
+	.socket_bind		= snet_socket_bind,
+	.socket_connect		= snet_socket_connect,
+	.socket_listen		= snet_socket_listen,
+	.socket_accept		= snet_socket_accept,
+	.socket_post_accept	= snet_socket_post_accept,
+	.socket_sendmsg		= snet_socket_sendmsg,
+	.socket_recvmsg		= snet_socket_recvmsg,
+	.socket_sock_rcv_skb	= snet_socket_sock_rcv_skb,
+	.socket_close		= snet_socket_close,
+
+	.cred_prepare		= snet_prepare_creds,
+	.cred_free		= snet_cred_free,
+};
+
+int snet_hooks_init(void)
+{
+	if (!security_module_enable(&snet_security_ops))
+		return 0;
+
+	if (register_security(&snet_security_ops))
+		panic("snet: failed to register security_ops\n");
+
+	return 0;
+}
diff --git a/security/snet/snet_hooks.h b/security/snet/snet_hooks.h
new file mode 100644
index 0000000..05fe5e8
--- /dev/null
+++ b/security/snet/snet_hooks.h
@@ -0,0 +1,10 @@
+#ifndef _SNET_HOOKS_H
+#define _SNET_HOOKS_H
+
+extern uint32_t snet_nl_pid;
+extern unsigned int snet_verdict_policy;
+
+/* init function */
+int snet_hooks_init(void);
+
+#endif /* _SNET_HOOK_H */
-- 
1.7.4.1


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

* [RFC v4 05/11] snet: introduce snet_hooks
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (17 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (24 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet LSM's subsystem

snet_hooks provides the security hook's functions and the security_operations
structure. Currently hook functions are only related to network stack.

For each hook function, there is a generic mecanism:
 0. check if the event [syscall, protocol] is registered
 1. prepare informations for userspace
 2. send informations to userspace (snet_netlink)
 3. wait for verdict from userspace (snet_verdict)
 4. apply verdict for the syscall

steps 3 and 4 are only valid for LSM hooks which are returning a value (a way to
'filter' the syscall). For hooks returning 'void', steps 3 and 4 don't exist,
but snet sends security informations to userspace (step 2) to update the global
security policy.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_hooks.c |  764 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_hooks.h |   10 +
 2 files changed, 774 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_hooks.c
 create mode 100644 security/snet/snet_hooks.h

diff --git a/security/snet/snet_hooks.c b/security/snet/snet_hooks.c
new file mode 100644
index 0000000..1d2f9b1
--- /dev/null
+++ b/security/snet/snet_hooks.c
@@ -0,0 +1,764 @@
+/*
+ * snet_hook.c
+ *
+ * here are interesting informations which can be picked up from hooks.
+ *
+ *
+ * SOCKET_CREATE:
+ *	family, type, protocol
+ * SOCKET_BIND:
+ *	family, protocol, saddr, sport
+ * SOCKET_CONNECT:
+ *	family, protocol, saddr, sport, daddr, dport
+ * SOCKET_LISTEN:
+ *	family, protocol, saddr, sport
+ * SOCKET_ACCEPT:
+ *	family, protocol, saddr, sport
+ *
+ * SOCKET_SENDMSG:
+ * SOCKET_RECVMSG:
+ * SOCKET_SOCK_RCV_SKB:
+ *
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/security.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <net/inet_sock.h>
+#include <linux/ipv6.h>
+#include <linux/snet.h>
+#include <linux/uio.h>
+#include "snet_hooks.h"
+#include "snet_verdict.h"
+#include "snet_netlink.h"
+#include "snet_event.h"
+#include "snet_ticket.h"
+#include "snet_stats.h"
+
+static inline void  snet_pr_tuple(struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		pr_debug("%pI4:%u->%pI4:%u\n",
+			 &info->src.u3.ip, info->src.u.port,
+			 &info->dst.u3.ip, info->dst.u.port);
+		break;
+	case AF_INET6:
+		pr_debug("%pI6:%u->%pI6:%u\n",
+			 &info->src.u3.ip6, info->src.u.port,
+			 &info->dst.u3.ip6, info->dst.u.port);
+		break;
+	default:
+		break;
+	}
+	return;
+}
+
+static inline int snet_check_listeners(enum snet_verdict *verdict)
+{
+	if (snet_nl_pid == 0 || snet_nl_pid == current->pid ) {
+		if (verdict != NULL)
+			*verdict = SNET_VERDICT_GRANT;
+		return -1;
+	}
+	return 0;
+}
+
+static void snet_do_verdict(enum snet_verdict *verdict, struct snet_info *info)
+{
+	if (info->verdict_id == 0)
+		return;
+	/* sending networking informations to userspace */
+	if (snet_nl_send_event(info) == 0)
+		/* waiting for userspace reply or timeout */
+		*verdict = snet_verdict_wait(info->verdict_id);
+	/* removing verdict */
+	snet_verdict_remove(info->verdict_id);
+	return;
+}
+
+static int snet_do_send_event(struct snet_info *info)
+{
+	return snet_nl_send_event(info);
+}
+
+/*
+ * security operations helper functions
+ */
+
+/*
+ * security operations functions members
+ */
+
+static int snet_socket_create(int family, int type, int protocol, int kern)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+
+	/* if (kern) */
+	/*	;		/\* do something smart *\/ */
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CREATE);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	if (snet_event_is_registered(SNET_SOCKET_CREATE, protocol)) {
+		struct snet_info info;
+
+		/* inserting verdict PENDING */
+		info.verdict_id = snet_verdict_insert();
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_CREATE;
+		info.protocol = protocol;
+		info.family = family;
+		info.type = type;
+
+		pr_debug("family=%u type=%u protocol=%u kern=%u\n",
+			 family, type, protocol, kern);
+
+		snet_do_verdict(&verdict, &info);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_CREATE);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_CREATE);
+	}
+
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+
+out:
+	return verdict;
+}
+
+static int snet_socket_bind(struct socket *sock,
+			    struct sockaddr *address, int addrlen)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_BIND);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_BIND, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+		struct sockaddr_in *a = (struct sockaddr_in *) address;
+		struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_BIND;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = a->sin_addr.s_addr;
+			info.dst.u3.ip = inet->inet_daddr;
+			info.src.u.port = ntohs(a->sin_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info) ;
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&a6->sin6_addr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			info.src.u.port = ntohs(a6->sin6_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_BIND);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_BIND);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_connect(struct socket *sock,
+			       struct sockaddr *address, int addrlen)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CONNECT);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_CONNECT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+		struct sockaddr_in *a = (struct sockaddr_in *) address;
+		struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+		/* prepare networking informations for userspace */
+		memset(&info, 0, sizeof(struct  snet_info));
+		info.syscall = SNET_SOCKET_CONNECT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = a->sin_addr.s_addr;
+			info.dst.u.port = ntohs(a->sin_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&a6->sin6_addr,
+			       sizeof(info.dst.u3.ip6));
+			info.dst.u.port = ntohs(a6->sin6_port);
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_CONNECT);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_CONNECT);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_listen(struct socket *sock, int backlog)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_LISTEN);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_LISTEN, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		memset(&info, 0, sizeof(struct  snet_info));
+		info.syscall = SNET_SOCKET_LISTEN;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_LISTEN);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_LISTEN);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_accept(struct socket *sock, struct socket *newsock)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_ACCEPT);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_ACCEPT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_ACCEPT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_ACCEPT);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_ACCEPT);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static void snet_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_POST_ACCEPT);
+
+	if (snet_check_listeners(NULL)  < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_POST_ACCEPT, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(newsock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_POST_ACCEPT;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.verdict_id = 0;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			goto out;
+			break;
+		}
+		snet_pr_tuple(&info);
+		if (snet_do_send_event(&info) < 0)
+			SNET_STATS_INC(SNET_STATS_REG_ERROR,
+				       SNET_SOCKET_POST_ACCEPT);
+		else
+			SNET_STATS_INC(SNET_STATS_REG_GRANT,
+				       SNET_SOCKET_POST_ACCEPT);
+	}
+out:
+	return;
+}
+
+static int snet_socket_sendmsg(struct socket *sock,
+			       struct msghdr *msg, int size)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_SENDMSG);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_SENDMSG, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_SENDMSG;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_SENDMSG);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_SENDMSG);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_recvmsg(struct socket *sock,
+			       struct msghdr *msg, int size, int flags)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_RECVMSG);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_RECVMSG, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_RECVMSG;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			/* check tickets */
+			verdict = snet_ticket_check(&info);
+			if (verdict != SNET_VERDICT_NONE)
+				goto out;
+			/* inserting verdict PENDING */
+			info.verdict_id = snet_verdict_insert();
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		snet_do_verdict(&verdict, &info);
+		/* create ticket */
+		snet_ticket_create(&info, verdict);
+		snet_stats_inc_reg(verdict, SNET_SOCKET_RECVMSG);
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_RECVMSG);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static int snet_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	u8 protocol = 0;
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_SOCK_RCV_SKB);
+
+	if (snet_check_listeners(&verdict) < 0)
+		goto out;
+
+	protocol = sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_SOCK_RCV_SKB, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_SOCK_RCV_SKB;
+		info.protocol = protocol;
+		info.family = sk->sk_family;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sk->sk_family) {
+		case PF_INET:
+			/* inserting verdict PENDING  */
+			/* info.verdict_id = snet_verdict_insert(); */
+
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			/* inserting verdict PENDING  */
+			/* info.verdict_id = snet_verdict_insert(); */
+
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			verdict = SNET_VERDICT_NONE;
+			goto skip_send_wait;
+			break;
+		}
+		snet_pr_tuple(&info);
+		/* SNET_DOC_VERDICT(info); */
+	} else {
+		verdict = SNET_VERDICT_GRANT;
+		SNET_STATS_INC(SNET_STATS_UNREG, SNET_SOCKET_SOCK_RCV_SKB);
+	}
+
+skip_send_wait:
+	if (verdict == SNET_VERDICT_NONE)
+		verdict = snet_verdict_policy;
+out:
+	return verdict;
+}
+
+static void snet_socket_close(struct socket *sock)
+{
+	u8 protocol = 0;
+
+	if (sock == NULL || sock->sk == NULL) {
+		goto out;
+	}
+
+	SNET_STATS_INC(SNET_STATS_EXEC, SNET_SOCKET_CLOSE);
+
+	if (snet_check_listeners(NULL)  < 0)
+		goto out;
+
+	protocol = sock->sk->sk_protocol;
+
+	if (snet_event_is_registered(SNET_SOCKET_CLOSE, protocol)) {
+		struct snet_info info;
+		struct inet_sock *inet = inet_sk(sock->sk);
+
+		/* prepare networking informations for userspace */
+		info.syscall = SNET_SOCKET_CLOSE;
+		info.protocol = protocol;
+		info.family = sock->sk->sk_family;
+		info.verdict_id = 0;
+		info.src.u.port = ntohs(inet->inet_sport);
+		info.dst.u.port = ntohs(inet->inet_dport);
+
+		switch (sock->sk->sk_family) {
+		case PF_INET:
+			info.src.u3.ip = inet->inet_saddr;
+			info.dst.u3.ip = inet->inet_daddr;
+			break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		case PF_INET6:
+			memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+			       sizeof(info.src.u3.ip6));
+			memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+			       sizeof(info.dst.u3.ip6));
+			break;
+#endif
+		default:
+			goto out;
+			break;
+		}
+		snet_pr_tuple(&info);
+		if (snet_do_send_event(&info) < 0)
+			SNET_STATS_INC(SNET_STATS_REG_ERROR, SNET_SOCKET_CLOSE);
+		else
+			SNET_STATS_INC(SNET_STATS_REG_GRANT, SNET_SOCKET_CLOSE);
+	}
+out:
+	return;
+}
+
+static struct security_operations snet_security_ops = {
+	.name			= "snet",
+
+	.socket_create		= snet_socket_create,
+	.socket_bind		= snet_socket_bind,
+	.socket_connect		= snet_socket_connect,
+	.socket_listen		= snet_socket_listen,
+	.socket_accept		= snet_socket_accept,
+	.socket_post_accept	= snet_socket_post_accept,
+	.socket_sendmsg		= snet_socket_sendmsg,
+	.socket_recvmsg		= snet_socket_recvmsg,
+	.socket_sock_rcv_skb	= snet_socket_sock_rcv_skb,
+	.socket_close		= snet_socket_close,
+
+	.cred_prepare		= snet_prepare_creds,
+	.cred_free		= snet_cred_free,
+};
+
+int snet_hooks_init(void)
+{
+	if (!security_module_enable(&snet_security_ops))
+		return 0;
+
+	if (register_security(&snet_security_ops))
+		panic("snet: failed to register security_ops\n");
+
+	return 0;
+}
diff --git a/security/snet/snet_hooks.h b/security/snet/snet_hooks.h
new file mode 100644
index 0000000..05fe5e8
--- /dev/null
+++ b/security/snet/snet_hooks.h
@@ -0,0 +1,10 @@
+#ifndef _SNET_HOOKS_H
+#define _SNET_HOOKS_H
+
+extern uint32_t snet_nl_pid;
+extern unsigned int snet_verdict_policy;
+
+/* init function */
+int snet_hooks_init(void);
+
+#endif /* _SNET_HOOK_H */
-- 
1.7.4.1


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

* [RFC v4 06/11] snet: introduce snet_netlink
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (22 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` [RFC v4 07/11] snet: introduce snet_verdict y
                   ` (19 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds the snet communication's subsystem.

snet_netlink is using genetlink for sending/receiving messages to/from userspace.
the genetlink operations permit to receive orders to manage the list of events
- events are values [syscall, protocol] - which is used to know which syscall
and protocol have to be protected. genl operations are also used to manage
communication of events to userspace, and to receive the related verdict

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_netlink.c        |  442 +++++++++++++++++++++++++++++++++++
 security/snet/snet_netlink.h        |   17 ++
 security/snet/snet_netlink_helper.c |  220 +++++++++++++++++
 security/snet/snet_netlink_helper.h |    7 +
 4 files changed, 686 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_netlink.c
 create mode 100644 security/snet/snet_netlink.h
 create mode 100644 security/snet/snet_netlink_helper.c
 create mode 100644 security/snet/snet_netlink_helper.h

diff --git a/security/snet/snet_netlink.c b/security/snet/snet_netlink.c
new file mode 100644
index 0000000..0d6cd8b
--- /dev/null
+++ b/security/snet/snet_netlink.c
@@ -0,0 +1,442 @@
+#include <linux/sched.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+#include "snet_netlink.h"
+#include "snet_netlink_helper.h"
+#include "snet_verdict.h"
+#include "snet_event.h"
+#include "snet_utils.h"
+
+atomic_t snet_nl_seq = ATOMIC_INIT(0);
+uint32_t snet_nl_pid;
+static struct genl_family snet_genl_family;
+
+/*
+ * snet genetlink
+ */
+int snet_nl_send_event(struct snet_info *info)
+{
+	struct sk_buff *skb_rsp;
+	void *msg_head;
+	int ret = 0, sbs = -1;
+	size_t size = 0;
+
+	sbs = snet_nl_size_by_syscall(info);
+	if (sbs < 0)
+		return -EINVAL;
+
+	size =  sbs +
+		2 * nla_total_size(sizeof(u8)) +
+		1 * nla_total_size(sizeof(u16)) +
+		(info->verdict_id ? 3 : 2) * nla_total_size(sizeof(u32));
+
+	skb_rsp = genlmsg_new(size, GFP_KERNEL);
+	if (skb_rsp == NULL)
+		return -ENOMEM;
+
+	msg_head = genlmsg_put(skb_rsp, snet_nl_pid,
+			       atomic_inc_return(&snet_nl_seq),
+			       &snet_genl_family, 0, SNET_C_VERDICT);
+	if (msg_head == NULL)
+		goto nla_put_failure;
+
+	pr_debug("verdict_id=0x%x syscall=%s protocol=%u "
+		 "family=%u uid=%u pid=%u\n",
+		 info->verdict_id, snet_syscall_name(info->syscall),
+		 info->protocol, info->family, current_uid(), current->pid);
+
+	if (info->verdict_id)
+		NLA_PUT_U32(skb_rsp, SNET_A_VERDICT_ID, info->verdict_id);
+	NLA_PUT_U16(skb_rsp, SNET_A_SYSCALL, info->syscall);
+	NLA_PUT_U8(skb_rsp, SNET_A_PROTOCOL, info->protocol);
+	NLA_PUT_U8(skb_rsp, SNET_A_FAMILY, info->family);
+	NLA_PUT_U32(skb_rsp, SNET_A_UID, current_uid());
+	NLA_PUT_U32(skb_rsp, SNET_A_PID, current->pid);
+
+	ret = snet_nl_fill_by_syscall(skb_rsp, info);
+	if (ret != 0)
+		goto nla_put_failure;
+
+	ret = genlmsg_end(skb_rsp, msg_head);
+	if (ret < 0)
+		goto nla_put_failure;
+
+	return genlmsg_unicast(&init_net, skb_rsp, snet_nl_pid);
+
+nla_put_failure:
+	kfree_skb(skb_rsp);
+	return -ECONNABORTED;
+}
+
+/*
+ * snet genetlink functions
+ */
+
+static struct genl_family snet_genl_family = {
+	.id		= GENL_ID_GENERATE,
+	.hdrsize	= 0,
+	.name		= SNET_GENL_NAME,
+	.version	= SNET_GENL_VERSION,
+	.maxattr	= SNET_A_MAX,
+};
+
+static const struct nla_policy snet_genl_policy[SNET_A_MAX + 1] = {
+	[SNET_A_VERSION]		= { .type = NLA_U32 },
+	[SNET_A_VERDICT_ID]		= { .type = NLA_U32 },
+	[SNET_A_FAMILY]			= { .type = NLA_U8 },
+	[SNET_A_SYSCALL]		= { .type = NLA_U16 },
+	[SNET_A_PROTOCOL]		= { .type = NLA_U8 },
+	[SNET_A_UID]			= { .type = NLA_U32 },
+	[SNET_A_PID]			= { .type = NLA_U32 },
+	[SNET_A_TYPE]			= { .type = NLA_U32 },
+	[SNET_A_IPV4SADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in_addr) },
+	[SNET_A_IPV6SADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in6_addr) },
+	[SNET_A_IPV4DADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in_addr) },
+	[SNET_A_IPV6DADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in6_addr) },
+	[SNET_A_SPORT]			= { .type = NLA_U16 },
+	[SNET_A_DPORT]			= { .type = NLA_U16 },
+	[SNET_A_VERDICT]		= { .type = NLA_U8 },
+	[SNET_A_VERDICT_DELAY]		= { .type = NLA_U32 },
+	[SNET_A_TICKET_DELAY]		= { .type = NLA_U32 },
+	[SNET_A_TICKET_MODE]		= { .type = NLA_U8 },
+};
+
+/**
+ * snet_nl_version - Handle a VERSION message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Process a user generated VERSION message and respond accordingly.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_version(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	struct sk_buff *skb_rsp = NULL;
+	void *msg_head;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (skb_rsp == NULL)
+		return -ENOMEM;
+	msg_head = genlmsg_put_reply(skb_rsp, info, &snet_genl_family,
+				     0, SNET_C_VERSION);
+	if (msg_head == NULL)
+		goto nla_put_failure;
+
+	NLA_PUT_U32(skb_rsp, SNET_A_VERSION, SNET_VERSION);
+
+	ret = genlmsg_end(skb_rsp, msg_head);
+	if (ret < 0)
+		goto nla_put_failure;
+
+	ret = genlmsg_reply(skb_rsp, info);
+	if (ret != 0)
+		goto nla_put_failure;
+	return 0;
+
+nla_put_failure:
+	kfree_skb(skb_rsp);
+	return ret;
+}
+
+/**
+ * snet_nl_register - Handle a REGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that an application is listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_register(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	u32 version = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_VERSION]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	version = nla_get_u32(info->attrs[SNET_A_VERSION]);
+
+	if (version != SNET_VERSION) {
+		ret = -EPERM;
+		goto out;
+	}
+
+	if (snet_nl_pid > 0) {
+		ret = -ECONNREFUSED;
+		goto out;
+	}
+	snet_nl_pid = info->snd_pid;
+	pr_debug("pid=%u\n", snet_nl_pid);
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_unregister - Handle a UNREGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that the application is no more listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_unregister(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (snet_nl_pid == 0) {
+		ret = -ENOTCONN;
+		goto out;
+	}
+
+	snet_nl_pid = 0;
+	pr_debug("pid=%u\n", snet_nl_pid);
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_insert - Handle a INSERT message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Insert a new event to the events' hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_insert(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	enum snet_syscall syscall;
+	u8 protocol;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+		ret =  -EINVAL;
+		goto out;
+	}
+	syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+	protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+	ret = snet_event_insert(syscall, protocol);
+	pr_debug("syscall=%s protocol=%u insert=%s\n",
+		 snet_syscall_name(syscall), protocol,
+		 (ret == 0) ? "success" : "failed");
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_remove - Handle a REMOVE message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove a event from the events' hastable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_remove(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	enum snet_syscall syscall;
+	u8 protocol;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+	protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+	ret = snet_event_remove(syscall, protocol);
+	pr_debug("syscall=%s protocol=%u remove=%s\n",
+		 snet_syscall_name(syscall), protocol,
+		 (ret == 0) ? "success" : "failed");
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_flush - Handle a FLUSH message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove all events from the hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_flush(struct sk_buff *skb, struct genl_info *info)
+{
+	atomic_set(&snet_nl_seq, info->snd_seq);
+	snet_event_flush();
+	return 0;
+}
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+			   u32 flags, u8 protocol, enum snet_syscall syscall)
+{
+	void *hdr;
+
+	hdr = genlmsg_put(skb, pid, seq, &snet_genl_family, flags, SNET_C_LIST);
+	if (hdr == NULL)
+		return -1;
+
+	NLA_PUT_U16(skb, SNET_A_SYSCALL, syscall);
+	NLA_PUT_U8(skb, SNET_A_PROTOCOL, protocol);
+
+	return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+	genlmsg_cancel(skb, hdr);
+	return -EMSGSIZE;
+}
+/**
+ * snet_nl_list - Handle a LIST message
+ * @skb: the NETLINK buffer
+ * @cb:
+ *
+ * Description:
+ * Process a user LIST message and respond. Returns zero on success,
+ * and negative values on error.
+ */
+static int snet_nl_list(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	unsigned int len = 0;
+
+	atomic_set(&snet_nl_seq, cb->nlh->nlmsg_seq);
+	len = snet_event_fill_info(skb, cb);
+	return len;
+}
+
+/**
+ * snet_nl_verdict - Handle a VERDICT message
+ * @skb: the NETLINK buffer
+ * @info the Generic NETLINK info block
+ *
+ * Description:
+ * Provides userspace with a VERDICT message, ie we are sending informations
+ * with this command. Userspace is sending the appropriate verdict for the
+ * event. Returns zero on success,and negative values on error.
+ */
+static int snet_nl_verdict(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	u32 verdict_id;
+	enum snet_verdict verdict;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (snet_nl_pid == 0) {
+		ret = -ENOTCONN;
+		goto out;
+	}
+
+	if (!info->attrs[SNET_A_VERDICT_ID] || !info->attrs[SNET_A_VERDICT]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	verdict_id = nla_get_u32(info->attrs[SNET_A_VERDICT_ID]);
+	verdict = nla_get_u8(info->attrs[SNET_A_VERDICT]);
+	ret = snet_verdict_set(verdict_id, verdict);
+out:
+	return ret;
+}
+
+static int snet_nl_config(struct sk_buff *skb,
+			  struct genl_info *info)
+{
+	int ret = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (info->attrs[SNET_A_VERDICT_DELAY]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_VERDICT_DELAY]);
+		if (new == 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_verdict_delay = new;
+		pr_debug("snet_nl_config: verdict_delay=%u\n", snet_verdict_delay);
+	}
+	if (info->attrs[SNET_A_TICKET_DELAY]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_TICKET_DELAY]);
+		if (new == 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_ticket_delay = new;
+		pr_debug("snet_nl_config: ticket_delay=%u\n", snet_ticket_delay);
+	}
+	if (info->attrs[SNET_A_TICKET_MODE]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_TICKET_MODE]);
+		if (new >= SNET_TICKET_INVALID) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_ticket_mode = new;
+		pr_debug("snet_nl_config: ticket_mode=%u\n", snet_ticket_mode);
+	}
+out:
+	return ret;
+}
+
+#define SNET_GENL_OPS(_cmd, _flags, _policy, _op, _opname)	\
+	{							\
+		.cmd	= _cmd,					\
+		.flags	= _flags,				\
+		.policy	= _policy,				\
+		._op	= snet_nl_##_opname,			\
+	}
+
+static struct genl_ops snet_genl_ops[] = {
+	SNET_GENL_OPS(SNET_C_VERSION, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, version),
+	SNET_GENL_OPS(SNET_C_REGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, register),
+	SNET_GENL_OPS(SNET_C_UNREGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, unregister),
+	SNET_GENL_OPS(SNET_C_INSERT, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, insert),
+	SNET_GENL_OPS(SNET_C_REMOVE, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, remove),
+	SNET_GENL_OPS(SNET_C_FLUSH, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, flush),
+	SNET_GENL_OPS(SNET_C_LIST, GENL_ADMIN_PERM, snet_genl_policy,
+		      dumpit, list),
+	SNET_GENL_OPS(SNET_C_VERDICT, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, verdict),
+	SNET_GENL_OPS(SNET_C_CONFIG, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, config),
+};
+
+#undef SNET_GENL_OPS
+
+static __init int snet_netlink_init(void)
+{
+	return genl_register_family_with_ops(&snet_genl_family,
+					     snet_genl_ops,
+					     ARRAY_SIZE(snet_genl_ops));
+}
+
+void snet_netlink_exit(void)
+{
+	genl_unregister_family(&snet_genl_family);
+}
+
+__initcall(snet_netlink_init);
diff --git a/security/snet/snet_netlink.h b/security/snet/snet_netlink.h
new file mode 100644
index 0000000..5e80d7b
--- /dev/null
+++ b/security/snet/snet_netlink.h
@@ -0,0 +1,17 @@
+#ifndef _SNET_NETLINK_H
+#define _SNET_NETLINK_H
+
+#include <linux/in6.h>
+
+extern unsigned int snet_verdict_delay;
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+int snet_nl_send_event(struct snet_info *info);
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+			   u32 flags, u8 protocol, enum snet_syscall syscall);
+
+void snet_netlink_exit(void);
+
+#endif /* _SNET_NETLINK_H */
diff --git a/security/snet/snet_netlink_helper.c b/security/snet/snet_netlink_helper.c
new file mode 100644
index 0000000..d7d743c
--- /dev/null
+++ b/security/snet/snet_netlink_helper.c
@@ -0,0 +1,220 @@
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+
+static int fill_src(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	switch (info->family) {
+	case PF_INET:
+		ret = nla_put(skb_rsp, SNET_A_IPV4SADDR,
+			      sizeof(struct in_addr), &(info->src.u3.ip));
+		if (ret != 0)
+			goto out;
+		break;
+	case PF_INET6:
+		ret = nla_put(skb_rsp, SNET_A_IPV6SADDR,
+			      sizeof(struct in6_addr), &(info->src.u3.ip6));
+		if (ret != 0)
+			goto out;
+		break;
+	default:
+		break;
+	}
+	ret = nla_put_u16(skb_rsp, SNET_A_SPORT, info->src.u.port);
+out:
+	return ret;
+
+}
+
+static int fill_dst(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	switch (info->family) {
+	case PF_INET:
+		ret = nla_put(skb_rsp, SNET_A_IPV4DADDR,
+			      sizeof(struct in_addr), &(info->dst.u3.ip));
+		if (ret != 0)
+			goto out;
+		break;
+	case PF_INET6:
+		ret = nla_put(skb_rsp, SNET_A_IPV6DADDR,
+			      sizeof(struct in6_addr), &(info->dst.u3.ip6));
+		if (ret != 0)
+			goto out;
+		break;
+	default:
+		break;
+	}
+	ret = nla_put_u16(skb_rsp, SNET_A_DPORT, info->dst.u.port);
+out:
+	return ret;
+}
+
+static int snet_fill_create(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	ret = nla_put_u8(skb_rsp, SNET_A_TYPE, info->type);
+	return ret;
+}
+
+static int snet_fill_bind(struct sk_buff *skb_rsp,  struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_connect(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_listen(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_accept(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_post_accept(struct sk_buff *skb_rsp,
+				 struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_sendmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_recvmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_sock_rcv_skb(struct sk_buff *skb_rsp,
+				  struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_close(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	static int (*snet_df[])(struct sk_buff *, struct snet_info *) = {
+		[SNET_SOCKET_CREATE]		= &snet_fill_create,
+		[SNET_SOCKET_BIND]		= &snet_fill_bind,
+		[SNET_SOCKET_CONNECT]		= &snet_fill_connect,
+		[SNET_SOCKET_LISTEN]		= &snet_fill_listen,
+		[SNET_SOCKET_ACCEPT]		= &snet_fill_accept,
+		[SNET_SOCKET_POST_ACCEPT]	= &snet_fill_post_accept,
+		[SNET_SOCKET_SENDMSG]		= &snet_fill_sendmsg,
+		[SNET_SOCKET_RECVMSG]		= &snet_fill_recvmsg,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= &snet_fill_sock_rcv_skb,
+		[SNET_SOCKET_CLOSE]		= &snet_fill_close,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return -EINVAL;
+	else
+		return snet_df[info->syscall](skb_rsp, info);
+}
+
+int snet_nl_size_by_syscall(struct snet_info *info)
+{
+	unsigned int size_addr4_port = nla_total_size(sizeof(struct in_addr)) +
+				       nla_total_size(sizeof(u16));
+	unsigned int size_addr6_port = nla_total_size(sizeof(struct in6_addr)) +
+				       nla_total_size(sizeof(u16));
+
+	unsigned int sbs4[] = {
+		[SNET_SOCKET_CREATE]		= nla_total_size(sizeof(u8)),
+		[SNET_SOCKET_BIND]		= size_addr4_port,
+		[SNET_SOCKET_CONNECT]		= 2 * size_addr4_port,
+		[SNET_SOCKET_LISTEN]		= size_addr4_port,
+		[SNET_SOCKET_ACCEPT]		= size_addr4_port,
+		[SNET_SOCKET_POST_ACCEPT]	= 2 * size_addr4_port,
+		[SNET_SOCKET_SENDMSG]		= 2 * size_addr4_port,
+		[SNET_SOCKET_RECVMSG]		= 2 * size_addr4_port,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= 2 * size_addr4_port,
+		[SNET_SOCKET_CLOSE]		= 2 * size_addr4_port,
+	};
+
+	unsigned int sbs6[] = {
+		[SNET_SOCKET_CREATE]		= nla_total_size(sizeof(u8)),
+		[SNET_SOCKET_BIND]		= size_addr6_port,
+		[SNET_SOCKET_CONNECT]		= 2 * size_addr6_port,
+		[SNET_SOCKET_LISTEN]		= size_addr6_port,
+		[SNET_SOCKET_ACCEPT]		= size_addr6_port,
+		[SNET_SOCKET_POST_ACCEPT]	= 2 * size_addr6_port,
+		[SNET_SOCKET_SENDMSG]		= 2 * size_addr6_port,
+		[SNET_SOCKET_RECVMSG]		= 2 * size_addr6_port,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= 2 * size_addr6_port,
+		[SNET_SOCKET_CLOSE]		= 2 * size_addr6_port,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return -EINVAL;
+	else {
+		switch (info->family) {
+		case AF_INET:
+			return sbs4[info->syscall];
+			break;
+		case AF_INET6:
+			return sbs6[info->syscall];
+			break;
+		default:
+			return -EINVAL;
+			break;
+		}
+	}
+}
diff --git a/security/snet/snet_netlink_helper.h b/security/snet/snet_netlink_helper.h
new file mode 100644
index 0000000..d12e563
--- /dev/null
+++ b/security/snet/snet_netlink_helper.h
@@ -0,0 +1,7 @@
+#ifndef _SNET_NETLINK_HELPER_H
+#define _SNET_NETLINK_HELPER_H
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info);
+int snet_nl_size_by_syscall(struct snet_info *info);
+
+#endif /* SNET_NETLINK_HELPER_H */
-- 
1.7.4.1

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

* [RFC v4 06/11] snet: introduce snet_netlink
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (20 preceding siblings ...)
  2011-05-05 13:59 ` [RFC v4 06/11] snet: introduce snet_netlink y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (21 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds the snet communication's subsystem.

snet_netlink is using genetlink for sending/receiving messages to/from userspace.
the genetlink operations permit to receive orders to manage the list of events
- events are values [syscall, protocol] - which is used to know which syscall
and protocol have to be protected. genl operations are also used to manage
communication of events to userspace, and to receive the related verdict

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_netlink.c        |  442 +++++++++++++++++++++++++++++++++++
 security/snet/snet_netlink.h        |   17 ++
 security/snet/snet_netlink_helper.c |  220 +++++++++++++++++
 security/snet/snet_netlink_helper.h |    7 +
 4 files changed, 686 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_netlink.c
 create mode 100644 security/snet/snet_netlink.h
 create mode 100644 security/snet/snet_netlink_helper.c
 create mode 100644 security/snet/snet_netlink_helper.h

diff --git a/security/snet/snet_netlink.c b/security/snet/snet_netlink.c
new file mode 100644
index 0000000..0d6cd8b
--- /dev/null
+++ b/security/snet/snet_netlink.c
@@ -0,0 +1,442 @@
+#include <linux/sched.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+#include "snet_netlink.h"
+#include "snet_netlink_helper.h"
+#include "snet_verdict.h"
+#include "snet_event.h"
+#include "snet_utils.h"
+
+atomic_t snet_nl_seq = ATOMIC_INIT(0);
+uint32_t snet_nl_pid;
+static struct genl_family snet_genl_family;
+
+/*
+ * snet genetlink
+ */
+int snet_nl_send_event(struct snet_info *info)
+{
+	struct sk_buff *skb_rsp;
+	void *msg_head;
+	int ret = 0, sbs = -1;
+	size_t size = 0;
+
+	sbs = snet_nl_size_by_syscall(info);
+	if (sbs < 0)
+		return -EINVAL;
+
+	size =  sbs +
+		2 * nla_total_size(sizeof(u8)) +
+		1 * nla_total_size(sizeof(u16)) +
+		(info->verdict_id ? 3 : 2) * nla_total_size(sizeof(u32));
+
+	skb_rsp = genlmsg_new(size, GFP_KERNEL);
+	if (skb_rsp == NULL)
+		return -ENOMEM;
+
+	msg_head = genlmsg_put(skb_rsp, snet_nl_pid,
+			       atomic_inc_return(&snet_nl_seq),
+			       &snet_genl_family, 0, SNET_C_VERDICT);
+	if (msg_head == NULL)
+		goto nla_put_failure;
+
+	pr_debug("verdict_id=0x%x syscall=%s protocol=%u "
+		 "family=%u uid=%u pid=%u\n",
+		 info->verdict_id, snet_syscall_name(info->syscall),
+		 info->protocol, info->family, current_uid(), current->pid);
+
+	if (info->verdict_id)
+		NLA_PUT_U32(skb_rsp, SNET_A_VERDICT_ID, info->verdict_id);
+	NLA_PUT_U16(skb_rsp, SNET_A_SYSCALL, info->syscall);
+	NLA_PUT_U8(skb_rsp, SNET_A_PROTOCOL, info->protocol);
+	NLA_PUT_U8(skb_rsp, SNET_A_FAMILY, info->family);
+	NLA_PUT_U32(skb_rsp, SNET_A_UID, current_uid());
+	NLA_PUT_U32(skb_rsp, SNET_A_PID, current->pid);
+
+	ret = snet_nl_fill_by_syscall(skb_rsp, info);
+	if (ret != 0)
+		goto nla_put_failure;
+
+	ret = genlmsg_end(skb_rsp, msg_head);
+	if (ret < 0)
+		goto nla_put_failure;
+
+	return genlmsg_unicast(&init_net, skb_rsp, snet_nl_pid);
+
+nla_put_failure:
+	kfree_skb(skb_rsp);
+	return -ECONNABORTED;
+}
+
+/*
+ * snet genetlink functions
+ */
+
+static struct genl_family snet_genl_family = {
+	.id		= GENL_ID_GENERATE,
+	.hdrsize	= 0,
+	.name		= SNET_GENL_NAME,
+	.version	= SNET_GENL_VERSION,
+	.maxattr	= SNET_A_MAX,
+};
+
+static const struct nla_policy snet_genl_policy[SNET_A_MAX + 1] = {
+	[SNET_A_VERSION]		= { .type = NLA_U32 },
+	[SNET_A_VERDICT_ID]		= { .type = NLA_U32 },
+	[SNET_A_FAMILY]			= { .type = NLA_U8 },
+	[SNET_A_SYSCALL]		= { .type = NLA_U16 },
+	[SNET_A_PROTOCOL]		= { .type = NLA_U8 },
+	[SNET_A_UID]			= { .type = NLA_U32 },
+	[SNET_A_PID]			= { .type = NLA_U32 },
+	[SNET_A_TYPE]			= { .type = NLA_U32 },
+	[SNET_A_IPV4SADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in_addr) },
+	[SNET_A_IPV6SADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in6_addr) },
+	[SNET_A_IPV4DADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in_addr) },
+	[SNET_A_IPV6DADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in6_addr) },
+	[SNET_A_SPORT]			= { .type = NLA_U16 },
+	[SNET_A_DPORT]			= { .type = NLA_U16 },
+	[SNET_A_VERDICT]		= { .type = NLA_U8 },
+	[SNET_A_VERDICT_DELAY]		= { .type = NLA_U32 },
+	[SNET_A_TICKET_DELAY]		= { .type = NLA_U32 },
+	[SNET_A_TICKET_MODE]		= { .type = NLA_U8 },
+};
+
+/**
+ * snet_nl_version - Handle a VERSION message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Process a user generated VERSION message and respond accordingly.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_version(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	struct sk_buff *skb_rsp = NULL;
+	void *msg_head;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (skb_rsp == NULL)
+		return -ENOMEM;
+	msg_head = genlmsg_put_reply(skb_rsp, info, &snet_genl_family,
+				     0, SNET_C_VERSION);
+	if (msg_head == NULL)
+		goto nla_put_failure;
+
+	NLA_PUT_U32(skb_rsp, SNET_A_VERSION, SNET_VERSION);
+
+	ret = genlmsg_end(skb_rsp, msg_head);
+	if (ret < 0)
+		goto nla_put_failure;
+
+	ret = genlmsg_reply(skb_rsp, info);
+	if (ret != 0)
+		goto nla_put_failure;
+	return 0;
+
+nla_put_failure:
+	kfree_skb(skb_rsp);
+	return ret;
+}
+
+/**
+ * snet_nl_register - Handle a REGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that an application is listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_register(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	u32 version = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_VERSION]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	version = nla_get_u32(info->attrs[SNET_A_VERSION]);
+
+	if (version != SNET_VERSION) {
+		ret = -EPERM;
+		goto out;
+	}
+
+	if (snet_nl_pid > 0) {
+		ret = -ECONNREFUSED;
+		goto out;
+	}
+	snet_nl_pid = info->snd_pid;
+	pr_debug("pid=%u\n", snet_nl_pid);
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_unregister - Handle a UNREGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that the application is no more listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_unregister(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (snet_nl_pid == 0) {
+		ret = -ENOTCONN;
+		goto out;
+	}
+
+	snet_nl_pid = 0;
+	pr_debug("pid=%u\n", snet_nl_pid);
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_insert - Handle a INSERT message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Insert a new event to the events' hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_insert(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	enum snet_syscall syscall;
+	u8 protocol;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+		ret =  -EINVAL;
+		goto out;
+	}
+	syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+	protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+	ret = snet_event_insert(syscall, protocol);
+	pr_debug("syscall=%s protocol=%u insert=%s\n",
+		 snet_syscall_name(syscall), protocol,
+		 (ret == 0) ? "success" : "failed");
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_remove - Handle a REMOVE message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove a event from the events' hastable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_remove(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	enum snet_syscall syscall;
+	u8 protocol;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+	protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+	ret = snet_event_remove(syscall, protocol);
+	pr_debug("syscall=%s protocol=%u remove=%s\n",
+		 snet_syscall_name(syscall), protocol,
+		 (ret == 0) ? "success" : "failed");
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_flush - Handle a FLUSH message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove all events from the hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_flush(struct sk_buff *skb, struct genl_info *info)
+{
+	atomic_set(&snet_nl_seq, info->snd_seq);
+	snet_event_flush();
+	return 0;
+}
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+			   u32 flags, u8 protocol, enum snet_syscall syscall)
+{
+	void *hdr;
+
+	hdr = genlmsg_put(skb, pid, seq, &snet_genl_family, flags, SNET_C_LIST);
+	if (hdr == NULL)
+		return -1;
+
+	NLA_PUT_U16(skb, SNET_A_SYSCALL, syscall);
+	NLA_PUT_U8(skb, SNET_A_PROTOCOL, protocol);
+
+	return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+	genlmsg_cancel(skb, hdr);
+	return -EMSGSIZE;
+}
+/**
+ * snet_nl_list - Handle a LIST message
+ * @skb: the NETLINK buffer
+ * @cb:
+ *
+ * Description:
+ * Process a user LIST message and respond. Returns zero on success,
+ * and negative values on error.
+ */
+static int snet_nl_list(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	unsigned int len = 0;
+
+	atomic_set(&snet_nl_seq, cb->nlh->nlmsg_seq);
+	len = snet_event_fill_info(skb, cb);
+	return len;
+}
+
+/**
+ * snet_nl_verdict - Handle a VERDICT message
+ * @skb: the NETLINK buffer
+ * @info the Generic NETLINK info block
+ *
+ * Description:
+ * Provides userspace with a VERDICT message, ie we are sending informations
+ * with this command. Userspace is sending the appropriate verdict for the
+ * event. Returns zero on success,and negative values on error.
+ */
+static int snet_nl_verdict(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	u32 verdict_id;
+	enum snet_verdict verdict;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (snet_nl_pid == 0) {
+		ret = -ENOTCONN;
+		goto out;
+	}
+
+	if (!info->attrs[SNET_A_VERDICT_ID] || !info->attrs[SNET_A_VERDICT]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	verdict_id = nla_get_u32(info->attrs[SNET_A_VERDICT_ID]);
+	verdict = nla_get_u8(info->attrs[SNET_A_VERDICT]);
+	ret = snet_verdict_set(verdict_id, verdict);
+out:
+	return ret;
+}
+
+static int snet_nl_config(struct sk_buff *skb,
+			  struct genl_info *info)
+{
+	int ret = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (info->attrs[SNET_A_VERDICT_DELAY]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_VERDICT_DELAY]);
+		if (new == 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_verdict_delay = new;
+		pr_debug("snet_nl_config: verdict_delay=%u\n", snet_verdict_delay);
+	}
+	if (info->attrs[SNET_A_TICKET_DELAY]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_TICKET_DELAY]);
+		if (new == 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_ticket_delay = new;
+		pr_debug("snet_nl_config: ticket_delay=%u\n", snet_ticket_delay);
+	}
+	if (info->attrs[SNET_A_TICKET_MODE]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_TICKET_MODE]);
+		if (new >= SNET_TICKET_INVALID) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_ticket_mode = new;
+		pr_debug("snet_nl_config: ticket_mode=%u\n", snet_ticket_mode);
+	}
+out:
+	return ret;
+}
+
+#define SNET_GENL_OPS(_cmd, _flags, _policy, _op, _opname)	\
+	{							\
+		.cmd	= _cmd,					\
+		.flags	= _flags,				\
+		.policy	= _policy,				\
+		._op	= snet_nl_##_opname,			\
+	}
+
+static struct genl_ops snet_genl_ops[] = {
+	SNET_GENL_OPS(SNET_C_VERSION, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, version),
+	SNET_GENL_OPS(SNET_C_REGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, register),
+	SNET_GENL_OPS(SNET_C_UNREGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, unregister),
+	SNET_GENL_OPS(SNET_C_INSERT, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, insert),
+	SNET_GENL_OPS(SNET_C_REMOVE, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, remove),
+	SNET_GENL_OPS(SNET_C_FLUSH, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, flush),
+	SNET_GENL_OPS(SNET_C_LIST, GENL_ADMIN_PERM, snet_genl_policy,
+		      dumpit, list),
+	SNET_GENL_OPS(SNET_C_VERDICT, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, verdict),
+	SNET_GENL_OPS(SNET_C_CONFIG, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, config),
+};
+
+#undef SNET_GENL_OPS
+
+static __init int snet_netlink_init(void)
+{
+	return genl_register_family_with_ops(&snet_genl_family,
+					     snet_genl_ops,
+					     ARRAY_SIZE(snet_genl_ops));
+}
+
+void snet_netlink_exit(void)
+{
+	genl_unregister_family(&snet_genl_family);
+}
+
+__initcall(snet_netlink_init);
diff --git a/security/snet/snet_netlink.h b/security/snet/snet_netlink.h
new file mode 100644
index 0000000..5e80d7b
--- /dev/null
+++ b/security/snet/snet_netlink.h
@@ -0,0 +1,17 @@
+#ifndef _SNET_NETLINK_H
+#define _SNET_NETLINK_H
+
+#include <linux/in6.h>
+
+extern unsigned int snet_verdict_delay;
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+int snet_nl_send_event(struct snet_info *info);
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+			   u32 flags, u8 protocol, enum snet_syscall syscall);
+
+void snet_netlink_exit(void);
+
+#endif /* _SNET_NETLINK_H */
diff --git a/security/snet/snet_netlink_helper.c b/security/snet/snet_netlink_helper.c
new file mode 100644
index 0000000..d7d743c
--- /dev/null
+++ b/security/snet/snet_netlink_helper.c
@@ -0,0 +1,220 @@
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+
+static int fill_src(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	switch (info->family) {
+	case PF_INET:
+		ret = nla_put(skb_rsp, SNET_A_IPV4SADDR,
+			      sizeof(struct in_addr), &(info->src.u3.ip));
+		if (ret != 0)
+			goto out;
+		break;
+	case PF_INET6:
+		ret = nla_put(skb_rsp, SNET_A_IPV6SADDR,
+			      sizeof(struct in6_addr), &(info->src.u3.ip6));
+		if (ret != 0)
+			goto out;
+		break;
+	default:
+		break;
+	}
+	ret = nla_put_u16(skb_rsp, SNET_A_SPORT, info->src.u.port);
+out:
+	return ret;
+
+}
+
+static int fill_dst(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	switch (info->family) {
+	case PF_INET:
+		ret = nla_put(skb_rsp, SNET_A_IPV4DADDR,
+			      sizeof(struct in_addr), &(info->dst.u3.ip));
+		if (ret != 0)
+			goto out;
+		break;
+	case PF_INET6:
+		ret = nla_put(skb_rsp, SNET_A_IPV6DADDR,
+			      sizeof(struct in6_addr), &(info->dst.u3.ip6));
+		if (ret != 0)
+			goto out;
+		break;
+	default:
+		break;
+	}
+	ret = nla_put_u16(skb_rsp, SNET_A_DPORT, info->dst.u.port);
+out:
+	return ret;
+}
+
+static int snet_fill_create(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	ret = nla_put_u8(skb_rsp, SNET_A_TYPE, info->type);
+	return ret;
+}
+
+static int snet_fill_bind(struct sk_buff *skb_rsp,  struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_connect(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_listen(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_accept(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_post_accept(struct sk_buff *skb_rsp,
+				 struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_sendmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_recvmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_sock_rcv_skb(struct sk_buff *skb_rsp,
+				  struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_close(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	static int (*snet_df[])(struct sk_buff *, struct snet_info *) = {
+		[SNET_SOCKET_CREATE]		= &snet_fill_create,
+		[SNET_SOCKET_BIND]		= &snet_fill_bind,
+		[SNET_SOCKET_CONNECT]		= &snet_fill_connect,
+		[SNET_SOCKET_LISTEN]		= &snet_fill_listen,
+		[SNET_SOCKET_ACCEPT]		= &snet_fill_accept,
+		[SNET_SOCKET_POST_ACCEPT]	= &snet_fill_post_accept,
+		[SNET_SOCKET_SENDMSG]		= &snet_fill_sendmsg,
+		[SNET_SOCKET_RECVMSG]		= &snet_fill_recvmsg,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= &snet_fill_sock_rcv_skb,
+		[SNET_SOCKET_CLOSE]		= &snet_fill_close,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return -EINVAL;
+	else
+		return snet_df[info->syscall](skb_rsp, info);
+}
+
+int snet_nl_size_by_syscall(struct snet_info *info)
+{
+	unsigned int size_addr4_port = nla_total_size(sizeof(struct in_addr)) +
+				       nla_total_size(sizeof(u16));
+	unsigned int size_addr6_port = nla_total_size(sizeof(struct in6_addr)) +
+				       nla_total_size(sizeof(u16));
+
+	unsigned int sbs4[] = {
+		[SNET_SOCKET_CREATE]		= nla_total_size(sizeof(u8)),
+		[SNET_SOCKET_BIND]		= size_addr4_port,
+		[SNET_SOCKET_CONNECT]		= 2 * size_addr4_port,
+		[SNET_SOCKET_LISTEN]		= size_addr4_port,
+		[SNET_SOCKET_ACCEPT]		= size_addr4_port,
+		[SNET_SOCKET_POST_ACCEPT]	= 2 * size_addr4_port,
+		[SNET_SOCKET_SENDMSG]		= 2 * size_addr4_port,
+		[SNET_SOCKET_RECVMSG]		= 2 * size_addr4_port,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= 2 * size_addr4_port,
+		[SNET_SOCKET_CLOSE]		= 2 * size_addr4_port,
+	};
+
+	unsigned int sbs6[] = {
+		[SNET_SOCKET_CREATE]		= nla_total_size(sizeof(u8)),
+		[SNET_SOCKET_BIND]		= size_addr6_port,
+		[SNET_SOCKET_CONNECT]		= 2 * size_addr6_port,
+		[SNET_SOCKET_LISTEN]		= size_addr6_port,
+		[SNET_SOCKET_ACCEPT]		= size_addr6_port,
+		[SNET_SOCKET_POST_ACCEPT]	= 2 * size_addr6_port,
+		[SNET_SOCKET_SENDMSG]		= 2 * size_addr6_port,
+		[SNET_SOCKET_RECVMSG]		= 2 * size_addr6_port,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= 2 * size_addr6_port,
+		[SNET_SOCKET_CLOSE]		= 2 * size_addr6_port,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return -EINVAL;
+	else {
+		switch (info->family) {
+		case AF_INET:
+			return sbs4[info->syscall];
+			break;
+		case AF_INET6:
+			return sbs6[info->syscall];
+			break;
+		default:
+			return -EINVAL;
+			break;
+		}
+	}
+}
diff --git a/security/snet/snet_netlink_helper.h b/security/snet/snet_netlink_helper.h
new file mode 100644
index 0000000..d12e563
--- /dev/null
+++ b/security/snet/snet_netlink_helper.h
@@ -0,0 +1,7 @@
+#ifndef _SNET_NETLINK_HELPER_H
+#define _SNET_NETLINK_HELPER_H
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info);
+int snet_nl_size_by_syscall(struct snet_info *info);
+
+#endif /* SNET_NETLINK_HELPER_H */
-- 
1.7.4.1


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

* [RFC v4 06/11] snet: introduce snet_netlink
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (21 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (20 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds the snet communication's subsystem.

snet_netlink is using genetlink for sending/receiving messages to/from userspace.
the genetlink operations permit to receive orders to manage the list of events
- events are values [syscall, protocol] - which is used to know which syscall
and protocol have to be protected. genl operations are also used to manage
communication of events to userspace, and to receive the related verdict

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_netlink.c        |  442 +++++++++++++++++++++++++++++++++++
 security/snet/snet_netlink.h        |   17 ++
 security/snet/snet_netlink_helper.c |  220 +++++++++++++++++
 security/snet/snet_netlink_helper.h |    7 +
 4 files changed, 686 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_netlink.c
 create mode 100644 security/snet/snet_netlink.h
 create mode 100644 security/snet/snet_netlink_helper.c
 create mode 100644 security/snet/snet_netlink_helper.h

diff --git a/security/snet/snet_netlink.c b/security/snet/snet_netlink.c
new file mode 100644
index 0000000..0d6cd8b
--- /dev/null
+++ b/security/snet/snet_netlink.c
@@ -0,0 +1,442 @@
+#include <linux/sched.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+#include "snet_netlink.h"
+#include "snet_netlink_helper.h"
+#include "snet_verdict.h"
+#include "snet_event.h"
+#include "snet_utils.h"
+
+atomic_t snet_nl_seq = ATOMIC_INIT(0);
+uint32_t snet_nl_pid;
+static struct genl_family snet_genl_family;
+
+/*
+ * snet genetlink
+ */
+int snet_nl_send_event(struct snet_info *info)
+{
+	struct sk_buff *skb_rsp;
+	void *msg_head;
+	int ret = 0, sbs = -1;
+	size_t size = 0;
+
+	sbs = snet_nl_size_by_syscall(info);
+	if (sbs < 0)
+		return -EINVAL;
+
+	size =  sbs +
+		2 * nla_total_size(sizeof(u8)) +
+		1 * nla_total_size(sizeof(u16)) +
+		(info->verdict_id ? 3 : 2) * nla_total_size(sizeof(u32));
+
+	skb_rsp = genlmsg_new(size, GFP_KERNEL);
+	if (skb_rsp == NULL)
+		return -ENOMEM;
+
+	msg_head = genlmsg_put(skb_rsp, snet_nl_pid,
+			       atomic_inc_return(&snet_nl_seq),
+			       &snet_genl_family, 0, SNET_C_VERDICT);
+	if (msg_head == NULL)
+		goto nla_put_failure;
+
+	pr_debug("verdict_id=0x%x syscall=%s protocol=%u "
+		 "family=%u uid=%u pid=%u\n",
+		 info->verdict_id, snet_syscall_name(info->syscall),
+		 info->protocol, info->family, current_uid(), current->pid);
+
+	if (info->verdict_id)
+		NLA_PUT_U32(skb_rsp, SNET_A_VERDICT_ID, info->verdict_id);
+	NLA_PUT_U16(skb_rsp, SNET_A_SYSCALL, info->syscall);
+	NLA_PUT_U8(skb_rsp, SNET_A_PROTOCOL, info->protocol);
+	NLA_PUT_U8(skb_rsp, SNET_A_FAMILY, info->family);
+	NLA_PUT_U32(skb_rsp, SNET_A_UID, current_uid());
+	NLA_PUT_U32(skb_rsp, SNET_A_PID, current->pid);
+
+	ret = snet_nl_fill_by_syscall(skb_rsp, info);
+	if (ret != 0)
+		goto nla_put_failure;
+
+	ret = genlmsg_end(skb_rsp, msg_head);
+	if (ret < 0)
+		goto nla_put_failure;
+
+	return genlmsg_unicast(&init_net, skb_rsp, snet_nl_pid);
+
+nla_put_failure:
+	kfree_skb(skb_rsp);
+	return -ECONNABORTED;
+}
+
+/*
+ * snet genetlink functions
+ */
+
+static struct genl_family snet_genl_family = {
+	.id		= GENL_ID_GENERATE,
+	.hdrsize	= 0,
+	.name		= SNET_GENL_NAME,
+	.version	= SNET_GENL_VERSION,
+	.maxattr	= SNET_A_MAX,
+};
+
+static const struct nla_policy snet_genl_policy[SNET_A_MAX + 1] = {
+	[SNET_A_VERSION]		= { .type = NLA_U32 },
+	[SNET_A_VERDICT_ID]		= { .type = NLA_U32 },
+	[SNET_A_FAMILY]			= { .type = NLA_U8 },
+	[SNET_A_SYSCALL]		= { .type = NLA_U16 },
+	[SNET_A_PROTOCOL]		= { .type = NLA_U8 },
+	[SNET_A_UID]			= { .type = NLA_U32 },
+	[SNET_A_PID]			= { .type = NLA_U32 },
+	[SNET_A_TYPE]			= { .type = NLA_U32 },
+	[SNET_A_IPV4SADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in_addr) },
+	[SNET_A_IPV6SADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in6_addr) },
+	[SNET_A_IPV4DADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in_addr) },
+	[SNET_A_IPV6DADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in6_addr) },
+	[SNET_A_SPORT]			= { .type = NLA_U16 },
+	[SNET_A_DPORT]			= { .type = NLA_U16 },
+	[SNET_A_VERDICT]		= { .type = NLA_U8 },
+	[SNET_A_VERDICT_DELAY]		= { .type = NLA_U32 },
+	[SNET_A_TICKET_DELAY]		= { .type = NLA_U32 },
+	[SNET_A_TICKET_MODE]		= { .type = NLA_U8 },
+};
+
+/**
+ * snet_nl_version - Handle a VERSION message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Process a user generated VERSION message and respond accordingly.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_version(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	struct sk_buff *skb_rsp = NULL;
+	void *msg_head;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (skb_rsp == NULL)
+		return -ENOMEM;
+	msg_head = genlmsg_put_reply(skb_rsp, info, &snet_genl_family,
+				     0, SNET_C_VERSION);
+	if (msg_head == NULL)
+		goto nla_put_failure;
+
+	NLA_PUT_U32(skb_rsp, SNET_A_VERSION, SNET_VERSION);
+
+	ret = genlmsg_end(skb_rsp, msg_head);
+	if (ret < 0)
+		goto nla_put_failure;
+
+	ret = genlmsg_reply(skb_rsp, info);
+	if (ret != 0)
+		goto nla_put_failure;
+	return 0;
+
+nla_put_failure:
+	kfree_skb(skb_rsp);
+	return ret;
+}
+
+/**
+ * snet_nl_register - Handle a REGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that an application is listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_register(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	u32 version = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_VERSION]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	version = nla_get_u32(info->attrs[SNET_A_VERSION]);
+
+	if (version != SNET_VERSION) {
+		ret = -EPERM;
+		goto out;
+	}
+
+	if (snet_nl_pid > 0) {
+		ret = -ECONNREFUSED;
+		goto out;
+	}
+	snet_nl_pid = info->snd_pid;
+	pr_debug("pid=%u\n", snet_nl_pid);
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_unregister - Handle a UNREGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that the application is no more listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_unregister(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (snet_nl_pid == 0) {
+		ret = -ENOTCONN;
+		goto out;
+	}
+
+	snet_nl_pid = 0;
+	pr_debug("pid=%u\n", snet_nl_pid);
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_insert - Handle a INSERT message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Insert a new event to the events' hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_insert(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	enum snet_syscall syscall;
+	u8 protocol;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+		ret =  -EINVAL;
+		goto out;
+	}
+	syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+	protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+	ret = snet_event_insert(syscall, protocol);
+	pr_debug("syscall=%s protocol=%u insert=%s\n",
+		 snet_syscall_name(syscall), protocol,
+		 (ret == 0) ? "success" : "failed");
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_remove - Handle a REMOVE message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove a event from the events' hastable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_remove(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	enum snet_syscall syscall;
+	u8 protocol;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+	protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+	ret = snet_event_remove(syscall, protocol);
+	pr_debug("syscall=%s protocol=%u remove=%s\n",
+		 snet_syscall_name(syscall), protocol,
+		 (ret == 0) ? "success" : "failed");
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_flush - Handle a FLUSH message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove all events from the hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_flush(struct sk_buff *skb, struct genl_info *info)
+{
+	atomic_set(&snet_nl_seq, info->snd_seq);
+	snet_event_flush();
+	return 0;
+}
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+			   u32 flags, u8 protocol, enum snet_syscall syscall)
+{
+	void *hdr;
+
+	hdr = genlmsg_put(skb, pid, seq, &snet_genl_family, flags, SNET_C_LIST);
+	if (hdr == NULL)
+		return -1;
+
+	NLA_PUT_U16(skb, SNET_A_SYSCALL, syscall);
+	NLA_PUT_U8(skb, SNET_A_PROTOCOL, protocol);
+
+	return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+	genlmsg_cancel(skb, hdr);
+	return -EMSGSIZE;
+}
+/**
+ * snet_nl_list - Handle a LIST message
+ * @skb: the NETLINK buffer
+ * @cb:
+ *
+ * Description:
+ * Process a user LIST message and respond. Returns zero on success,
+ * and negative values on error.
+ */
+static int snet_nl_list(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	unsigned int len = 0;
+
+	atomic_set(&snet_nl_seq, cb->nlh->nlmsg_seq);
+	len = snet_event_fill_info(skb, cb);
+	return len;
+}
+
+/**
+ * snet_nl_verdict - Handle a VERDICT message
+ * @skb: the NETLINK buffer
+ * @info the Generic NETLINK info block
+ *
+ * Description:
+ * Provides userspace with a VERDICT message, ie we are sending informations
+ * with this command. Userspace is sending the appropriate verdict for the
+ * event. Returns zero on success,and negative values on error.
+ */
+static int snet_nl_verdict(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	u32 verdict_id;
+	enum snet_verdict verdict;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (snet_nl_pid == 0) {
+		ret = -ENOTCONN;
+		goto out;
+	}
+
+	if (!info->attrs[SNET_A_VERDICT_ID] || !info->attrs[SNET_A_VERDICT]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	verdict_id = nla_get_u32(info->attrs[SNET_A_VERDICT_ID]);
+	verdict = nla_get_u8(info->attrs[SNET_A_VERDICT]);
+	ret = snet_verdict_set(verdict_id, verdict);
+out:
+	return ret;
+}
+
+static int snet_nl_config(struct sk_buff *skb,
+			  struct genl_info *info)
+{
+	int ret = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (info->attrs[SNET_A_VERDICT_DELAY]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_VERDICT_DELAY]);
+		if (new == 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_verdict_delay = new;
+		pr_debug("snet_nl_config: verdict_delay=%u\n", snet_verdict_delay);
+	}
+	if (info->attrs[SNET_A_TICKET_DELAY]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_TICKET_DELAY]);
+		if (new == 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_ticket_delay = new;
+		pr_debug("snet_nl_config: ticket_delay=%u\n", snet_ticket_delay);
+	}
+	if (info->attrs[SNET_A_TICKET_MODE]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_TICKET_MODE]);
+		if (new >= SNET_TICKET_INVALID) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_ticket_mode = new;
+		pr_debug("snet_nl_config: ticket_mode=%u\n", snet_ticket_mode);
+	}
+out:
+	return ret;
+}
+
+#define SNET_GENL_OPS(_cmd, _flags, _policy, _op, _opname)	\
+	{							\
+		.cmd	= _cmd,					\
+		.flags	= _flags,				\
+		.policy	= _policy,				\
+		._op	= snet_nl_##_opname,			\
+	}
+
+static struct genl_ops snet_genl_ops[] = {
+	SNET_GENL_OPS(SNET_C_VERSION, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, version),
+	SNET_GENL_OPS(SNET_C_REGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, register),
+	SNET_GENL_OPS(SNET_C_UNREGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, unregister),
+	SNET_GENL_OPS(SNET_C_INSERT, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, insert),
+	SNET_GENL_OPS(SNET_C_REMOVE, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, remove),
+	SNET_GENL_OPS(SNET_C_FLUSH, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, flush),
+	SNET_GENL_OPS(SNET_C_LIST, GENL_ADMIN_PERM, snet_genl_policy,
+		      dumpit, list),
+	SNET_GENL_OPS(SNET_C_VERDICT, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, verdict),
+	SNET_GENL_OPS(SNET_C_CONFIG, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, config),
+};
+
+#undef SNET_GENL_OPS
+
+static __init int snet_netlink_init(void)
+{
+	return genl_register_family_with_ops(&snet_genl_family,
+					     snet_genl_ops,
+					     ARRAY_SIZE(snet_genl_ops));
+}
+
+void snet_netlink_exit(void)
+{
+	genl_unregister_family(&snet_genl_family);
+}
+
+__initcall(snet_netlink_init);
diff --git a/security/snet/snet_netlink.h b/security/snet/snet_netlink.h
new file mode 100644
index 0000000..5e80d7b
--- /dev/null
+++ b/security/snet/snet_netlink.h
@@ -0,0 +1,17 @@
+#ifndef _SNET_NETLINK_H
+#define _SNET_NETLINK_H
+
+#include <linux/in6.h>
+
+extern unsigned int snet_verdict_delay;
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+int snet_nl_send_event(struct snet_info *info);
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+			   u32 flags, u8 protocol, enum snet_syscall syscall);
+
+void snet_netlink_exit(void);
+
+#endif /* _SNET_NETLINK_H */
diff --git a/security/snet/snet_netlink_helper.c b/security/snet/snet_netlink_helper.c
new file mode 100644
index 0000000..d7d743c
--- /dev/null
+++ b/security/snet/snet_netlink_helper.c
@@ -0,0 +1,220 @@
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+
+static int fill_src(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	switch (info->family) {
+	case PF_INET:
+		ret = nla_put(skb_rsp, SNET_A_IPV4SADDR,
+			      sizeof(struct in_addr), &(info->src.u3.ip));
+		if (ret != 0)
+			goto out;
+		break;
+	case PF_INET6:
+		ret = nla_put(skb_rsp, SNET_A_IPV6SADDR,
+			      sizeof(struct in6_addr), &(info->src.u3.ip6));
+		if (ret != 0)
+			goto out;
+		break;
+	default:
+		break;
+	}
+	ret = nla_put_u16(skb_rsp, SNET_A_SPORT, info->src.u.port);
+out:
+	return ret;
+
+}
+
+static int fill_dst(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	switch (info->family) {
+	case PF_INET:
+		ret = nla_put(skb_rsp, SNET_A_IPV4DADDR,
+			      sizeof(struct in_addr), &(info->dst.u3.ip));
+		if (ret != 0)
+			goto out;
+		break;
+	case PF_INET6:
+		ret = nla_put(skb_rsp, SNET_A_IPV6DADDR,
+			      sizeof(struct in6_addr), &(info->dst.u3.ip6));
+		if (ret != 0)
+			goto out;
+		break;
+	default:
+		break;
+	}
+	ret = nla_put_u16(skb_rsp, SNET_A_DPORT, info->dst.u.port);
+out:
+	return ret;
+}
+
+static int snet_fill_create(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	ret = nla_put_u8(skb_rsp, SNET_A_TYPE, info->type);
+	return ret;
+}
+
+static int snet_fill_bind(struct sk_buff *skb_rsp,  struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_connect(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_listen(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_accept(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_post_accept(struct sk_buff *skb_rsp,
+				 struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_sendmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_recvmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_sock_rcv_skb(struct sk_buff *skb_rsp,
+				  struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_close(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	static int (*snet_df[])(struct sk_buff *, struct snet_info *) = {
+		[SNET_SOCKET_CREATE]		= &snet_fill_create,
+		[SNET_SOCKET_BIND]		= &snet_fill_bind,
+		[SNET_SOCKET_CONNECT]		= &snet_fill_connect,
+		[SNET_SOCKET_LISTEN]		= &snet_fill_listen,
+		[SNET_SOCKET_ACCEPT]		= &snet_fill_accept,
+		[SNET_SOCKET_POST_ACCEPT]	= &snet_fill_post_accept,
+		[SNET_SOCKET_SENDMSG]		= &snet_fill_sendmsg,
+		[SNET_SOCKET_RECVMSG]		= &snet_fill_recvmsg,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= &snet_fill_sock_rcv_skb,
+		[SNET_SOCKET_CLOSE]		= &snet_fill_close,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return -EINVAL;
+	else
+		return snet_df[info->syscall](skb_rsp, info);
+}
+
+int snet_nl_size_by_syscall(struct snet_info *info)
+{
+	unsigned int size_addr4_port = nla_total_size(sizeof(struct in_addr)) +
+				       nla_total_size(sizeof(u16));
+	unsigned int size_addr6_port = nla_total_size(sizeof(struct in6_addr)) +
+				       nla_total_size(sizeof(u16));
+
+	unsigned int sbs4[] = {
+		[SNET_SOCKET_CREATE]		= nla_total_size(sizeof(u8)),
+		[SNET_SOCKET_BIND]		= size_addr4_port,
+		[SNET_SOCKET_CONNECT]		= 2 * size_addr4_port,
+		[SNET_SOCKET_LISTEN]		= size_addr4_port,
+		[SNET_SOCKET_ACCEPT]		= size_addr4_port,
+		[SNET_SOCKET_POST_ACCEPT]	= 2 * size_addr4_port,
+		[SNET_SOCKET_SENDMSG]		= 2 * size_addr4_port,
+		[SNET_SOCKET_RECVMSG]		= 2 * size_addr4_port,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= 2 * size_addr4_port,
+		[SNET_SOCKET_CLOSE]		= 2 * size_addr4_port,
+	};
+
+	unsigned int sbs6[] = {
+		[SNET_SOCKET_CREATE]		= nla_total_size(sizeof(u8)),
+		[SNET_SOCKET_BIND]		= size_addr6_port,
+		[SNET_SOCKET_CONNECT]		= 2 * size_addr6_port,
+		[SNET_SOCKET_LISTEN]		= size_addr6_port,
+		[SNET_SOCKET_ACCEPT]		= size_addr6_port,
+		[SNET_SOCKET_POST_ACCEPT]	= 2 * size_addr6_port,
+		[SNET_SOCKET_SENDMSG]		= 2 * size_addr6_port,
+		[SNET_SOCKET_RECVMSG]		= 2 * size_addr6_port,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= 2 * size_addr6_port,
+		[SNET_SOCKET_CLOSE]		= 2 * size_addr6_port,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return -EINVAL;
+	else {
+		switch (info->family) {
+		case AF_INET:
+			return sbs4[info->syscall];
+			break;
+		case AF_INET6:
+			return sbs6[info->syscall];
+			break;
+		default:
+			return -EINVAL;
+			break;
+		}
+	}
+}
diff --git a/security/snet/snet_netlink_helper.h b/security/snet/snet_netlink_helper.h
new file mode 100644
index 0000000..d12e563
--- /dev/null
+++ b/security/snet/snet_netlink_helper.h
@@ -0,0 +1,7 @@
+#ifndef _SNET_NETLINK_HELPER_H
+#define _SNET_NETLINK_HELPER_H
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info);
+int snet_nl_size_by_syscall(struct snet_info *info);
+
+#endif /* SNET_NETLINK_HELPER_H */
-- 
1.7.4.1


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

* [RFC v4 06/11] snet: introduce snet_netlink
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (19 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (22 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds the snet communication's subsystem.

snet_netlink is using genetlink for sending/receiving messages to/from userspace.
the genetlink operations permit to receive orders to manage the list of events
- events are values [syscall, protocol] - which is used to know which syscall
and protocol have to be protected. genl operations are also used to manage
communication of events to userspace, and to receive the related verdict

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_netlink.c        |  442 +++++++++++++++++++++++++++++++++++
 security/snet/snet_netlink.h        |   17 ++
 security/snet/snet_netlink_helper.c |  220 +++++++++++++++++
 security/snet/snet_netlink_helper.h |    7 +
 4 files changed, 686 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_netlink.c
 create mode 100644 security/snet/snet_netlink.h
 create mode 100644 security/snet/snet_netlink_helper.c
 create mode 100644 security/snet/snet_netlink_helper.h

diff --git a/security/snet/snet_netlink.c b/security/snet/snet_netlink.c
new file mode 100644
index 0000000..0d6cd8b
--- /dev/null
+++ b/security/snet/snet_netlink.c
@@ -0,0 +1,442 @@
+#include <linux/sched.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+#include "snet_netlink.h"
+#include "snet_netlink_helper.h"
+#include "snet_verdict.h"
+#include "snet_event.h"
+#include "snet_utils.h"
+
+atomic_t snet_nl_seq = ATOMIC_INIT(0);
+uint32_t snet_nl_pid;
+static struct genl_family snet_genl_family;
+
+/*
+ * snet genetlink
+ */
+int snet_nl_send_event(struct snet_info *info)
+{
+	struct sk_buff *skb_rsp;
+	void *msg_head;
+	int ret = 0, sbs = -1;
+	size_t size = 0;
+
+	sbs = snet_nl_size_by_syscall(info);
+	if (sbs < 0)
+		return -EINVAL;
+
+	size =  sbs +
+		2 * nla_total_size(sizeof(u8)) +
+		1 * nla_total_size(sizeof(u16)) +
+		(info->verdict_id ? 3 : 2) * nla_total_size(sizeof(u32));
+
+	skb_rsp = genlmsg_new(size, GFP_KERNEL);
+	if (skb_rsp == NULL)
+		return -ENOMEM;
+
+	msg_head = genlmsg_put(skb_rsp, snet_nl_pid,
+			       atomic_inc_return(&snet_nl_seq),
+			       &snet_genl_family, 0, SNET_C_VERDICT);
+	if (msg_head == NULL)
+		goto nla_put_failure;
+
+	pr_debug("verdict_id=0x%x syscall=%s protocol=%u "
+		 "family=%u uid=%u pid=%u\n",
+		 info->verdict_id, snet_syscall_name(info->syscall),
+		 info->protocol, info->family, current_uid(), current->pid);
+
+	if (info->verdict_id)
+		NLA_PUT_U32(skb_rsp, SNET_A_VERDICT_ID, info->verdict_id);
+	NLA_PUT_U16(skb_rsp, SNET_A_SYSCALL, info->syscall);
+	NLA_PUT_U8(skb_rsp, SNET_A_PROTOCOL, info->protocol);
+	NLA_PUT_U8(skb_rsp, SNET_A_FAMILY, info->family);
+	NLA_PUT_U32(skb_rsp, SNET_A_UID, current_uid());
+	NLA_PUT_U32(skb_rsp, SNET_A_PID, current->pid);
+
+	ret = snet_nl_fill_by_syscall(skb_rsp, info);
+	if (ret != 0)
+		goto nla_put_failure;
+
+	ret = genlmsg_end(skb_rsp, msg_head);
+	if (ret < 0)
+		goto nla_put_failure;
+
+	return genlmsg_unicast(&init_net, skb_rsp, snet_nl_pid);
+
+nla_put_failure:
+	kfree_skb(skb_rsp);
+	return -ECONNABORTED;
+}
+
+/*
+ * snet genetlink functions
+ */
+
+static struct genl_family snet_genl_family = {
+	.id		= GENL_ID_GENERATE,
+	.hdrsize	= 0,
+	.name		= SNET_GENL_NAME,
+	.version	= SNET_GENL_VERSION,
+	.maxattr	= SNET_A_MAX,
+};
+
+static const struct nla_policy snet_genl_policy[SNET_A_MAX + 1] = {
+	[SNET_A_VERSION]		= { .type = NLA_U32 },
+	[SNET_A_VERDICT_ID]		= { .type = NLA_U32 },
+	[SNET_A_FAMILY]			= { .type = NLA_U8 },
+	[SNET_A_SYSCALL]		= { .type = NLA_U16 },
+	[SNET_A_PROTOCOL]		= { .type = NLA_U8 },
+	[SNET_A_UID]			= { .type = NLA_U32 },
+	[SNET_A_PID]			= { .type = NLA_U32 },
+	[SNET_A_TYPE]			= { .type = NLA_U32 },
+	[SNET_A_IPV4SADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in_addr) },
+	[SNET_A_IPV6SADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in6_addr) },
+	[SNET_A_IPV4DADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in_addr) },
+	[SNET_A_IPV6DADDR]		= { .type = NLA_BINARY,
+					    .len = sizeof(struct in6_addr) },
+	[SNET_A_SPORT]			= { .type = NLA_U16 },
+	[SNET_A_DPORT]			= { .type = NLA_U16 },
+	[SNET_A_VERDICT]		= { .type = NLA_U8 },
+	[SNET_A_VERDICT_DELAY]		= { .type = NLA_U32 },
+	[SNET_A_TICKET_DELAY]		= { .type = NLA_U32 },
+	[SNET_A_TICKET_MODE]		= { .type = NLA_U8 },
+};
+
+/**
+ * snet_nl_version - Handle a VERSION message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Process a user generated VERSION message and respond accordingly.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_version(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	struct sk_buff *skb_rsp = NULL;
+	void *msg_head;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (skb_rsp == NULL)
+		return -ENOMEM;
+	msg_head = genlmsg_put_reply(skb_rsp, info, &snet_genl_family,
+				     0, SNET_C_VERSION);
+	if (msg_head == NULL)
+		goto nla_put_failure;
+
+	NLA_PUT_U32(skb_rsp, SNET_A_VERSION, SNET_VERSION);
+
+	ret = genlmsg_end(skb_rsp, msg_head);
+	if (ret < 0)
+		goto nla_put_failure;
+
+	ret = genlmsg_reply(skb_rsp, info);
+	if (ret != 0)
+		goto nla_put_failure;
+	return 0;
+
+nla_put_failure:
+	kfree_skb(skb_rsp);
+	return ret;
+}
+
+/**
+ * snet_nl_register - Handle a REGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that an application is listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_register(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	u32 version = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_VERSION]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	version = nla_get_u32(info->attrs[SNET_A_VERSION]);
+
+	if (version != SNET_VERSION) {
+		ret = -EPERM;
+		goto out;
+	}
+
+	if (snet_nl_pid > 0) {
+		ret = -ECONNREFUSED;
+		goto out;
+	}
+	snet_nl_pid = info->snd_pid;
+	pr_debug("pid=%u\n", snet_nl_pid);
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_unregister - Handle a UNREGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that the application is no more listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_unregister(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (snet_nl_pid == 0) {
+		ret = -ENOTCONN;
+		goto out;
+	}
+
+	snet_nl_pid = 0;
+	pr_debug("pid=%u\n", snet_nl_pid);
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_insert - Handle a INSERT message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Insert a new event to the events' hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_insert(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	enum snet_syscall syscall;
+	u8 protocol;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+		ret =  -EINVAL;
+		goto out;
+	}
+	syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+	protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+	ret = snet_event_insert(syscall, protocol);
+	pr_debug("syscall=%s protocol=%u insert=%s\n",
+		 snet_syscall_name(syscall), protocol,
+		 (ret == 0) ? "success" : "failed");
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_remove - Handle a REMOVE message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove a event from the events' hastable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_remove(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	enum snet_syscall syscall;
+	u8 protocol;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+	protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+	ret = snet_event_remove(syscall, protocol);
+	pr_debug("syscall=%s protocol=%u remove=%s\n",
+		 snet_syscall_name(syscall), protocol,
+		 (ret == 0) ? "success" : "failed");
+out:
+	return ret;
+}
+
+/**
+ * snet_nl_flush - Handle a FLUSH message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove all events from the hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_flush(struct sk_buff *skb, struct genl_info *info)
+{
+	atomic_set(&snet_nl_seq, info->snd_seq);
+	snet_event_flush();
+	return 0;
+}
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+			   u32 flags, u8 protocol, enum snet_syscall syscall)
+{
+	void *hdr;
+
+	hdr = genlmsg_put(skb, pid, seq, &snet_genl_family, flags, SNET_C_LIST);
+	if (hdr == NULL)
+		return -1;
+
+	NLA_PUT_U16(skb, SNET_A_SYSCALL, syscall);
+	NLA_PUT_U8(skb, SNET_A_PROTOCOL, protocol);
+
+	return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+	genlmsg_cancel(skb, hdr);
+	return -EMSGSIZE;
+}
+/**
+ * snet_nl_list - Handle a LIST message
+ * @skb: the NETLINK buffer
+ * @cb:
+ *
+ * Description:
+ * Process a user LIST message and respond. Returns zero on success,
+ * and negative values on error.
+ */
+static int snet_nl_list(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	unsigned int len = 0;
+
+	atomic_set(&snet_nl_seq, cb->nlh->nlmsg_seq);
+	len = snet_event_fill_info(skb, cb);
+	return len;
+}
+
+/**
+ * snet_nl_verdict - Handle a VERDICT message
+ * @skb: the NETLINK buffer
+ * @info the Generic NETLINK info block
+ *
+ * Description:
+ * Provides userspace with a VERDICT message, ie we are sending informations
+ * with this command. Userspace is sending the appropriate verdict for the
+ * event. Returns zero on success,and negative values on error.
+ */
+static int snet_nl_verdict(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	u32 verdict_id;
+	enum snet_verdict verdict;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (snet_nl_pid == 0) {
+		ret = -ENOTCONN;
+		goto out;
+	}
+
+	if (!info->attrs[SNET_A_VERDICT_ID] || !info->attrs[SNET_A_VERDICT]) {
+		ret = -EINVAL;
+		goto out;
+	}
+	verdict_id = nla_get_u32(info->attrs[SNET_A_VERDICT_ID]);
+	verdict = nla_get_u8(info->attrs[SNET_A_VERDICT]);
+	ret = snet_verdict_set(verdict_id, verdict);
+out:
+	return ret;
+}
+
+static int snet_nl_config(struct sk_buff *skb,
+			  struct genl_info *info)
+{
+	int ret = 0;
+
+	atomic_set(&snet_nl_seq, info->snd_seq);
+
+	if (info->attrs[SNET_A_VERDICT_DELAY]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_VERDICT_DELAY]);
+		if (new == 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_verdict_delay = new;
+		pr_debug("snet_nl_config: verdict_delay=%u\n", snet_verdict_delay);
+	}
+	if (info->attrs[SNET_A_TICKET_DELAY]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_TICKET_DELAY]);
+		if (new == 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_ticket_delay = new;
+		pr_debug("snet_nl_config: ticket_delay=%u\n", snet_ticket_delay);
+	}
+	if (info->attrs[SNET_A_TICKET_MODE]) {
+		unsigned int new = nla_get_u32(info->attrs[SNET_A_TICKET_MODE]);
+		if (new >= SNET_TICKET_INVALID) {
+			ret = -EINVAL;
+			goto out;
+		}
+		snet_ticket_mode = new;
+		pr_debug("snet_nl_config: ticket_mode=%u\n", snet_ticket_mode);
+	}
+out:
+	return ret;
+}
+
+#define SNET_GENL_OPS(_cmd, _flags, _policy, _op, _opname)	\
+	{							\
+		.cmd	= _cmd,					\
+		.flags	= _flags,				\
+		.policy	= _policy,				\
+		._op	= snet_nl_##_opname,			\
+	}
+
+static struct genl_ops snet_genl_ops[] = {
+	SNET_GENL_OPS(SNET_C_VERSION, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, version),
+	SNET_GENL_OPS(SNET_C_REGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, register),
+	SNET_GENL_OPS(SNET_C_UNREGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, unregister),
+	SNET_GENL_OPS(SNET_C_INSERT, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, insert),
+	SNET_GENL_OPS(SNET_C_REMOVE, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, remove),
+	SNET_GENL_OPS(SNET_C_FLUSH, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, flush),
+	SNET_GENL_OPS(SNET_C_LIST, GENL_ADMIN_PERM, snet_genl_policy,
+		      dumpit, list),
+	SNET_GENL_OPS(SNET_C_VERDICT, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, verdict),
+	SNET_GENL_OPS(SNET_C_CONFIG, GENL_ADMIN_PERM, snet_genl_policy,
+		      doit, config),
+};
+
+#undef SNET_GENL_OPS
+
+static __init int snet_netlink_init(void)
+{
+	return genl_register_family_with_ops(&snet_genl_family,
+					     snet_genl_ops,
+					     ARRAY_SIZE(snet_genl_ops));
+}
+
+void snet_netlink_exit(void)
+{
+	genl_unregister_family(&snet_genl_family);
+}
+
+__initcall(snet_netlink_init);
diff --git a/security/snet/snet_netlink.h b/security/snet/snet_netlink.h
new file mode 100644
index 0000000..5e80d7b
--- /dev/null
+++ b/security/snet/snet_netlink.h
@@ -0,0 +1,17 @@
+#ifndef _SNET_NETLINK_H
+#define _SNET_NETLINK_H
+
+#include <linux/in6.h>
+
+extern unsigned int snet_verdict_delay;
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+int snet_nl_send_event(struct snet_info *info);
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+			   u32 flags, u8 protocol, enum snet_syscall syscall);
+
+void snet_netlink_exit(void);
+
+#endif /* _SNET_NETLINK_H */
diff --git a/security/snet/snet_netlink_helper.c b/security/snet/snet_netlink_helper.c
new file mode 100644
index 0000000..d7d743c
--- /dev/null
+++ b/security/snet/snet_netlink_helper.c
@@ -0,0 +1,220 @@
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+
+static int fill_src(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	switch (info->family) {
+	case PF_INET:
+		ret = nla_put(skb_rsp, SNET_A_IPV4SADDR,
+			      sizeof(struct in_addr), &(info->src.u3.ip));
+		if (ret != 0)
+			goto out;
+		break;
+	case PF_INET6:
+		ret = nla_put(skb_rsp, SNET_A_IPV6SADDR,
+			      sizeof(struct in6_addr), &(info->src.u3.ip6));
+		if (ret != 0)
+			goto out;
+		break;
+	default:
+		break;
+	}
+	ret = nla_put_u16(skb_rsp, SNET_A_SPORT, info->src.u.port);
+out:
+	return ret;
+
+}
+
+static int fill_dst(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	switch (info->family) {
+	case PF_INET:
+		ret = nla_put(skb_rsp, SNET_A_IPV4DADDR,
+			      sizeof(struct in_addr), &(info->dst.u3.ip));
+		if (ret != 0)
+			goto out;
+		break;
+	case PF_INET6:
+		ret = nla_put(skb_rsp, SNET_A_IPV6DADDR,
+			      sizeof(struct in6_addr), &(info->dst.u3.ip6));
+		if (ret != 0)
+			goto out;
+		break;
+	default:
+		break;
+	}
+	ret = nla_put_u16(skb_rsp, SNET_A_DPORT, info->dst.u.port);
+out:
+	return ret;
+}
+
+static int snet_fill_create(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+	ret = nla_put_u8(skb_rsp, SNET_A_TYPE, info->type);
+	return ret;
+}
+
+static int snet_fill_bind(struct sk_buff *skb_rsp,  struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_connect(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_listen(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_accept(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_post_accept(struct sk_buff *skb_rsp,
+				 struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_sendmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_recvmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_sock_rcv_skb(struct sk_buff *skb_rsp,
+				  struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+static int snet_fill_close(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	int ret;
+
+	ret = fill_src(skb_rsp, info);
+	if (ret != 0)
+		goto out;
+	ret = fill_dst(skb_rsp, info);
+out:
+	return ret;
+}
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+	static int (*snet_df[])(struct sk_buff *, struct snet_info *) = {
+		[SNET_SOCKET_CREATE]		= &snet_fill_create,
+		[SNET_SOCKET_BIND]		= &snet_fill_bind,
+		[SNET_SOCKET_CONNECT]		= &snet_fill_connect,
+		[SNET_SOCKET_LISTEN]		= &snet_fill_listen,
+		[SNET_SOCKET_ACCEPT]		= &snet_fill_accept,
+		[SNET_SOCKET_POST_ACCEPT]	= &snet_fill_post_accept,
+		[SNET_SOCKET_SENDMSG]		= &snet_fill_sendmsg,
+		[SNET_SOCKET_RECVMSG]		= &snet_fill_recvmsg,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= &snet_fill_sock_rcv_skb,
+		[SNET_SOCKET_CLOSE]		= &snet_fill_close,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return -EINVAL;
+	else
+		return snet_df[info->syscall](skb_rsp, info);
+}
+
+int snet_nl_size_by_syscall(struct snet_info *info)
+{
+	unsigned int size_addr4_port = nla_total_size(sizeof(struct in_addr)) +
+				       nla_total_size(sizeof(u16));
+	unsigned int size_addr6_port = nla_total_size(sizeof(struct in6_addr)) +
+				       nla_total_size(sizeof(u16));
+
+	unsigned int sbs4[] = {
+		[SNET_SOCKET_CREATE]		= nla_total_size(sizeof(u8)),
+		[SNET_SOCKET_BIND]		= size_addr4_port,
+		[SNET_SOCKET_CONNECT]		= 2 * size_addr4_port,
+		[SNET_SOCKET_LISTEN]		= size_addr4_port,
+		[SNET_SOCKET_ACCEPT]		= size_addr4_port,
+		[SNET_SOCKET_POST_ACCEPT]	= 2 * size_addr4_port,
+		[SNET_SOCKET_SENDMSG]		= 2 * size_addr4_port,
+		[SNET_SOCKET_RECVMSG]		= 2 * size_addr4_port,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= 2 * size_addr4_port,
+		[SNET_SOCKET_CLOSE]		= 2 * size_addr4_port,
+	};
+
+	unsigned int sbs6[] = {
+		[SNET_SOCKET_CREATE]		= nla_total_size(sizeof(u8)),
+		[SNET_SOCKET_BIND]		= size_addr6_port,
+		[SNET_SOCKET_CONNECT]		= 2 * size_addr6_port,
+		[SNET_SOCKET_LISTEN]		= size_addr6_port,
+		[SNET_SOCKET_ACCEPT]		= size_addr6_port,
+		[SNET_SOCKET_POST_ACCEPT]	= 2 * size_addr6_port,
+		[SNET_SOCKET_SENDMSG]		= 2 * size_addr6_port,
+		[SNET_SOCKET_RECVMSG]		= 2 * size_addr6_port,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= 2 * size_addr6_port,
+		[SNET_SOCKET_CLOSE]		= 2 * size_addr6_port,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return -EINVAL;
+	else {
+		switch (info->family) {
+		case AF_INET:
+			return sbs4[info->syscall];
+			break;
+		case AF_INET6:
+			return sbs6[info->syscall];
+			break;
+		default:
+			return -EINVAL;
+			break;
+		}
+	}
+}
diff --git a/security/snet/snet_netlink_helper.h b/security/snet/snet_netlink_helper.h
new file mode 100644
index 0000000..d12e563
--- /dev/null
+++ b/security/snet/snet_netlink_helper.h
@@ -0,0 +1,7 @@
+#ifndef _SNET_NETLINK_HELPER_H
+#define _SNET_NETLINK_HELPER_H
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info);
+int snet_nl_size_by_syscall(struct snet_info *info);
+
+#endif /* SNET_NETLINK_HELPER_H */
-- 
1.7.4.1


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

* [RFC v4 07/11] snet: introduce snet_verdict
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (23 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (18 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet's subsystem responsive of managing verdicts

snet is using the word 'verdict' for the returning value of LSM hooks.
Different states exist (grant/deny/pending/none).

This patch introduces a hashtable 'verdict_hash' and operations (set/get/search..)
in order to manage verdicts. Syscalls are waiting, inside a classical waitqueue,
for theirs verdicts or for a timeout. Timeout value and the default verdict
policy are configurable at boot or by the snet_netlink subsystem.
With the help of the communication's subsystem, verdicts are coming from userspace

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_verdict.c |  203 ++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_verdict.h |   23 +++++
 2 files changed, 226 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_verdict.c
 create mode 100644 security/snet/snet_verdict.h

diff --git a/security/snet/snet_verdict.c b/security/snet/snet_verdict.c
new file mode 100644
index 0000000..b0811ac
--- /dev/null
+++ b/security/snet/snet_verdict.c
@@ -0,0 +1,203 @@
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+#include <linux/snet.h>
+#include <linux/slab.h>
+#include "snet_verdict.h"
+
+static struct list_head *snet_vdh;
+static rwlock_t snet_vdh_lock = __RW_LOCK_UNLOCKED();
+
+struct snet_verdict_entry {
+	struct list_head list;
+	u32 verdict_id;
+	enum snet_verdict verdict;
+};
+
+static atomic_t value = ATOMIC_INIT(1);
+
+/* when waiting for a verdict, process is added to this queue */
+static DECLARE_WAIT_QUEUE_HEAD(snet_wq);
+
+static struct kmem_cache *snet_verdict_entry_cachep;
+
+/* lookup for a verdict - before using this function, lock snet_vdh_lock */
+static struct snet_verdict_entry *__snet_verdict_lookup(const u32 verdict_id)
+{
+	unsigned int h = 0;
+	struct list_head *l = NULL;
+	struct snet_verdict_entry *s = NULL;
+
+	h = verdict_id % snet_vdh_size;
+	l = &snet_vdh[h];
+
+	list_for_each_entry(s, l, list) {
+		if (s->verdict_id == verdict_id) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	long ret = 0;
+
+	ret = wait_event_timeout(snet_wq,
+				 (verdict = snet_verdict_get(verdict_id))
+				 != SNET_VERDICT_PENDING,
+				 snet_verdict_delay * HZ);
+	if (ret)
+		return snet_verdict_get(verdict_id);
+	else
+		return SNET_VERDICT_NONE;
+}
+
+const enum snet_verdict snet_verdict_get(const u32 verdict_id)
+{
+	enum snet_verdict v = SNET_VERDICT_NONE;
+	struct snet_verdict_entry *data = NULL;
+
+	read_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data != NULL)
+		v = data->verdict;
+
+	read_unlock_bh(&snet_vdh_lock);
+	return v;
+}
+
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict)
+{
+	struct snet_verdict_entry *data = NULL;
+	int ret = -EINVAL;
+
+	if (verdict >= SNET_NR_VERDICT_TYPES)
+		goto out;
+
+	write_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data != NULL) {
+		/* if verdict is already set because of
+		   timeout, we won't modify it */
+		if (data->verdict == SNET_VERDICT_PENDING) {
+			data->verdict = verdict;
+			ret = 0;
+		}
+	}
+	write_unlock_bh(&snet_vdh_lock);
+	wake_up(&snet_wq);
+out:
+	return ret;
+}
+
+int snet_verdict_remove(const u32 verdict_id)
+{
+	struct snet_verdict_entry *data = NULL;
+
+	write_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data == NULL) {
+		write_unlock_bh(&snet_vdh_lock);
+		return -EINVAL;
+	}
+	pr_debug("(verdict_id=%u)\n", data->verdict_id);
+	list_del(&data->list);
+	write_unlock_bh(&snet_vdh_lock);
+	kmem_cache_free(snet_verdict_entry_cachep, data);
+	return 0;
+}
+
+int snet_verdict_insert(void)
+{
+	struct snet_verdict_entry *data = NULL;
+	unsigned int h = 0;
+	u32 verdict_id = 0;
+
+	data = kmem_cache_zalloc(snet_verdict_entry_cachep, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	do {
+		verdict_id = atomic_inc_return(&value);
+	} while (verdict_id == 0);
+
+	data->verdict_id = verdict_id;
+	data->verdict = SNET_VERDICT_PENDING;
+	INIT_LIST_HEAD(&(data->list));
+	h = data->verdict_id % snet_vdh_size;
+
+	write_lock_bh(&snet_vdh_lock);
+	list_add_tail(&data->list, &snet_vdh[h]);
+	pr_debug("[%u]=(verdict_id=%u)\n", h, data->verdict_id);
+	write_unlock_bh(&snet_vdh_lock);
+
+	return verdict_id;
+}
+
+void snet_verdict_flush(void)
+{
+	unsigned int i = 0;
+
+	write_lock_bh(&snet_vdh_lock);
+	for (i = 0; i < snet_vdh_size; i++) {
+		struct snet_verdict_entry *data, *tmp;
+		list_for_each_entry_safe(data, tmp, &snet_vdh[i], list) {
+			list_del(&data->list);
+			kmem_cache_free(snet_verdict_entry_cachep, data);
+		}
+	}
+	write_unlock_bh(&snet_vdh_lock);
+	return;
+}
+
+/* init function */
+int snet_verdict_init(void)
+{
+	int err = 0, i = 0;
+
+	if (snet_vdh_size == 0) {
+		printk(KERN_ERR "snet: bad snet_vdh_size value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (snet_verdict_delay == 0) {
+		printk(KERN_ERR "snet: bad snet_verdict_delay value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	snet_vdh = kzalloc(sizeof(struct list_head) * snet_vdh_size,
+			  GFP_KERNEL);
+	if (!snet_vdh) {
+		printk(KERN_WARNING
+		       "snet: can't alloc memory for verdict\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < snet_vdh_size; i++)
+		INIT_LIST_HEAD(&snet_vdh[i]);
+
+	/* snet_verdict_entry_cachep is not destroyed */
+	snet_verdict_entry_cachep = kmem_cache_create("snet_verdict_entry",
+						      sizeof(struct snet_verdict_entry),
+						      0, SLAB_PANIC, NULL);
+out:
+	return err;
+}
+
+/* exit function */
+void snet_verdict_exit(void)
+{
+	if (snet_vdh) {
+		kfree(snet_vdh);
+		snet_vdh = NULL;
+	}
+
+	return;
+}
diff --git a/security/snet/snet_verdict.h b/security/snet/snet_verdict.h
new file mode 100644
index 0000000..07e8638
--- /dev/null
+++ b/security/snet/snet_verdict.h
@@ -0,0 +1,23 @@
+#ifndef _SNET_VERDICT_H
+#define _SNET_VERDICT_H
+
+extern unsigned int snet_vdh_size;
+extern unsigned int snet_verdict_delay;
+
+/* helper functions */
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id);
+
+/* manipulate the verdicts hash table */
+const enum snet_verdict snet_verdict_get(const u32 verdict_id);
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict);
+int snet_verdict_insert(void);
+int snet_verdict_remove(const u32 verdict_id);
+int snet_verdict_insert(void);
+void snet_verdict_flush(void);
+
+/* init function */
+int snet_verdict_init(void);
+/* exit function */
+void snet_verdict_exit(void);
+
+#endif /* _SNET_VERDICT_H */
-- 
1.7.4.1

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

* [RFC v4 07/11] snet: introduce snet_verdict
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (26 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` [RFC v4 08/11] snet: introduce snet_ticket y
                   ` (15 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet's subsystem responsive of managing verdicts

snet is using the word 'verdict' for the returning value of LSM hooks.
Different states exist (grant/deny/pending/none).

This patch introduces a hashtable 'verdict_hash' and operations (set/get/search..)
in order to manage verdicts. Syscalls are waiting, inside a classical waitqueue,
for theirs verdicts or for a timeout. Timeout value and the default verdict
policy are configurable at boot or by the snet_netlink subsystem.
With the help of the communication's subsystem, verdicts are coming from userspace

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_verdict.c |  203 ++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_verdict.h |   23 +++++
 2 files changed, 226 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_verdict.c
 create mode 100644 security/snet/snet_verdict.h

diff --git a/security/snet/snet_verdict.c b/security/snet/snet_verdict.c
new file mode 100644
index 0000000..b0811ac
--- /dev/null
+++ b/security/snet/snet_verdict.c
@@ -0,0 +1,203 @@
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+#include <linux/snet.h>
+#include <linux/slab.h>
+#include "snet_verdict.h"
+
+static struct list_head *snet_vdh;
+static rwlock_t snet_vdh_lock = __RW_LOCK_UNLOCKED();
+
+struct snet_verdict_entry {
+	struct list_head list;
+	u32 verdict_id;
+	enum snet_verdict verdict;
+};
+
+static atomic_t value = ATOMIC_INIT(1);
+
+/* when waiting for a verdict, process is added to this queue */
+static DECLARE_WAIT_QUEUE_HEAD(snet_wq);
+
+static struct kmem_cache *snet_verdict_entry_cachep;
+
+/* lookup for a verdict - before using this function, lock snet_vdh_lock */
+static struct snet_verdict_entry *__snet_verdict_lookup(const u32 verdict_id)
+{
+	unsigned int h = 0;
+	struct list_head *l = NULL;
+	struct snet_verdict_entry *s = NULL;
+
+	h = verdict_id % snet_vdh_size;
+	l = &snet_vdh[h];
+
+	list_for_each_entry(s, l, list) {
+		if (s->verdict_id == verdict_id) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	long ret = 0;
+
+	ret = wait_event_timeout(snet_wq,
+				 (verdict = snet_verdict_get(verdict_id))
+				 != SNET_VERDICT_PENDING,
+				 snet_verdict_delay * HZ);
+	if (ret)
+		return snet_verdict_get(verdict_id);
+	else
+		return SNET_VERDICT_NONE;
+}
+
+const enum snet_verdict snet_verdict_get(const u32 verdict_id)
+{
+	enum snet_verdict v = SNET_VERDICT_NONE;
+	struct snet_verdict_entry *data = NULL;
+
+	read_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data != NULL)
+		v = data->verdict;
+
+	read_unlock_bh(&snet_vdh_lock);
+	return v;
+}
+
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict)
+{
+	struct snet_verdict_entry *data = NULL;
+	int ret = -EINVAL;
+
+	if (verdict >= SNET_NR_VERDICT_TYPES)
+		goto out;
+
+	write_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data != NULL) {
+		/* if verdict is already set because of
+		   timeout, we won't modify it */
+		if (data->verdict == SNET_VERDICT_PENDING) {
+			data->verdict = verdict;
+			ret = 0;
+		}
+	}
+	write_unlock_bh(&snet_vdh_lock);
+	wake_up(&snet_wq);
+out:
+	return ret;
+}
+
+int snet_verdict_remove(const u32 verdict_id)
+{
+	struct snet_verdict_entry *data = NULL;
+
+	write_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data == NULL) {
+		write_unlock_bh(&snet_vdh_lock);
+		return -EINVAL;
+	}
+	pr_debug("(verdict_id=%u)\n", data->verdict_id);
+	list_del(&data->list);
+	write_unlock_bh(&snet_vdh_lock);
+	kmem_cache_free(snet_verdict_entry_cachep, data);
+	return 0;
+}
+
+int snet_verdict_insert(void)
+{
+	struct snet_verdict_entry *data = NULL;
+	unsigned int h = 0;
+	u32 verdict_id = 0;
+
+	data = kmem_cache_zalloc(snet_verdict_entry_cachep, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	do {
+		verdict_id = atomic_inc_return(&value);
+	} while (verdict_id == 0);
+
+	data->verdict_id = verdict_id;
+	data->verdict = SNET_VERDICT_PENDING;
+	INIT_LIST_HEAD(&(data->list));
+	h = data->verdict_id % snet_vdh_size;
+
+	write_lock_bh(&snet_vdh_lock);
+	list_add_tail(&data->list, &snet_vdh[h]);
+	pr_debug("[%u]=(verdict_id=%u)\n", h, data->verdict_id);
+	write_unlock_bh(&snet_vdh_lock);
+
+	return verdict_id;
+}
+
+void snet_verdict_flush(void)
+{
+	unsigned int i = 0;
+
+	write_lock_bh(&snet_vdh_lock);
+	for (i = 0; i < snet_vdh_size; i++) {
+		struct snet_verdict_entry *data, *tmp;
+		list_for_each_entry_safe(data, tmp, &snet_vdh[i], list) {
+			list_del(&data->list);
+			kmem_cache_free(snet_verdict_entry_cachep, data);
+		}
+	}
+	write_unlock_bh(&snet_vdh_lock);
+	return;
+}
+
+/* init function */
+int snet_verdict_init(void)
+{
+	int err = 0, i = 0;
+
+	if (snet_vdh_size == 0) {
+		printk(KERN_ERR "snet: bad snet_vdh_size value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (snet_verdict_delay == 0) {
+		printk(KERN_ERR "snet: bad snet_verdict_delay value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	snet_vdh = kzalloc(sizeof(struct list_head) * snet_vdh_size,
+			  GFP_KERNEL);
+	if (!snet_vdh) {
+		printk(KERN_WARNING
+		       "snet: can't alloc memory for verdict\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < snet_vdh_size; i++)
+		INIT_LIST_HEAD(&snet_vdh[i]);
+
+	/* snet_verdict_entry_cachep is not destroyed */
+	snet_verdict_entry_cachep = kmem_cache_create("snet_verdict_entry",
+						      sizeof(struct snet_verdict_entry),
+						      0, SLAB_PANIC, NULL);
+out:
+	return err;
+}
+
+/* exit function */
+void snet_verdict_exit(void)
+{
+	if (snet_vdh) {
+		kfree(snet_vdh);
+		snet_vdh = NULL;
+	}
+
+	return;
+}
diff --git a/security/snet/snet_verdict.h b/security/snet/snet_verdict.h
new file mode 100644
index 0000000..07e8638
--- /dev/null
+++ b/security/snet/snet_verdict.h
@@ -0,0 +1,23 @@
+#ifndef _SNET_VERDICT_H
+#define _SNET_VERDICT_H
+
+extern unsigned int snet_vdh_size;
+extern unsigned int snet_verdict_delay;
+
+/* helper functions */
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id);
+
+/* manipulate the verdicts hash table */
+const enum snet_verdict snet_verdict_get(const u32 verdict_id);
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict);
+int snet_verdict_insert(void);
+int snet_verdict_remove(const u32 verdict_id);
+int snet_verdict_insert(void);
+void snet_verdict_flush(void);
+
+/* init function */
+int snet_verdict_init(void);
+/* exit function */
+void snet_verdict_exit(void);
+
+#endif /* _SNET_VERDICT_H */
-- 
1.7.4.1


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

* [RFC v4 07/11] snet: introduce snet_verdict
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (25 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (16 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet's subsystem responsive of managing verdicts

snet is using the word 'verdict' for the returning value of LSM hooks.
Different states exist (grant/deny/pending/none).

This patch introduces a hashtable 'verdict_hash' and operations (set/get/search..)
in order to manage verdicts. Syscalls are waiting, inside a classical waitqueue,
for theirs verdicts or for a timeout. Timeout value and the default verdict
policy are configurable at boot or by the snet_netlink subsystem.
With the help of the communication's subsystem, verdicts are coming from userspace

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_verdict.c |  203 ++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_verdict.h |   23 +++++
 2 files changed, 226 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_verdict.c
 create mode 100644 security/snet/snet_verdict.h

diff --git a/security/snet/snet_verdict.c b/security/snet/snet_verdict.c
new file mode 100644
index 0000000..b0811ac
--- /dev/null
+++ b/security/snet/snet_verdict.c
@@ -0,0 +1,203 @@
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+#include <linux/snet.h>
+#include <linux/slab.h>
+#include "snet_verdict.h"
+
+static struct list_head *snet_vdh;
+static rwlock_t snet_vdh_lock = __RW_LOCK_UNLOCKED();
+
+struct snet_verdict_entry {
+	struct list_head list;
+	u32 verdict_id;
+	enum snet_verdict verdict;
+};
+
+static atomic_t value = ATOMIC_INIT(1);
+
+/* when waiting for a verdict, process is added to this queue */
+static DECLARE_WAIT_QUEUE_HEAD(snet_wq);
+
+static struct kmem_cache *snet_verdict_entry_cachep;
+
+/* lookup for a verdict - before using this function, lock snet_vdh_lock */
+static struct snet_verdict_entry *__snet_verdict_lookup(const u32 verdict_id)
+{
+	unsigned int h = 0;
+	struct list_head *l = NULL;
+	struct snet_verdict_entry *s = NULL;
+
+	h = verdict_id % snet_vdh_size;
+	l = &snet_vdh[h];
+
+	list_for_each_entry(s, l, list) {
+		if (s->verdict_id == verdict_id) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	long ret = 0;
+
+	ret = wait_event_timeout(snet_wq,
+				 (verdict = snet_verdict_get(verdict_id))
+				 != SNET_VERDICT_PENDING,
+				 snet_verdict_delay * HZ);
+	if (ret)
+		return snet_verdict_get(verdict_id);
+	else
+		return SNET_VERDICT_NONE;
+}
+
+const enum snet_verdict snet_verdict_get(const u32 verdict_id)
+{
+	enum snet_verdict v = SNET_VERDICT_NONE;
+	struct snet_verdict_entry *data = NULL;
+
+	read_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data != NULL)
+		v = data->verdict;
+
+	read_unlock_bh(&snet_vdh_lock);
+	return v;
+}
+
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict)
+{
+	struct snet_verdict_entry *data = NULL;
+	int ret = -EINVAL;
+
+	if (verdict >= SNET_NR_VERDICT_TYPES)
+		goto out;
+
+	write_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data != NULL) {
+		/* if verdict is already set because of
+		   timeout, we won't modify it */
+		if (data->verdict == SNET_VERDICT_PENDING) {
+			data->verdict = verdict;
+			ret = 0;
+		}
+	}
+	write_unlock_bh(&snet_vdh_lock);
+	wake_up(&snet_wq);
+out:
+	return ret;
+}
+
+int snet_verdict_remove(const u32 verdict_id)
+{
+	struct snet_verdict_entry *data = NULL;
+
+	write_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data == NULL) {
+		write_unlock_bh(&snet_vdh_lock);
+		return -EINVAL;
+	}
+	pr_debug("(verdict_id=%u)\n", data->verdict_id);
+	list_del(&data->list);
+	write_unlock_bh(&snet_vdh_lock);
+	kmem_cache_free(snet_verdict_entry_cachep, data);
+	return 0;
+}
+
+int snet_verdict_insert(void)
+{
+	struct snet_verdict_entry *data = NULL;
+	unsigned int h = 0;
+	u32 verdict_id = 0;
+
+	data = kmem_cache_zalloc(snet_verdict_entry_cachep, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	do {
+		verdict_id = atomic_inc_return(&value);
+	} while (verdict_id == 0);
+
+	data->verdict_id = verdict_id;
+	data->verdict = SNET_VERDICT_PENDING;
+	INIT_LIST_HEAD(&(data->list));
+	h = data->verdict_id % snet_vdh_size;
+
+	write_lock_bh(&snet_vdh_lock);
+	list_add_tail(&data->list, &snet_vdh[h]);
+	pr_debug("[%u]=(verdict_id=%u)\n", h, data->verdict_id);
+	write_unlock_bh(&snet_vdh_lock);
+
+	return verdict_id;
+}
+
+void snet_verdict_flush(void)
+{
+	unsigned int i = 0;
+
+	write_lock_bh(&snet_vdh_lock);
+	for (i = 0; i < snet_vdh_size; i++) {
+		struct snet_verdict_entry *data, *tmp;
+		list_for_each_entry_safe(data, tmp, &snet_vdh[i], list) {
+			list_del(&data->list);
+			kmem_cache_free(snet_verdict_entry_cachep, data);
+		}
+	}
+	write_unlock_bh(&snet_vdh_lock);
+	return;
+}
+
+/* init function */
+int snet_verdict_init(void)
+{
+	int err = 0, i = 0;
+
+	if (snet_vdh_size == 0) {
+		printk(KERN_ERR "snet: bad snet_vdh_size value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (snet_verdict_delay == 0) {
+		printk(KERN_ERR "snet: bad snet_verdict_delay value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	snet_vdh = kzalloc(sizeof(struct list_head) * snet_vdh_size,
+			  GFP_KERNEL);
+	if (!snet_vdh) {
+		printk(KERN_WARNING
+		       "snet: can't alloc memory for verdict\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < snet_vdh_size; i++)
+		INIT_LIST_HEAD(&snet_vdh[i]);
+
+	/* snet_verdict_entry_cachep is not destroyed */
+	snet_verdict_entry_cachep = kmem_cache_create("snet_verdict_entry",
+						      sizeof(struct snet_verdict_entry),
+						      0, SLAB_PANIC, NULL);
+out:
+	return err;
+}
+
+/* exit function */
+void snet_verdict_exit(void)
+{
+	if (snet_vdh) {
+		kfree(snet_vdh);
+		snet_vdh = NULL;
+	}
+
+	return;
+}
diff --git a/security/snet/snet_verdict.h b/security/snet/snet_verdict.h
new file mode 100644
index 0000000..07e8638
--- /dev/null
+++ b/security/snet/snet_verdict.h
@@ -0,0 +1,23 @@
+#ifndef _SNET_VERDICT_H
+#define _SNET_VERDICT_H
+
+extern unsigned int snet_vdh_size;
+extern unsigned int snet_verdict_delay;
+
+/* helper functions */
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id);
+
+/* manipulate the verdicts hash table */
+const enum snet_verdict snet_verdict_get(const u32 verdict_id);
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict);
+int snet_verdict_insert(void);
+int snet_verdict_remove(const u32 verdict_id);
+int snet_verdict_insert(void);
+void snet_verdict_flush(void);
+
+/* init function */
+int snet_verdict_init(void);
+/* exit function */
+void snet_verdict_exit(void);
+
+#endif /* _SNET_VERDICT_H */
-- 
1.7.4.1


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

* [RFC v4 07/11] snet: introduce snet_verdict
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (24 preceding siblings ...)
  2011-05-05 13:59 ` [RFC v4 07/11] snet: introduce snet_verdict y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (17 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch adds the snet's subsystem responsive of managing verdicts

snet is using the word 'verdict' for the returning value of LSM hooks.
Different states exist (grant/deny/pending/none).

This patch introduces a hashtable 'verdict_hash' and operations (set/get/search..)
in order to manage verdicts. Syscalls are waiting, inside a classical waitqueue,
for theirs verdicts or for a timeout. Timeout value and the default verdict
policy are configurable at boot or by the snet_netlink subsystem.
With the help of the communication's subsystem, verdicts are coming from userspace

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_verdict.c |  203 ++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_verdict.h |   23 +++++
 2 files changed, 226 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_verdict.c
 create mode 100644 security/snet/snet_verdict.h

diff --git a/security/snet/snet_verdict.c b/security/snet/snet_verdict.c
new file mode 100644
index 0000000..b0811ac
--- /dev/null
+++ b/security/snet/snet_verdict.c
@@ -0,0 +1,203 @@
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+#include <linux/snet.h>
+#include <linux/slab.h>
+#include "snet_verdict.h"
+
+static struct list_head *snet_vdh;
+static rwlock_t snet_vdh_lock = __RW_LOCK_UNLOCKED();
+
+struct snet_verdict_entry {
+	struct list_head list;
+	u32 verdict_id;
+	enum snet_verdict verdict;
+};
+
+static atomic_t value = ATOMIC_INIT(1);
+
+/* when waiting for a verdict, process is added to this queue */
+static DECLARE_WAIT_QUEUE_HEAD(snet_wq);
+
+static struct kmem_cache *snet_verdict_entry_cachep;
+
+/* lookup for a verdict - before using this function, lock snet_vdh_lock */
+static struct snet_verdict_entry *__snet_verdict_lookup(const u32 verdict_id)
+{
+	unsigned int h = 0;
+	struct list_head *l = NULL;
+	struct snet_verdict_entry *s = NULL;
+
+	h = verdict_id % snet_vdh_size;
+	l = &snet_vdh[h];
+
+	list_for_each_entry(s, l, list) {
+		if (s->verdict_id == verdict_id) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id)
+{
+	enum snet_verdict verdict = SNET_VERDICT_NONE;
+	long ret = 0;
+
+	ret = wait_event_timeout(snet_wq,
+				 (verdict = snet_verdict_get(verdict_id))
+				 != SNET_VERDICT_PENDING,
+				 snet_verdict_delay * HZ);
+	if (ret)
+		return snet_verdict_get(verdict_id);
+	else
+		return SNET_VERDICT_NONE;
+}
+
+const enum snet_verdict snet_verdict_get(const u32 verdict_id)
+{
+	enum snet_verdict v = SNET_VERDICT_NONE;
+	struct snet_verdict_entry *data = NULL;
+
+	read_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data != NULL)
+		v = data->verdict;
+
+	read_unlock_bh(&snet_vdh_lock);
+	return v;
+}
+
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict)
+{
+	struct snet_verdict_entry *data = NULL;
+	int ret = -EINVAL;
+
+	if (verdict >= SNET_NR_VERDICT_TYPES)
+		goto out;
+
+	write_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data != NULL) {
+		/* if verdict is already set because of
+		   timeout, we won't modify it */
+		if (data->verdict == SNET_VERDICT_PENDING) {
+			data->verdict = verdict;
+			ret = 0;
+		}
+	}
+	write_unlock_bh(&snet_vdh_lock);
+	wake_up(&snet_wq);
+out:
+	return ret;
+}
+
+int snet_verdict_remove(const u32 verdict_id)
+{
+	struct snet_verdict_entry *data = NULL;
+
+	write_lock_bh(&snet_vdh_lock);
+	data = __snet_verdict_lookup(verdict_id);
+	if (data == NULL) {
+		write_unlock_bh(&snet_vdh_lock);
+		return -EINVAL;
+	}
+	pr_debug("(verdict_id=%u)\n", data->verdict_id);
+	list_del(&data->list);
+	write_unlock_bh(&snet_vdh_lock);
+	kmem_cache_free(snet_verdict_entry_cachep, data);
+	return 0;
+}
+
+int snet_verdict_insert(void)
+{
+	struct snet_verdict_entry *data = NULL;
+	unsigned int h = 0;
+	u32 verdict_id = 0;
+
+	data = kmem_cache_zalloc(snet_verdict_entry_cachep, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	do {
+		verdict_id = atomic_inc_return(&value);
+	} while (verdict_id == 0);
+
+	data->verdict_id = verdict_id;
+	data->verdict = SNET_VERDICT_PENDING;
+	INIT_LIST_HEAD(&(data->list));
+	h = data->verdict_id % snet_vdh_size;
+
+	write_lock_bh(&snet_vdh_lock);
+	list_add_tail(&data->list, &snet_vdh[h]);
+	pr_debug("[%u]=(verdict_id=%u)\n", h, data->verdict_id);
+	write_unlock_bh(&snet_vdh_lock);
+
+	return verdict_id;
+}
+
+void snet_verdict_flush(void)
+{
+	unsigned int i = 0;
+
+	write_lock_bh(&snet_vdh_lock);
+	for (i = 0; i < snet_vdh_size; i++) {
+		struct snet_verdict_entry *data, *tmp;
+		list_for_each_entry_safe(data, tmp, &snet_vdh[i], list) {
+			list_del(&data->list);
+			kmem_cache_free(snet_verdict_entry_cachep, data);
+		}
+	}
+	write_unlock_bh(&snet_vdh_lock);
+	return;
+}
+
+/* init function */
+int snet_verdict_init(void)
+{
+	int err = 0, i = 0;
+
+	if (snet_vdh_size == 0) {
+		printk(KERN_ERR "snet: bad snet_vdh_size value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (snet_verdict_delay == 0) {
+		printk(KERN_ERR "snet: bad snet_verdict_delay value\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	snet_vdh = kzalloc(sizeof(struct list_head) * snet_vdh_size,
+			  GFP_KERNEL);
+	if (!snet_vdh) {
+		printk(KERN_WARNING
+		       "snet: can't alloc memory for verdict\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < snet_vdh_size; i++)
+		INIT_LIST_HEAD(&snet_vdh[i]);
+
+	/* snet_verdict_entry_cachep is not destroyed */
+	snet_verdict_entry_cachep = kmem_cache_create("snet_verdict_entry",
+						      sizeof(struct snet_verdict_entry),
+						      0, SLAB_PANIC, NULL);
+out:
+	return err;
+}
+
+/* exit function */
+void snet_verdict_exit(void)
+{
+	if (snet_vdh) {
+		kfree(snet_vdh);
+		snet_vdh = NULL;
+	}
+
+	return;
+}
diff --git a/security/snet/snet_verdict.h b/security/snet/snet_verdict.h
new file mode 100644
index 0000000..07e8638
--- /dev/null
+++ b/security/snet/snet_verdict.h
@@ -0,0 +1,23 @@
+#ifndef _SNET_VERDICT_H
+#define _SNET_VERDICT_H
+
+extern unsigned int snet_vdh_size;
+extern unsigned int snet_verdict_delay;
+
+/* helper functions */
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id);
+
+/* manipulate the verdicts hash table */
+const enum snet_verdict snet_verdict_get(const u32 verdict_id);
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict);
+int snet_verdict_insert(void);
+int snet_verdict_remove(const u32 verdict_id);
+int snet_verdict_insert(void);
+void snet_verdict_flush(void);
+
+/* init function */
+int snet_verdict_init(void);
+/* exit function */
+void snet_verdict_exit(void);
+
+#endif /* _SNET_VERDICT_H */
-- 
1.7.4.1


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

* [RFC v4 08/11] snet: introduce snet_ticket
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (29 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (12 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds the snet's subsystem managing granted-access tickets

snet is using the term 'ticket' for refering to a structure which keeps
informations about verdict, coming from userspace.

generic informations:
  timeout
  syscall
  protocol
  verdict

protocol-dependant informations : (so some infos may not be used)
  address family
  socket type
  source address
  source port
  distant address
  distant port

ticket are attached to the "void *security" pointer of task_struct

there are 3 modes:

0. no ticket - SNET_TICKET_OFF
   every syscalls has to be verified by userspace.

1. timeout fixed - SNET_TICKET_FIX
   for each response from the userspace, we are creating a ticket,
   attached to the task_struct, with the filled informations, and a
   fixed timeout value (10 secs by default).
   then before asking userspace, kernel mecanism is checking existing
   tickets for the task_struct, if there is a granted-access ticket, we
   are using the verdict value attached.
   after the timeout value, the ticket is destroyed.

2. timeout with extendable value - SNET_TICKET_EXTEND
   this is the same mecanism as 1, but every time a ticket is matched
   and used, the timeout value is reset to the default value, so its
   life is extended.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_ticket.c        |  195 ++++++++++++++++++++++++++++++++++++
 security/snet/snet_ticket.h        |   37 +++++++
 security/snet/snet_ticket_helper.c |  127 +++++++++++++++++++++++
 security/snet/snet_ticket_helper.h |    8 ++
 4 files changed, 367 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_ticket.c
 create mode 100644 security/snet/snet_ticket.h
 create mode 100644 security/snet/snet_ticket_helper.c
 create mode 100644 security/snet/snet_ticket_helper.h

diff --git a/security/snet/snet_ticket.c b/security/snet/snet_ticket.c
new file mode 100644
index 0000000..a260412
--- /dev/null
+++ b/security/snet/snet_ticket.c
@@ -0,0 +1,195 @@
+#include <linux/slab.h>
+#include <linux/cred.h>
+#include <linux/jhash.h>
+#include <linux/security.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_ticket_helper.h"
+
+#define HSIZE 16
+
+static struct kmem_cache *snet_ticket_cachep;
+static struct kmem_cache *snet_task_security_cachep;
+
+enum snet_verdict snet_ticket_check(struct snet_info *info)
+{
+	struct snet_ticket *st = NULL;
+	unsigned int h = 0, verdict = SNET_VERDICT_NONE;
+	struct list_head *l = NULL;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode == SNET_TICKET_OFF)
+		goto out;
+
+	tsec = (struct snet_task_security*) current_security();
+
+	h = jhash_2words(info->syscall, info->protocol, 0) % HSIZE;
+	l = &tsec->hash[h];
+
+	read_lock_bh(&tsec->lock);
+	list_for_each_entry(st, l, list) {
+		if (__ticket_check(st, info)) {
+			verdict = st->verdict;
+			pr_debug("snet_ticket found: ticket=%p tsec=%p\n",
+				 st, st->tsec);
+			if (snet_ticket_mode == SNET_TICKET_EXTEND) {
+				mod_timer(&st->timeout,
+					  jiffies + snet_ticket_delay * HZ);
+			}
+			break;
+		}
+	}
+	read_unlock_bh(&tsec->lock);
+out:
+	return verdict;
+}
+
+static void snet_ticket_timeout(unsigned long arg)
+{
+	struct snet_ticket *st = (struct snet_ticket*)arg;
+
+	pr_debug("snet_ticket_timeout: ticket=%p tsec=%p\n", st, st->tsec);
+
+	write_lock_bh(&st->tsec->lock);
+	list_del(&st->list);
+	write_unlock_bh(&st->tsec->lock);
+	kmem_cache_free(snet_ticket_cachep, st);
+	return;
+}
+
+static struct snet_ticket *snet_ticket_alloc(void)
+{
+	struct snet_ticket *st = NULL;
+
+	st = kmem_cache_zalloc(snet_ticket_cachep, GFP_KERNEL);
+	if (st == NULL)
+		goto out;
+
+	INIT_LIST_HEAD(&st->list);
+	init_timer(&st->timeout);
+	st->timeout.expires = snet_ticket_delay * HZ;
+out:
+	return st;
+}
+
+static void snet_ticket_insert(struct snet_ticket *st)
+{
+	unsigned int h;
+	struct list_head *l;
+
+	h = jhash_2words(st->syscall, st->protocol, 0) % HSIZE;
+	l = &(st->tsec->hash[h]);
+
+	st->timeout.expires += jiffies;
+	add_timer(&st->timeout);
+
+	write_lock_bh(&(st->tsec->lock));
+	list_add_tail(&st->list, l);
+	write_unlock_bh(&(st->tsec->lock));
+	return;
+}
+
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict)
+{
+	struct snet_ticket *st;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode == SNET_TICKET_OFF)
+		goto out;
+
+	tsec = (struct snet_task_security*) current_security();
+
+	st = snet_ticket_alloc();
+	if (st == NULL)
+		goto out;
+
+	st->tsec = tsec;
+	snet_ticket_fill(st, info, verdict);
+	setup_timer(&st->timeout, snet_ticket_timeout, (unsigned long)st);
+	snet_ticket_insert(st);
+out:
+	return;
+}
+
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
+{
+	unsigned int index = 0;
+	struct snet_task_security *tsec = NULL;
+
+	tsec = kmem_cache_zalloc(snet_task_security_cachep, gfp);
+	if (tsec == NULL)
+		return -ENOMEM;
+
+	pr_debug("ticket_prepare_creds: pid=%u tsec=%p\n", current->pid, tsec);
+	rwlock_init(&tsec->lock);
+	for (index = 0; index < HSIZE; index++)
+		INIT_LIST_HEAD(&tsec->hash[index]);
+
+	new->security = tsec;
+	return 0;
+}
+
+void snet_cred_free(struct cred *cred)
+{
+	struct snet_task_security *tsec = cred->security;
+	unsigned int index;
+
+	pr_debug("ticket_free_cred: pid=%u tsec=%p\n",  current->pid, tsec);
+
+	write_lock_bh(&tsec->lock);
+	/* destroy all tickets */
+	for (index = 0; index < HSIZE; index++) {
+		struct snet_ticket *st, *tmp;
+		list_for_each_entry_safe(st, tmp, &tsec->hash[index], list) {
+			if (del_timer_sync(&st->timeout)) {
+				pr_debug("ticket_cred_free: [%u] ticket=%p tsec=%p\n",
+					 index, st, st->tsec);
+				list_del(&st->list);
+				kmem_cache_free(snet_ticket_cachep, st);
+			}
+		}
+	}
+	cred->security = NULL;
+	write_unlock_bh(&tsec->lock);
+	kmem_cache_free(snet_task_security_cachep, tsec);
+	return;
+}
+
+int snet_ticket_init(void)
+{
+	unsigned int index = 0;
+	struct cred *cred = (struct cred *) current->real_cred;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode >= SNET_TICKET_INVALID) {
+		printk(KERN_ERR "snet: bad snet_ticket_mode\n");
+		return -EINVAL;
+	}
+
+	if ((snet_ticket_mode == SNET_TICKET_FIX ||
+	    snet_ticket_mode == SNET_TICKET_EXTEND) &&
+	    (snet_ticket_delay == 0)) {
+		printk(KERN_ERR "snet: bad snet_ticket_delay\n");
+		return -EINVAL;
+	}
+
+	/* snet_ticket_cachep is not destroyed */
+	snet_ticket_cachep = kmem_cache_create("snet_ticket",
+					       sizeof(struct snet_ticket),
+					       0, SLAB_PANIC, NULL);
+	/* snet_task_security_cachep is not destroyed */
+	snet_task_security_cachep = kmem_cache_create("snet_task_security",
+						      sizeof(struct snet_task_security),
+						      0, SLAB_PANIC, NULL);
+
+	tsec = kmem_cache_zalloc(snet_task_security_cachep, GFP_KERNEL);
+	if (tsec == NULL)
+		return -ENOMEM;
+
+	rwlock_init(&tsec->lock);
+	for (index = 0; index < HSIZE; index++)
+		INIT_LIST_HEAD(&tsec->hash[index]);
+
+	cred->security = tsec;
+	return 0;
+}
diff --git a/security/snet/snet_ticket.h b/security/snet/snet_ticket.h
new file mode 100644
index 0000000..b6d1020
--- /dev/null
+++ b/security/snet/snet_ticket.h
@@ -0,0 +1,37 @@
+#ifndef _SNET_TICKET_H
+#define _SNET_TICKET_H
+
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/cred.h>
+#include <linux/snet.h>
+
+struct snet_task_security {
+	struct list_head hash[16];
+	rwlock_t lock;
+};
+
+struct snet_ticket {
+	struct list_head list;
+	struct snet_task_security *tsec;
+	struct timer_list timeout;
+
+	enum snet_syscall syscall;
+	u8 protocol;
+	u8 family;
+	int type;
+	struct snet_sock_half src;
+	struct snet_sock_half dst;
+	enum snet_verdict verdict;
+};
+
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+void snet_cred_free(struct cred *cred);
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
+enum snet_verdict snet_ticket_check(struct snet_info *info);
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict);
+int snet_ticket_init(void);
+
+#endif	/* _SNET_TICKET_H */
diff --git a/security/snet/snet_ticket_helper.c b/security/snet/snet_ticket_helper.c
new file mode 100644
index 0000000..2dc9b94
--- /dev/null
+++ b/security/snet/snet_ticket_helper.c
@@ -0,0 +1,127 @@
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_utils.h"
+
+static int check_create(struct snet_ticket *st, struct snet_info *info)
+{
+	return (st->type == info->type);
+}
+
+static int check_src(struct snet_ticket *st, struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		if ((st->src.u3.ip == info->src.u3.ip) &&
+		    (st->src.u.port == info->src.u.port))
+			return 1;
+		break;
+	case AF_INET6:
+		if ((!memcmp(&st->src.u3.ip6, &info->src.u3.ip6,
+			     sizeof(info->src.u3.ip6))) &&
+		    (st->src.u.port == info->src.u.port))
+			return 1;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int check_dst(struct snet_ticket *st, struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		if ((st->dst.u3.ip == info->dst.u3.ip) &&
+		    (st->dst.u.port == info->dst.u.port))
+			return 1;
+		break;
+	case AF_INET6:
+		if ((!memcmp(&st->dst.u3.ip6, &info->dst.u3.ip6,
+			     sizeof(info->dst.u3.ip6))) &&
+		    (st->dst.u.port == info->dst.u.port))
+			return 1;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int check_src_and_dst(struct snet_ticket *st, struct snet_info *info)
+{
+	return (check_src(st, info) && check_dst(st, info));
+}
+
+static int check_none(struct snet_ticket *st, struct snet_info *info)
+{
+	return 0;
+}
+
+int __ticket_check(struct snet_ticket *st, struct snet_info *info)
+{
+	static int (*ticket_df[])(struct snet_ticket *, struct snet_info *) = {
+		[SNET_SOCKET_CREATE]		= &check_create,
+		[SNET_SOCKET_BIND]		= &check_src,
+		[SNET_SOCKET_CONNECT]		= &check_dst,
+		[SNET_SOCKET_LISTEN]		= &check_src,
+		[SNET_SOCKET_ACCEPT]		= &check_src,
+		[SNET_SOCKET_POST_ACCEPT]	= &check_none,
+		[SNET_SOCKET_SENDMSG]		= &check_src_and_dst,
+		[SNET_SOCKET_RECVMSG]		= &check_src_and_dst,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= &check_src_and_dst,
+		[SNET_SOCKET_CLOSE]		= &check_none,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return 0;
+	else {
+		if ((st->syscall == info->syscall) &&
+		    (st->protocol == info->protocol) &&
+		    (st->family == info->family) &&
+		    ticket_df[info->syscall](st, info))
+			return 1;
+		else
+			return 0;
+	}
+}
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+		      enum snet_verdict verdict)
+{
+	st->syscall = info->syscall;
+	st->protocol = info->protocol;
+	st->family = info->family;
+	st->src.u.port = info->src.u.port;
+	st->dst.u.port = info->dst.u.port;
+	st->verdict = verdict;
+
+	switch (info->family) {
+	case AF_INET:
+		st->src.u3.ip = info->src.u3.ip;
+		st->dst.u3.ip = info->dst.u3.ip;
+		pr_debug("ticket=%p [syscall=%s protocol=%u "
+			 "family=%u %pI4:%u->%pI4:%u] verdict=%s | tsec=%p pid=%u\n",
+			 st, snet_syscall_name(st->syscall), st->protocol,
+			 st->family, &st->src.u3.ip, st->src.u.port,
+			 &st->dst.u3.ip, st->dst.u.port,
+			 snet_verdict_name(st->verdict), st->tsec, current->pid);
+		break;
+	case AF_INET6:
+		memcpy(&st->src.u3.ip6, &info->src.u3.ip6,
+		       sizeof(info->src.u3.ip6));
+		memcpy(&st->dst.u3.ip6, &info->dst.u3.ip6,
+		       sizeof(info->dst.u3.ip6));
+		pr_debug("ticket=%p [syscall=%s protocol=%u "
+			 "family=%u %pI6:%u->%pI6:%u] verdict=%s | tsec=%p pid=%u\n",
+			 st, snet_syscall_name(st->syscall), st->protocol,
+			 st->family, &st->src.u3.ip6, st->src.u.port,
+			 &st->dst.u3.ip6, st->dst.u.port,
+			 snet_verdict_name(st->verdict), st->tsec, current->pid);
+		break;
+	default:
+		break;
+	}
+	return;
+}
diff --git a/security/snet/snet_ticket_helper.h b/security/snet/snet_ticket_helper.h
new file mode 100644
index 0000000..177bca5
--- /dev/null
+++ b/security/snet/snet_ticket_helper.h
@@ -0,0 +1,8 @@
+#ifndef _SNET_TICKET_HELPER_H
+#define _SNET_TICKET_HELPER_H
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+		      enum snet_verdict verdict);
+int __ticket_check(struct snet_ticket *st, struct snet_info *info);
+
+#endif	/* _SNET_TICKET_HELPER_H */
-- 
1.7.4.1

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

* [RFC v4 08/11] snet: introduce snet_ticket
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (27 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (14 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds the snet's subsystem managing granted-access tickets

snet is using the term 'ticket' for refering to a structure which keeps
informations about verdict, coming from userspace.

generic informations:
  timeout
  syscall
  protocol
  verdict

protocol-dependant informations : (so some infos may not be used)
  address family
  socket type
  source address
  source port
  distant address
  distant port

ticket are attached to the "void *security" pointer of task_struct

there are 3 modes:

0. no ticket - SNET_TICKET_OFF
   every syscalls has to be verified by userspace.

1. timeout fixed - SNET_TICKET_FIX
   for each response from the userspace, we are creating a ticket,
   attached to the task_struct, with the filled informations, and a
   fixed timeout value (10 secs by default).
   then before asking userspace, kernel mecanism is checking existing
   tickets for the task_struct, if there is a granted-access ticket, we
   are using the verdict value attached.
   after the timeout value, the ticket is destroyed.

2. timeout with extendable value - SNET_TICKET_EXTEND
   this is the same mecanism as 1, but every time a ticket is matched
   and used, the timeout value is reset to the default value, so its
   life is extended.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_ticket.c        |  195 ++++++++++++++++++++++++++++++++++++
 security/snet/snet_ticket.h        |   37 +++++++
 security/snet/snet_ticket_helper.c |  127 +++++++++++++++++++++++
 security/snet/snet_ticket_helper.h |    8 ++
 4 files changed, 367 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_ticket.c
 create mode 100644 security/snet/snet_ticket.h
 create mode 100644 security/snet/snet_ticket_helper.c
 create mode 100644 security/snet/snet_ticket_helper.h

diff --git a/security/snet/snet_ticket.c b/security/snet/snet_ticket.c
new file mode 100644
index 0000000..a260412
--- /dev/null
+++ b/security/snet/snet_ticket.c
@@ -0,0 +1,195 @@
+#include <linux/slab.h>
+#include <linux/cred.h>
+#include <linux/jhash.h>
+#include <linux/security.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_ticket_helper.h"
+
+#define HSIZE 16
+
+static struct kmem_cache *snet_ticket_cachep;
+static struct kmem_cache *snet_task_security_cachep;
+
+enum snet_verdict snet_ticket_check(struct snet_info *info)
+{
+	struct snet_ticket *st = NULL;
+	unsigned int h = 0, verdict = SNET_VERDICT_NONE;
+	struct list_head *l = NULL;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode == SNET_TICKET_OFF)
+		goto out;
+
+	tsec = (struct snet_task_security*) current_security();
+
+	h = jhash_2words(info->syscall, info->protocol, 0) % HSIZE;
+	l = &tsec->hash[h];
+
+	read_lock_bh(&tsec->lock);
+	list_for_each_entry(st, l, list) {
+		if (__ticket_check(st, info)) {
+			verdict = st->verdict;
+			pr_debug("snet_ticket found: ticket=%p tsec=%p\n",
+				 st, st->tsec);
+			if (snet_ticket_mode == SNET_TICKET_EXTEND) {
+				mod_timer(&st->timeout,
+					  jiffies + snet_ticket_delay * HZ);
+			}
+			break;
+		}
+	}
+	read_unlock_bh(&tsec->lock);
+out:
+	return verdict;
+}
+
+static void snet_ticket_timeout(unsigned long arg)
+{
+	struct snet_ticket *st = (struct snet_ticket*)arg;
+
+	pr_debug("snet_ticket_timeout: ticket=%p tsec=%p\n", st, st->tsec);
+
+	write_lock_bh(&st->tsec->lock);
+	list_del(&st->list);
+	write_unlock_bh(&st->tsec->lock);
+	kmem_cache_free(snet_ticket_cachep, st);
+	return;
+}
+
+static struct snet_ticket *snet_ticket_alloc(void)
+{
+	struct snet_ticket *st = NULL;
+
+	st = kmem_cache_zalloc(snet_ticket_cachep, GFP_KERNEL);
+	if (st == NULL)
+		goto out;
+
+	INIT_LIST_HEAD(&st->list);
+	init_timer(&st->timeout);
+	st->timeout.expires = snet_ticket_delay * HZ;
+out:
+	return st;
+}
+
+static void snet_ticket_insert(struct snet_ticket *st)
+{
+	unsigned int h;
+	struct list_head *l;
+
+	h = jhash_2words(st->syscall, st->protocol, 0) % HSIZE;
+	l = &(st->tsec->hash[h]);
+
+	st->timeout.expires += jiffies;
+	add_timer(&st->timeout);
+
+	write_lock_bh(&(st->tsec->lock));
+	list_add_tail(&st->list, l);
+	write_unlock_bh(&(st->tsec->lock));
+	return;
+}
+
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict)
+{
+	struct snet_ticket *st;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode == SNET_TICKET_OFF)
+		goto out;
+
+	tsec = (struct snet_task_security*) current_security();
+
+	st = snet_ticket_alloc();
+	if (st == NULL)
+		goto out;
+
+	st->tsec = tsec;
+	snet_ticket_fill(st, info, verdict);
+	setup_timer(&st->timeout, snet_ticket_timeout, (unsigned long)st);
+	snet_ticket_insert(st);
+out:
+	return;
+}
+
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
+{
+	unsigned int index = 0;
+	struct snet_task_security *tsec = NULL;
+
+	tsec = kmem_cache_zalloc(snet_task_security_cachep, gfp);
+	if (tsec == NULL)
+		return -ENOMEM;
+
+	pr_debug("ticket_prepare_creds: pid=%u tsec=%p\n", current->pid, tsec);
+	rwlock_init(&tsec->lock);
+	for (index = 0; index < HSIZE; index++)
+		INIT_LIST_HEAD(&tsec->hash[index]);
+
+	new->security = tsec;
+	return 0;
+}
+
+void snet_cred_free(struct cred *cred)
+{
+	struct snet_task_security *tsec = cred->security;
+	unsigned int index;
+
+	pr_debug("ticket_free_cred: pid=%u tsec=%p\n",  current->pid, tsec);
+
+	write_lock_bh(&tsec->lock);
+	/* destroy all tickets */
+	for (index = 0; index < HSIZE; index++) {
+		struct snet_ticket *st, *tmp;
+		list_for_each_entry_safe(st, tmp, &tsec->hash[index], list) {
+			if (del_timer_sync(&st->timeout)) {
+				pr_debug("ticket_cred_free: [%u] ticket=%p tsec=%p\n",
+					 index, st, st->tsec);
+				list_del(&st->list);
+				kmem_cache_free(snet_ticket_cachep, st);
+			}
+		}
+	}
+	cred->security = NULL;
+	write_unlock_bh(&tsec->lock);
+	kmem_cache_free(snet_task_security_cachep, tsec);
+	return;
+}
+
+int snet_ticket_init(void)
+{
+	unsigned int index = 0;
+	struct cred *cred = (struct cred *) current->real_cred;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode >= SNET_TICKET_INVALID) {
+		printk(KERN_ERR "snet: bad snet_ticket_mode\n");
+		return -EINVAL;
+	}
+
+	if ((snet_ticket_mode == SNET_TICKET_FIX ||
+	    snet_ticket_mode == SNET_TICKET_EXTEND) &&
+	    (snet_ticket_delay == 0)) {
+		printk(KERN_ERR "snet: bad snet_ticket_delay\n");
+		return -EINVAL;
+	}
+
+	/* snet_ticket_cachep is not destroyed */
+	snet_ticket_cachep = kmem_cache_create("snet_ticket",
+					       sizeof(struct snet_ticket),
+					       0, SLAB_PANIC, NULL);
+	/* snet_task_security_cachep is not destroyed */
+	snet_task_security_cachep = kmem_cache_create("snet_task_security",
+						      sizeof(struct snet_task_security),
+						      0, SLAB_PANIC, NULL);
+
+	tsec = kmem_cache_zalloc(snet_task_security_cachep, GFP_KERNEL);
+	if (tsec == NULL)
+		return -ENOMEM;
+
+	rwlock_init(&tsec->lock);
+	for (index = 0; index < HSIZE; index++)
+		INIT_LIST_HEAD(&tsec->hash[index]);
+
+	cred->security = tsec;
+	return 0;
+}
diff --git a/security/snet/snet_ticket.h b/security/snet/snet_ticket.h
new file mode 100644
index 0000000..b6d1020
--- /dev/null
+++ b/security/snet/snet_ticket.h
@@ -0,0 +1,37 @@
+#ifndef _SNET_TICKET_H
+#define _SNET_TICKET_H
+
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/cred.h>
+#include <linux/snet.h>
+
+struct snet_task_security {
+	struct list_head hash[16];
+	rwlock_t lock;
+};
+
+struct snet_ticket {
+	struct list_head list;
+	struct snet_task_security *tsec;
+	struct timer_list timeout;
+
+	enum snet_syscall syscall;
+	u8 protocol;
+	u8 family;
+	int type;
+	struct snet_sock_half src;
+	struct snet_sock_half dst;
+	enum snet_verdict verdict;
+};
+
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+void snet_cred_free(struct cred *cred);
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
+enum snet_verdict snet_ticket_check(struct snet_info *info);
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict);
+int snet_ticket_init(void);
+
+#endif	/* _SNET_TICKET_H */
diff --git a/security/snet/snet_ticket_helper.c b/security/snet/snet_ticket_helper.c
new file mode 100644
index 0000000..2dc9b94
--- /dev/null
+++ b/security/snet/snet_ticket_helper.c
@@ -0,0 +1,127 @@
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_utils.h"
+
+static int check_create(struct snet_ticket *st, struct snet_info *info)
+{
+	return (st->type == info->type);
+}
+
+static int check_src(struct snet_ticket *st, struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		if ((st->src.u3.ip == info->src.u3.ip) &&
+		    (st->src.u.port == info->src.u.port))
+			return 1;
+		break;
+	case AF_INET6:
+		if ((!memcmp(&st->src.u3.ip6, &info->src.u3.ip6,
+			     sizeof(info->src.u3.ip6))) &&
+		    (st->src.u.port == info->src.u.port))
+			return 1;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int check_dst(struct snet_ticket *st, struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		if ((st->dst.u3.ip == info->dst.u3.ip) &&
+		    (st->dst.u.port == info->dst.u.port))
+			return 1;
+		break;
+	case AF_INET6:
+		if ((!memcmp(&st->dst.u3.ip6, &info->dst.u3.ip6,
+			     sizeof(info->dst.u3.ip6))) &&
+		    (st->dst.u.port == info->dst.u.port))
+			return 1;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int check_src_and_dst(struct snet_ticket *st, struct snet_info *info)
+{
+	return (check_src(st, info) && check_dst(st, info));
+}
+
+static int check_none(struct snet_ticket *st, struct snet_info *info)
+{
+	return 0;
+}
+
+int __ticket_check(struct snet_ticket *st, struct snet_info *info)
+{
+	static int (*ticket_df[])(struct snet_ticket *, struct snet_info *) = {
+		[SNET_SOCKET_CREATE]		= &check_create,
+		[SNET_SOCKET_BIND]		= &check_src,
+		[SNET_SOCKET_CONNECT]		= &check_dst,
+		[SNET_SOCKET_LISTEN]		= &check_src,
+		[SNET_SOCKET_ACCEPT]		= &check_src,
+		[SNET_SOCKET_POST_ACCEPT]	= &check_none,
+		[SNET_SOCKET_SENDMSG]		= &check_src_and_dst,
+		[SNET_SOCKET_RECVMSG]		= &check_src_and_dst,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= &check_src_and_dst,
+		[SNET_SOCKET_CLOSE]		= &check_none,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return 0;
+	else {
+		if ((st->syscall == info->syscall) &&
+		    (st->protocol == info->protocol) &&
+		    (st->family == info->family) &&
+		    ticket_df[info->syscall](st, info))
+			return 1;
+		else
+			return 0;
+	}
+}
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+		      enum snet_verdict verdict)
+{
+	st->syscall = info->syscall;
+	st->protocol = info->protocol;
+	st->family = info->family;
+	st->src.u.port = info->src.u.port;
+	st->dst.u.port = info->dst.u.port;
+	st->verdict = verdict;
+
+	switch (info->family) {
+	case AF_INET:
+		st->src.u3.ip = info->src.u3.ip;
+		st->dst.u3.ip = info->dst.u3.ip;
+		pr_debug("ticket=%p [syscall=%s protocol=%u "
+			 "family=%u %pI4:%u->%pI4:%u] verdict=%s | tsec=%p pid=%u\n",
+			 st, snet_syscall_name(st->syscall), st->protocol,
+			 st->family, &st->src.u3.ip, st->src.u.port,
+			 &st->dst.u3.ip, st->dst.u.port,
+			 snet_verdict_name(st->verdict), st->tsec, current->pid);
+		break;
+	case AF_INET6:
+		memcpy(&st->src.u3.ip6, &info->src.u3.ip6,
+		       sizeof(info->src.u3.ip6));
+		memcpy(&st->dst.u3.ip6, &info->dst.u3.ip6,
+		       sizeof(info->dst.u3.ip6));
+		pr_debug("ticket=%p [syscall=%s protocol=%u "
+			 "family=%u %pI6:%u->%pI6:%u] verdict=%s | tsec=%p pid=%u\n",
+			 st, snet_syscall_name(st->syscall), st->protocol,
+			 st->family, &st->src.u3.ip6, st->src.u.port,
+			 &st->dst.u3.ip6, st->dst.u.port,
+			 snet_verdict_name(st->verdict), st->tsec, current->pid);
+		break;
+	default:
+		break;
+	}
+	return;
+}
diff --git a/security/snet/snet_ticket_helper.h b/security/snet/snet_ticket_helper.h
new file mode 100644
index 0000000..177bca5
--- /dev/null
+++ b/security/snet/snet_ticket_helper.h
@@ -0,0 +1,8 @@
+#ifndef _SNET_TICKET_HELPER_H
+#define _SNET_TICKET_HELPER_H
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+		      enum snet_verdict verdict);
+int __ticket_check(struct snet_ticket *st, struct snet_info *info);
+
+#endif	/* _SNET_TICKET_HELPER_H */
-- 
1.7.4.1


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

* [RFC v4 08/11] snet: introduce snet_ticket
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (28 preceding siblings ...)
  2011-05-05 13:59 ` [RFC v4 08/11] snet: introduce snet_ticket y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (13 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds the snet's subsystem managing granted-access tickets

snet is using the term 'ticket' for refering to a structure which keeps
informations about verdict, coming from userspace.

generic informations:
  timeout
  syscall
  protocol
  verdict

protocol-dependant informations : (so some infos may not be used)
  address family
  socket type
  source address
  source port
  distant address
  distant port

ticket are attached to the "void *security" pointer of task_struct

there are 3 modes:

0. no ticket - SNET_TICKET_OFF
   every syscalls has to be verified by userspace.

1. timeout fixed - SNET_TICKET_FIX
   for each response from the userspace, we are creating a ticket,
   attached to the task_struct, with the filled informations, and a
   fixed timeout value (10 secs by default).
   then before asking userspace, kernel mecanism is checking existing
   tickets for the task_struct, if there is a granted-access ticket, we
   are using the verdict value attached.
   after the timeout value, the ticket is destroyed.

2. timeout with extendable value - SNET_TICKET_EXTEND
   this is the same mecanism as 1, but every time a ticket is matched
   and used, the timeout value is reset to the default value, so its
   life is extended.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_ticket.c        |  195 ++++++++++++++++++++++++++++++++++++
 security/snet/snet_ticket.h        |   37 +++++++
 security/snet/snet_ticket_helper.c |  127 +++++++++++++++++++++++
 security/snet/snet_ticket_helper.h |    8 ++
 4 files changed, 367 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_ticket.c
 create mode 100644 security/snet/snet_ticket.h
 create mode 100644 security/snet/snet_ticket_helper.c
 create mode 100644 security/snet/snet_ticket_helper.h

diff --git a/security/snet/snet_ticket.c b/security/snet/snet_ticket.c
new file mode 100644
index 0000000..a260412
--- /dev/null
+++ b/security/snet/snet_ticket.c
@@ -0,0 +1,195 @@
+#include <linux/slab.h>
+#include <linux/cred.h>
+#include <linux/jhash.h>
+#include <linux/security.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_ticket_helper.h"
+
+#define HSIZE 16
+
+static struct kmem_cache *snet_ticket_cachep;
+static struct kmem_cache *snet_task_security_cachep;
+
+enum snet_verdict snet_ticket_check(struct snet_info *info)
+{
+	struct snet_ticket *st = NULL;
+	unsigned int h = 0, verdict = SNET_VERDICT_NONE;
+	struct list_head *l = NULL;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode == SNET_TICKET_OFF)
+		goto out;
+
+	tsec = (struct snet_task_security*) current_security();
+
+	h = jhash_2words(info->syscall, info->protocol, 0) % HSIZE;
+	l = &tsec->hash[h];
+
+	read_lock_bh(&tsec->lock);
+	list_for_each_entry(st, l, list) {
+		if (__ticket_check(st, info)) {
+			verdict = st->verdict;
+			pr_debug("snet_ticket found: ticket=%p tsec=%p\n",
+				 st, st->tsec);
+			if (snet_ticket_mode == SNET_TICKET_EXTEND) {
+				mod_timer(&st->timeout,
+					  jiffies + snet_ticket_delay * HZ);
+			}
+			break;
+		}
+	}
+	read_unlock_bh(&tsec->lock);
+out:
+	return verdict;
+}
+
+static void snet_ticket_timeout(unsigned long arg)
+{
+	struct snet_ticket *st = (struct snet_ticket*)arg;
+
+	pr_debug("snet_ticket_timeout: ticket=%p tsec=%p\n", st, st->tsec);
+
+	write_lock_bh(&st->tsec->lock);
+	list_del(&st->list);
+	write_unlock_bh(&st->tsec->lock);
+	kmem_cache_free(snet_ticket_cachep, st);
+	return;
+}
+
+static struct snet_ticket *snet_ticket_alloc(void)
+{
+	struct snet_ticket *st = NULL;
+
+	st = kmem_cache_zalloc(snet_ticket_cachep, GFP_KERNEL);
+	if (st == NULL)
+		goto out;
+
+	INIT_LIST_HEAD(&st->list);
+	init_timer(&st->timeout);
+	st->timeout.expires = snet_ticket_delay * HZ;
+out:
+	return st;
+}
+
+static void snet_ticket_insert(struct snet_ticket *st)
+{
+	unsigned int h;
+	struct list_head *l;
+
+	h = jhash_2words(st->syscall, st->protocol, 0) % HSIZE;
+	l = &(st->tsec->hash[h]);
+
+	st->timeout.expires += jiffies;
+	add_timer(&st->timeout);
+
+	write_lock_bh(&(st->tsec->lock));
+	list_add_tail(&st->list, l);
+	write_unlock_bh(&(st->tsec->lock));
+	return;
+}
+
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict)
+{
+	struct snet_ticket *st;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode == SNET_TICKET_OFF)
+		goto out;
+
+	tsec = (struct snet_task_security*) current_security();
+
+	st = snet_ticket_alloc();
+	if (st == NULL)
+		goto out;
+
+	st->tsec = tsec;
+	snet_ticket_fill(st, info, verdict);
+	setup_timer(&st->timeout, snet_ticket_timeout, (unsigned long)st);
+	snet_ticket_insert(st);
+out:
+	return;
+}
+
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
+{
+	unsigned int index = 0;
+	struct snet_task_security *tsec = NULL;
+
+	tsec = kmem_cache_zalloc(snet_task_security_cachep, gfp);
+	if (tsec == NULL)
+		return -ENOMEM;
+
+	pr_debug("ticket_prepare_creds: pid=%u tsec=%p\n", current->pid, tsec);
+	rwlock_init(&tsec->lock);
+	for (index = 0; index < HSIZE; index++)
+		INIT_LIST_HEAD(&tsec->hash[index]);
+
+	new->security = tsec;
+	return 0;
+}
+
+void snet_cred_free(struct cred *cred)
+{
+	struct snet_task_security *tsec = cred->security;
+	unsigned int index;
+
+	pr_debug("ticket_free_cred: pid=%u tsec=%p\n",  current->pid, tsec);
+
+	write_lock_bh(&tsec->lock);
+	/* destroy all tickets */
+	for (index = 0; index < HSIZE; index++) {
+		struct snet_ticket *st, *tmp;
+		list_for_each_entry_safe(st, tmp, &tsec->hash[index], list) {
+			if (del_timer_sync(&st->timeout)) {
+				pr_debug("ticket_cred_free: [%u] ticket=%p tsec=%p\n",
+					 index, st, st->tsec);
+				list_del(&st->list);
+				kmem_cache_free(snet_ticket_cachep, st);
+			}
+		}
+	}
+	cred->security = NULL;
+	write_unlock_bh(&tsec->lock);
+	kmem_cache_free(snet_task_security_cachep, tsec);
+	return;
+}
+
+int snet_ticket_init(void)
+{
+	unsigned int index = 0;
+	struct cred *cred = (struct cred *) current->real_cred;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode >= SNET_TICKET_INVALID) {
+		printk(KERN_ERR "snet: bad snet_ticket_mode\n");
+		return -EINVAL;
+	}
+
+	if ((snet_ticket_mode == SNET_TICKET_FIX ||
+	    snet_ticket_mode == SNET_TICKET_EXTEND) &&
+	    (snet_ticket_delay == 0)) {
+		printk(KERN_ERR "snet: bad snet_ticket_delay\n");
+		return -EINVAL;
+	}
+
+	/* snet_ticket_cachep is not destroyed */
+	snet_ticket_cachep = kmem_cache_create("snet_ticket",
+					       sizeof(struct snet_ticket),
+					       0, SLAB_PANIC, NULL);
+	/* snet_task_security_cachep is not destroyed */
+	snet_task_security_cachep = kmem_cache_create("snet_task_security",
+						      sizeof(struct snet_task_security),
+						      0, SLAB_PANIC, NULL);
+
+	tsec = kmem_cache_zalloc(snet_task_security_cachep, GFP_KERNEL);
+	if (tsec == NULL)
+		return -ENOMEM;
+
+	rwlock_init(&tsec->lock);
+	for (index = 0; index < HSIZE; index++)
+		INIT_LIST_HEAD(&tsec->hash[index]);
+
+	cred->security = tsec;
+	return 0;
+}
diff --git a/security/snet/snet_ticket.h b/security/snet/snet_ticket.h
new file mode 100644
index 0000000..b6d1020
--- /dev/null
+++ b/security/snet/snet_ticket.h
@@ -0,0 +1,37 @@
+#ifndef _SNET_TICKET_H
+#define _SNET_TICKET_H
+
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/cred.h>
+#include <linux/snet.h>
+
+struct snet_task_security {
+	struct list_head hash[16];
+	rwlock_t lock;
+};
+
+struct snet_ticket {
+	struct list_head list;
+	struct snet_task_security *tsec;
+	struct timer_list timeout;
+
+	enum snet_syscall syscall;
+	u8 protocol;
+	u8 family;
+	int type;
+	struct snet_sock_half src;
+	struct snet_sock_half dst;
+	enum snet_verdict verdict;
+};
+
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+void snet_cred_free(struct cred *cred);
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
+enum snet_verdict snet_ticket_check(struct snet_info *info);
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict);
+int snet_ticket_init(void);
+
+#endif	/* _SNET_TICKET_H */
diff --git a/security/snet/snet_ticket_helper.c b/security/snet/snet_ticket_helper.c
new file mode 100644
index 0000000..2dc9b94
--- /dev/null
+++ b/security/snet/snet_ticket_helper.c
@@ -0,0 +1,127 @@
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_utils.h"
+
+static int check_create(struct snet_ticket *st, struct snet_info *info)
+{
+	return (st->type == info->type);
+}
+
+static int check_src(struct snet_ticket *st, struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		if ((st->src.u3.ip == info->src.u3.ip) &&
+		    (st->src.u.port == info->src.u.port))
+			return 1;
+		break;
+	case AF_INET6:
+		if ((!memcmp(&st->src.u3.ip6, &info->src.u3.ip6,
+			     sizeof(info->src.u3.ip6))) &&
+		    (st->src.u.port == info->src.u.port))
+			return 1;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int check_dst(struct snet_ticket *st, struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		if ((st->dst.u3.ip == info->dst.u3.ip) &&
+		    (st->dst.u.port == info->dst.u.port))
+			return 1;
+		break;
+	case AF_INET6:
+		if ((!memcmp(&st->dst.u3.ip6, &info->dst.u3.ip6,
+			     sizeof(info->dst.u3.ip6))) &&
+		    (st->dst.u.port == info->dst.u.port))
+			return 1;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int check_src_and_dst(struct snet_ticket *st, struct snet_info *info)
+{
+	return (check_src(st, info) && check_dst(st, info));
+}
+
+static int check_none(struct snet_ticket *st, struct snet_info *info)
+{
+	return 0;
+}
+
+int __ticket_check(struct snet_ticket *st, struct snet_info *info)
+{
+	static int (*ticket_df[])(struct snet_ticket *, struct snet_info *) = {
+		[SNET_SOCKET_CREATE]		= &check_create,
+		[SNET_SOCKET_BIND]		= &check_src,
+		[SNET_SOCKET_CONNECT]		= &check_dst,
+		[SNET_SOCKET_LISTEN]		= &check_src,
+		[SNET_SOCKET_ACCEPT]		= &check_src,
+		[SNET_SOCKET_POST_ACCEPT]	= &check_none,
+		[SNET_SOCKET_SENDMSG]		= &check_src_and_dst,
+		[SNET_SOCKET_RECVMSG]		= &check_src_and_dst,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= &check_src_and_dst,
+		[SNET_SOCKET_CLOSE]		= &check_none,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return 0;
+	else {
+		if ((st->syscall == info->syscall) &&
+		    (st->protocol == info->protocol) &&
+		    (st->family == info->family) &&
+		    ticket_df[info->syscall](st, info))
+			return 1;
+		else
+			return 0;
+	}
+}
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+		      enum snet_verdict verdict)
+{
+	st->syscall = info->syscall;
+	st->protocol = info->protocol;
+	st->family = info->family;
+	st->src.u.port = info->src.u.port;
+	st->dst.u.port = info->dst.u.port;
+	st->verdict = verdict;
+
+	switch (info->family) {
+	case AF_INET:
+		st->src.u3.ip = info->src.u3.ip;
+		st->dst.u3.ip = info->dst.u3.ip;
+		pr_debug("ticket=%p [syscall=%s protocol=%u "
+			 "family=%u %pI4:%u->%pI4:%u] verdict=%s | tsec=%p pid=%u\n",
+			 st, snet_syscall_name(st->syscall), st->protocol,
+			 st->family, &st->src.u3.ip, st->src.u.port,
+			 &st->dst.u3.ip, st->dst.u.port,
+			 snet_verdict_name(st->verdict), st->tsec, current->pid);
+		break;
+	case AF_INET6:
+		memcpy(&st->src.u3.ip6, &info->src.u3.ip6,
+		       sizeof(info->src.u3.ip6));
+		memcpy(&st->dst.u3.ip6, &info->dst.u3.ip6,
+		       sizeof(info->dst.u3.ip6));
+		pr_debug("ticket=%p [syscall=%s protocol=%u "
+			 "family=%u %pI6:%u->%pI6:%u] verdict=%s | tsec=%p pid=%u\n",
+			 st, snet_syscall_name(st->syscall), st->protocol,
+			 st->family, &st->src.u3.ip6, st->src.u.port,
+			 &st->dst.u3.ip6, st->dst.u.port,
+			 snet_verdict_name(st->verdict), st->tsec, current->pid);
+		break;
+	default:
+		break;
+	}
+	return;
+}
diff --git a/security/snet/snet_ticket_helper.h b/security/snet/snet_ticket_helper.h
new file mode 100644
index 0000000..177bca5
--- /dev/null
+++ b/security/snet/snet_ticket_helper.h
@@ -0,0 +1,8 @@
+#ifndef _SNET_TICKET_HELPER_H
+#define _SNET_TICKET_HELPER_H
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+		      enum snet_verdict verdict);
+int __ticket_check(struct snet_ticket *st, struct snet_info *info);
+
+#endif	/* _SNET_TICKET_HELPER_H */
-- 
1.7.4.1


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

* [RFC v4 08/11] snet: introduce snet_ticket
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (30 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` [RFC v4 09/11] snet: introduce snet_utils y
                   ` (11 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds the snet's subsystem managing granted-access tickets

snet is using the term 'ticket' for refering to a structure which keeps
informations about verdict, coming from userspace.

generic informations:
  timeout
  syscall
  protocol
  verdict

protocol-dependant informations : (so some infos may not be used)
  address family
  socket type
  source address
  source port
  distant address
  distant port

ticket are attached to the "void *security" pointer of task_struct

there are 3 modes:

0. no ticket - SNET_TICKET_OFF
   every syscalls has to be verified by userspace.

1. timeout fixed - SNET_TICKET_FIX
   for each response from the userspace, we are creating a ticket,
   attached to the task_struct, with the filled informations, and a
   fixed timeout value (10 secs by default).
   then before asking userspace, kernel mecanism is checking existing
   tickets for the task_struct, if there is a granted-access ticket, we
   are using the verdict value attached.
   after the timeout value, the ticket is destroyed.

2. timeout with extendable value - SNET_TICKET_EXTEND
   this is the same mecanism as 1, but every time a ticket is matched
   and used, the timeout value is reset to the default value, so its
   life is extended.

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_ticket.c        |  195 ++++++++++++++++++++++++++++++++++++
 security/snet/snet_ticket.h        |   37 +++++++
 security/snet/snet_ticket_helper.c |  127 +++++++++++++++++++++++
 security/snet/snet_ticket_helper.h |    8 ++
 4 files changed, 367 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_ticket.c
 create mode 100644 security/snet/snet_ticket.h
 create mode 100644 security/snet/snet_ticket_helper.c
 create mode 100644 security/snet/snet_ticket_helper.h

diff --git a/security/snet/snet_ticket.c b/security/snet/snet_ticket.c
new file mode 100644
index 0000000..a260412
--- /dev/null
+++ b/security/snet/snet_ticket.c
@@ -0,0 +1,195 @@
+#include <linux/slab.h>
+#include <linux/cred.h>
+#include <linux/jhash.h>
+#include <linux/security.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_ticket_helper.h"
+
+#define HSIZE 16
+
+static struct kmem_cache *snet_ticket_cachep;
+static struct kmem_cache *snet_task_security_cachep;
+
+enum snet_verdict snet_ticket_check(struct snet_info *info)
+{
+	struct snet_ticket *st = NULL;
+	unsigned int h = 0, verdict = SNET_VERDICT_NONE;
+	struct list_head *l = NULL;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode == SNET_TICKET_OFF)
+		goto out;
+
+	tsec = (struct snet_task_security*) current_security();
+
+	h = jhash_2words(info->syscall, info->protocol, 0) % HSIZE;
+	l = &tsec->hash[h];
+
+	read_lock_bh(&tsec->lock);
+	list_for_each_entry(st, l, list) {
+		if (__ticket_check(st, info)) {
+			verdict = st->verdict;
+			pr_debug("snet_ticket found: ticket=%p tsec=%p\n",
+				 st, st->tsec);
+			if (snet_ticket_mode == SNET_TICKET_EXTEND) {
+				mod_timer(&st->timeout,
+					  jiffies + snet_ticket_delay * HZ);
+			}
+			break;
+		}
+	}
+	read_unlock_bh(&tsec->lock);
+out:
+	return verdict;
+}
+
+static void snet_ticket_timeout(unsigned long arg)
+{
+	struct snet_ticket *st = (struct snet_ticket*)arg;
+
+	pr_debug("snet_ticket_timeout: ticket=%p tsec=%p\n", st, st->tsec);
+
+	write_lock_bh(&st->tsec->lock);
+	list_del(&st->list);
+	write_unlock_bh(&st->tsec->lock);
+	kmem_cache_free(snet_ticket_cachep, st);
+	return;
+}
+
+static struct snet_ticket *snet_ticket_alloc(void)
+{
+	struct snet_ticket *st = NULL;
+
+	st = kmem_cache_zalloc(snet_ticket_cachep, GFP_KERNEL);
+	if (st == NULL)
+		goto out;
+
+	INIT_LIST_HEAD(&st->list);
+	init_timer(&st->timeout);
+	st->timeout.expires = snet_ticket_delay * HZ;
+out:
+	return st;
+}
+
+static void snet_ticket_insert(struct snet_ticket *st)
+{
+	unsigned int h;
+	struct list_head *l;
+
+	h = jhash_2words(st->syscall, st->protocol, 0) % HSIZE;
+	l = &(st->tsec->hash[h]);
+
+	st->timeout.expires += jiffies;
+	add_timer(&st->timeout);
+
+	write_lock_bh(&(st->tsec->lock));
+	list_add_tail(&st->list, l);
+	write_unlock_bh(&(st->tsec->lock));
+	return;
+}
+
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict)
+{
+	struct snet_ticket *st;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode == SNET_TICKET_OFF)
+		goto out;
+
+	tsec = (struct snet_task_security*) current_security();
+
+	st = snet_ticket_alloc();
+	if (st == NULL)
+		goto out;
+
+	st->tsec = tsec;
+	snet_ticket_fill(st, info, verdict);
+	setup_timer(&st->timeout, snet_ticket_timeout, (unsigned long)st);
+	snet_ticket_insert(st);
+out:
+	return;
+}
+
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
+{
+	unsigned int index = 0;
+	struct snet_task_security *tsec = NULL;
+
+	tsec = kmem_cache_zalloc(snet_task_security_cachep, gfp);
+	if (tsec == NULL)
+		return -ENOMEM;
+
+	pr_debug("ticket_prepare_creds: pid=%u tsec=%p\n", current->pid, tsec);
+	rwlock_init(&tsec->lock);
+	for (index = 0; index < HSIZE; index++)
+		INIT_LIST_HEAD(&tsec->hash[index]);
+
+	new->security = tsec;
+	return 0;
+}
+
+void snet_cred_free(struct cred *cred)
+{
+	struct snet_task_security *tsec = cred->security;
+	unsigned int index;
+
+	pr_debug("ticket_free_cred: pid=%u tsec=%p\n",  current->pid, tsec);
+
+	write_lock_bh(&tsec->lock);
+	/* destroy all tickets */
+	for (index = 0; index < HSIZE; index++) {
+		struct snet_ticket *st, *tmp;
+		list_for_each_entry_safe(st, tmp, &tsec->hash[index], list) {
+			if (del_timer_sync(&st->timeout)) {
+				pr_debug("ticket_cred_free: [%u] ticket=%p tsec=%p\n",
+					 index, st, st->tsec);
+				list_del(&st->list);
+				kmem_cache_free(snet_ticket_cachep, st);
+			}
+		}
+	}
+	cred->security = NULL;
+	write_unlock_bh(&tsec->lock);
+	kmem_cache_free(snet_task_security_cachep, tsec);
+	return;
+}
+
+int snet_ticket_init(void)
+{
+	unsigned int index = 0;
+	struct cred *cred = (struct cred *) current->real_cred;
+	struct snet_task_security *tsec = NULL;
+
+	if (snet_ticket_mode >= SNET_TICKET_INVALID) {
+		printk(KERN_ERR "snet: bad snet_ticket_mode\n");
+		return -EINVAL;
+	}
+
+	if ((snet_ticket_mode == SNET_TICKET_FIX ||
+	    snet_ticket_mode == SNET_TICKET_EXTEND) &&
+	    (snet_ticket_delay == 0)) {
+		printk(KERN_ERR "snet: bad snet_ticket_delay\n");
+		return -EINVAL;
+	}
+
+	/* snet_ticket_cachep is not destroyed */
+	snet_ticket_cachep = kmem_cache_create("snet_ticket",
+					       sizeof(struct snet_ticket),
+					       0, SLAB_PANIC, NULL);
+	/* snet_task_security_cachep is not destroyed */
+	snet_task_security_cachep = kmem_cache_create("snet_task_security",
+						      sizeof(struct snet_task_security),
+						      0, SLAB_PANIC, NULL);
+
+	tsec = kmem_cache_zalloc(snet_task_security_cachep, GFP_KERNEL);
+	if (tsec == NULL)
+		return -ENOMEM;
+
+	rwlock_init(&tsec->lock);
+	for (index = 0; index < HSIZE; index++)
+		INIT_LIST_HEAD(&tsec->hash[index]);
+
+	cred->security = tsec;
+	return 0;
+}
diff --git a/security/snet/snet_ticket.h b/security/snet/snet_ticket.h
new file mode 100644
index 0000000..b6d1020
--- /dev/null
+++ b/security/snet/snet_ticket.h
@@ -0,0 +1,37 @@
+#ifndef _SNET_TICKET_H
+#define _SNET_TICKET_H
+
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/cred.h>
+#include <linux/snet.h>
+
+struct snet_task_security {
+	struct list_head hash[16];
+	rwlock_t lock;
+};
+
+struct snet_ticket {
+	struct list_head list;
+	struct snet_task_security *tsec;
+	struct timer_list timeout;
+
+	enum snet_syscall syscall;
+	u8 protocol;
+	u8 family;
+	int type;
+	struct snet_sock_half src;
+	struct snet_sock_half dst;
+	enum snet_verdict verdict;
+};
+
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+void snet_cred_free(struct cred *cred);
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
+enum snet_verdict snet_ticket_check(struct snet_info *info);
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict);
+int snet_ticket_init(void);
+
+#endif	/* _SNET_TICKET_H */
diff --git a/security/snet/snet_ticket_helper.c b/security/snet/snet_ticket_helper.c
new file mode 100644
index 0000000..2dc9b94
--- /dev/null
+++ b/security/snet/snet_ticket_helper.c
@@ -0,0 +1,127 @@
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_utils.h"
+
+static int check_create(struct snet_ticket *st, struct snet_info *info)
+{
+	return (st->type == info->type);
+}
+
+static int check_src(struct snet_ticket *st, struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		if ((st->src.u3.ip == info->src.u3.ip) &&
+		    (st->src.u.port == info->src.u.port))
+			return 1;
+		break;
+	case AF_INET6:
+		if ((!memcmp(&st->src.u3.ip6, &info->src.u3.ip6,
+			     sizeof(info->src.u3.ip6))) &&
+		    (st->src.u.port == info->src.u.port))
+			return 1;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int check_dst(struct snet_ticket *st, struct snet_info *info)
+{
+	switch (info->family) {
+	case AF_INET:
+		if ((st->dst.u3.ip == info->dst.u3.ip) &&
+		    (st->dst.u.port == info->dst.u.port))
+			return 1;
+		break;
+	case AF_INET6:
+		if ((!memcmp(&st->dst.u3.ip6, &info->dst.u3.ip6,
+			     sizeof(info->dst.u3.ip6))) &&
+		    (st->dst.u.port == info->dst.u.port))
+			return 1;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int check_src_and_dst(struct snet_ticket *st, struct snet_info *info)
+{
+	return (check_src(st, info) && check_dst(st, info));
+}
+
+static int check_none(struct snet_ticket *st, struct snet_info *info)
+{
+	return 0;
+}
+
+int __ticket_check(struct snet_ticket *st, struct snet_info *info)
+{
+	static int (*ticket_df[])(struct snet_ticket *, struct snet_info *) = {
+		[SNET_SOCKET_CREATE]		= &check_create,
+		[SNET_SOCKET_BIND]		= &check_src,
+		[SNET_SOCKET_CONNECT]		= &check_dst,
+		[SNET_SOCKET_LISTEN]		= &check_src,
+		[SNET_SOCKET_ACCEPT]		= &check_src,
+		[SNET_SOCKET_POST_ACCEPT]	= &check_none,
+		[SNET_SOCKET_SENDMSG]		= &check_src_and_dst,
+		[SNET_SOCKET_RECVMSG]		= &check_src_and_dst,
+		[SNET_SOCKET_SOCK_RCV_SKB]	= &check_src_and_dst,
+		[SNET_SOCKET_CLOSE]		= &check_none,
+	};
+
+	if (info->syscall >= SNET_NR_SOCKET_TYPES)
+		return 0;
+	else {
+		if ((st->syscall == info->syscall) &&
+		    (st->protocol == info->protocol) &&
+		    (st->family == info->family) &&
+		    ticket_df[info->syscall](st, info))
+			return 1;
+		else
+			return 0;
+	}
+}
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+		      enum snet_verdict verdict)
+{
+	st->syscall = info->syscall;
+	st->protocol = info->protocol;
+	st->family = info->family;
+	st->src.u.port = info->src.u.port;
+	st->dst.u.port = info->dst.u.port;
+	st->verdict = verdict;
+
+	switch (info->family) {
+	case AF_INET:
+		st->src.u3.ip = info->src.u3.ip;
+		st->dst.u3.ip = info->dst.u3.ip;
+		pr_debug("ticket=%p [syscall=%s protocol=%u "
+			 "family=%u %pI4:%u->%pI4:%u] verdict=%s | tsec=%p pid=%u\n",
+			 st, snet_syscall_name(st->syscall), st->protocol,
+			 st->family, &st->src.u3.ip, st->src.u.port,
+			 &st->dst.u3.ip, st->dst.u.port,
+			 snet_verdict_name(st->verdict), st->tsec, current->pid);
+		break;
+	case AF_INET6:
+		memcpy(&st->src.u3.ip6, &info->src.u3.ip6,
+		       sizeof(info->src.u3.ip6));
+		memcpy(&st->dst.u3.ip6, &info->dst.u3.ip6,
+		       sizeof(info->dst.u3.ip6));
+		pr_debug("ticket=%p [syscall=%s protocol=%u "
+			 "family=%u %pI6:%u->%pI6:%u] verdict=%s | tsec=%p pid=%u\n",
+			 st, snet_syscall_name(st->syscall), st->protocol,
+			 st->family, &st->src.u3.ip6, st->src.u.port,
+			 &st->dst.u3.ip6, st->dst.u.port,
+			 snet_verdict_name(st->verdict), st->tsec, current->pid);
+		break;
+	default:
+		break;
+	}
+	return;
+}
diff --git a/security/snet/snet_ticket_helper.h b/security/snet/snet_ticket_helper.h
new file mode 100644
index 0000000..177bca5
--- /dev/null
+++ b/security/snet/snet_ticket_helper.h
@@ -0,0 +1,8 @@
+#ifndef _SNET_TICKET_HELPER_H
+#define _SNET_TICKET_HELPER_H
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+		      enum snet_verdict verdict);
+int __ticket_check(struct snet_ticket *st, struct snet_info *info);
+
+#endif	/* _SNET_TICKET_HELPER_H */
-- 
1.7.4.1


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

* [RFC v4 09/11] snet: introduce snet_utils
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (32 preceding siblings ...)
  2011-05-05 13:59 ` [RFC v4 09/11] snet: introduce snet_utils y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (9 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch provides helper functions for other subsystems

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_utils.c |   39 +++++++++++++++++++++++++++++++++++++++
 security/snet/snet_utils.h |    9 +++++++++
 2 files changed, 48 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_utils.c
 create mode 100644 security/snet/snet_utils.h

diff --git a/security/snet/snet_utils.c b/security/snet/snet_utils.c
new file mode 100644
index 0000000..4e80263
--- /dev/null
+++ b/security/snet/snet_utils.c
@@ -0,0 +1,39 @@
+#include <linux/types.h>
+#include <linux/snet.h>
+
+const char *snet_verdict_name(const enum snet_verdict cmd)
+{
+	static const char *const verdict_name[] = {
+		[SNET_VERDICT_GRANT]	= "Grant",
+		[SNET_VERDICT_DENY]	= "Deny",
+		[SNET_VERDICT_PENDING]	= "Pending",
+		[SNET_VERDICT_NONE]	= "None",
+		[SNET_VERDICT_INVALID]	= "Invalid",
+	};
+
+	if (cmd >= SNET_NR_VERDICT_TYPES)
+		return "ERROR";
+	else
+		return verdict_name[cmd];
+}
+
+const char *snet_syscall_name(const enum snet_syscall sys)
+{
+	static const char *const syscall_name[] = {
+		[SNET_SOCKET_CREATE]		= "Create",
+		[SNET_SOCKET_BIND]		= "Bind",
+		[SNET_SOCKET_CONNECT]		= "Connect",
+		[SNET_SOCKET_LISTEN]		= "Listen",
+		[SNET_SOCKET_ACCEPT]		= "Accept",
+		[SNET_SOCKET_POST_ACCEPT]	= "Post Accept",
+		[SNET_SOCKET_SENDMSG]		= "Sendmsg",
+		[SNET_SOCKET_RECVMSG]		= "Recvmsg",
+		[SNET_SOCKET_SOCK_RCV_SKB]	= "Sock Rcv Skb",
+		[SNET_SOCKET_CLOSE]		= "Close",
+	};
+
+	if (sys >= SNET_NR_SOCKET_TYPES)
+		return "ERROR";
+	else
+		return syscall_name[sys];
+}
diff --git a/security/snet/snet_utils.h b/security/snet/snet_utils.h
new file mode 100644
index 0000000..01e515f
--- /dev/null
+++ b/security/snet/snet_utils.h
@@ -0,0 +1,9 @@
+#ifndef _SNET_UTILS_H
+#define _SNET_UTILS_H
+
+#include <linux/skbuff.h>
+
+const char *snet_verdict_name(const enum snet_verdict cmd);
+const char *snet_syscall_name(const enum snet_syscall sys);
+
+#endif	/* _SNET_UTILS_H */
-- 
1.7.4.1

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

* [RFC v4 09/11] snet: introduce snet_utils
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (33 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (8 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch provides helper functions for other subsystems

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_utils.c |   39 +++++++++++++++++++++++++++++++++++++++
 security/snet/snet_utils.h |    9 +++++++++
 2 files changed, 48 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_utils.c
 create mode 100644 security/snet/snet_utils.h

diff --git a/security/snet/snet_utils.c b/security/snet/snet_utils.c
new file mode 100644
index 0000000..4e80263
--- /dev/null
+++ b/security/snet/snet_utils.c
@@ -0,0 +1,39 @@
+#include <linux/types.h>
+#include <linux/snet.h>
+
+const char *snet_verdict_name(const enum snet_verdict cmd)
+{
+	static const char *const verdict_name[] = {
+		[SNET_VERDICT_GRANT]	= "Grant",
+		[SNET_VERDICT_DENY]	= "Deny",
+		[SNET_VERDICT_PENDING]	= "Pending",
+		[SNET_VERDICT_NONE]	= "None",
+		[SNET_VERDICT_INVALID]	= "Invalid",
+	};
+
+	if (cmd >= SNET_NR_VERDICT_TYPES)
+		return "ERROR";
+	else
+		return verdict_name[cmd];
+}
+
+const char *snet_syscall_name(const enum snet_syscall sys)
+{
+	static const char *const syscall_name[] = {
+		[SNET_SOCKET_CREATE]		= "Create",
+		[SNET_SOCKET_BIND]		= "Bind",
+		[SNET_SOCKET_CONNECT]		= "Connect",
+		[SNET_SOCKET_LISTEN]		= "Listen",
+		[SNET_SOCKET_ACCEPT]		= "Accept",
+		[SNET_SOCKET_POST_ACCEPT]	= "Post Accept",
+		[SNET_SOCKET_SENDMSG]		= "Sendmsg",
+		[SNET_SOCKET_RECVMSG]		= "Recvmsg",
+		[SNET_SOCKET_SOCK_RCV_SKB]	= "Sock Rcv Skb",
+		[SNET_SOCKET_CLOSE]		= "Close",
+	};
+
+	if (sys >= SNET_NR_SOCKET_TYPES)
+		return "ERROR";
+	else
+		return syscall_name[sys];
+}
diff --git a/security/snet/snet_utils.h b/security/snet/snet_utils.h
new file mode 100644
index 0000000..01e515f
--- /dev/null
+++ b/security/snet/snet_utils.h
@@ -0,0 +1,9 @@
+#ifndef _SNET_UTILS_H
+#define _SNET_UTILS_H
+
+#include <linux/skbuff.h>
+
+const char *snet_verdict_name(const enum snet_verdict cmd);
+const char *snet_syscall_name(const enum snet_syscall sys);
+
+#endif	/* _SNET_UTILS_H */
-- 
1.7.4.1


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

* [RFC v4 09/11] snet: introduce snet_utils
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (34 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` [RFC v4 10/11] snet: introduce snet_stats y
                   ` (7 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch provides helper functions for other subsystems

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_utils.c |   39 +++++++++++++++++++++++++++++++++++++++
 security/snet/snet_utils.h |    9 +++++++++
 2 files changed, 48 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_utils.c
 create mode 100644 security/snet/snet_utils.h

diff --git a/security/snet/snet_utils.c b/security/snet/snet_utils.c
new file mode 100644
index 0000000..4e80263
--- /dev/null
+++ b/security/snet/snet_utils.c
@@ -0,0 +1,39 @@
+#include <linux/types.h>
+#include <linux/snet.h>
+
+const char *snet_verdict_name(const enum snet_verdict cmd)
+{
+	static const char *const verdict_name[] = {
+		[SNET_VERDICT_GRANT]	= "Grant",
+		[SNET_VERDICT_DENY]	= "Deny",
+		[SNET_VERDICT_PENDING]	= "Pending",
+		[SNET_VERDICT_NONE]	= "None",
+		[SNET_VERDICT_INVALID]	= "Invalid",
+	};
+
+	if (cmd >= SNET_NR_VERDICT_TYPES)
+		return "ERROR";
+	else
+		return verdict_name[cmd];
+}
+
+const char *snet_syscall_name(const enum snet_syscall sys)
+{
+	static const char *const syscall_name[] = {
+		[SNET_SOCKET_CREATE]		= "Create",
+		[SNET_SOCKET_BIND]		= "Bind",
+		[SNET_SOCKET_CONNECT]		= "Connect",
+		[SNET_SOCKET_LISTEN]		= "Listen",
+		[SNET_SOCKET_ACCEPT]		= "Accept",
+		[SNET_SOCKET_POST_ACCEPT]	= "Post Accept",
+		[SNET_SOCKET_SENDMSG]		= "Sendmsg",
+		[SNET_SOCKET_RECVMSG]		= "Recvmsg",
+		[SNET_SOCKET_SOCK_RCV_SKB]	= "Sock Rcv Skb",
+		[SNET_SOCKET_CLOSE]		= "Close",
+	};
+
+	if (sys >= SNET_NR_SOCKET_TYPES)
+		return "ERROR";
+	else
+		return syscall_name[sys];
+}
diff --git a/security/snet/snet_utils.h b/security/snet/snet_utils.h
new file mode 100644
index 0000000..01e515f
--- /dev/null
+++ b/security/snet/snet_utils.h
@@ -0,0 +1,9 @@
+#ifndef _SNET_UTILS_H
+#define _SNET_UTILS_H
+
+#include <linux/skbuff.h>
+
+const char *snet_verdict_name(const enum snet_verdict cmd);
+const char *snet_syscall_name(const enum snet_syscall sys);
+
+#endif	/* _SNET_UTILS_H */
-- 
1.7.4.1


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

* [RFC v4 09/11] snet: introduce snet_utils
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (31 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (10 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

This patch provides helper functions for other subsystems

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_utils.c |   39 +++++++++++++++++++++++++++++++++++++++
 security/snet/snet_utils.h |    9 +++++++++
 2 files changed, 48 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_utils.c
 create mode 100644 security/snet/snet_utils.h

diff --git a/security/snet/snet_utils.c b/security/snet/snet_utils.c
new file mode 100644
index 0000000..4e80263
--- /dev/null
+++ b/security/snet/snet_utils.c
@@ -0,0 +1,39 @@
+#include <linux/types.h>
+#include <linux/snet.h>
+
+const char *snet_verdict_name(const enum snet_verdict cmd)
+{
+	static const char *const verdict_name[] = {
+		[SNET_VERDICT_GRANT]	= "Grant",
+		[SNET_VERDICT_DENY]	= "Deny",
+		[SNET_VERDICT_PENDING]	= "Pending",
+		[SNET_VERDICT_NONE]	= "None",
+		[SNET_VERDICT_INVALID]	= "Invalid",
+	};
+
+	if (cmd >= SNET_NR_VERDICT_TYPES)
+		return "ERROR";
+	else
+		return verdict_name[cmd];
+}
+
+const char *snet_syscall_name(const enum snet_syscall sys)
+{
+	static const char *const syscall_name[] = {
+		[SNET_SOCKET_CREATE]		= "Create",
+		[SNET_SOCKET_BIND]		= "Bind",
+		[SNET_SOCKET_CONNECT]		= "Connect",
+		[SNET_SOCKET_LISTEN]		= "Listen",
+		[SNET_SOCKET_ACCEPT]		= "Accept",
+		[SNET_SOCKET_POST_ACCEPT]	= "Post Accept",
+		[SNET_SOCKET_SENDMSG]		= "Sendmsg",
+		[SNET_SOCKET_RECVMSG]		= "Recvmsg",
+		[SNET_SOCKET_SOCK_RCV_SKB]	= "Sock Rcv Skb",
+		[SNET_SOCKET_CLOSE]		= "Close",
+	};
+
+	if (sys >= SNET_NR_SOCKET_TYPES)
+		return "ERROR";
+	else
+		return syscall_name[sys];
+}
diff --git a/security/snet/snet_utils.h b/security/snet/snet_utils.h
new file mode 100644
index 0000000..01e515f
--- /dev/null
+++ b/security/snet/snet_utils.h
@@ -0,0 +1,9 @@
+#ifndef _SNET_UTILS_H
+#define _SNET_UTILS_H
+
+#include <linux/skbuff.h>
+
+const char *snet_verdict_name(const enum snet_verdict cmd);
+const char *snet_syscall_name(const enum snet_syscall sys);
+
+#endif	/* _SNET_UTILS_H */
-- 
1.7.4.1


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

* [RFC v4 10/11] snet: introduce snet_stats
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (37 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (4 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds a /proc/snet/snet_stats entry to show statistics about
snet. this patch adds also the functions to manage the statistics.

 Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_stats.c |   65 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_stats.h |   57 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_stats.c
 create mode 100644 security/snet/snet_stats.h

diff --git a/security/snet/snet_stats.c b/security/snet/snet_stats.c
new file mode 100644
index 0000000..b946dda
--- /dev/null
+++ b/security/snet/snet_stats.c
@@ -0,0 +1,65 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <net/net_namespace.h>
+#include "snet_stats.h"
+#include "snet_utils.h"
+
+extern struct net init_net;
+
+#define SNET_STATS_PROC_DIR	"snet"
+#define SNET_STATS_PROC_INFO	"snet_stats"
+
+static struct proc_dir_entry *snet_stats_dir;
+static struct proc_dir_entry *snet_stats_pde;
+struct snet_statistics snet_stats;
+
+static int snet_stats_show(struct seq_file *m, void *v)
+{
+	unsigned int sys = 0;
+	seq_printf(m, "snet statistics\n");
+
+	for (sys = SNET_SOCKET_CREATE; sys < SNET_NR_SOCKET_TYPES; sys++) {
+		unsigned int v = 0;
+		seq_printf(m, "%s {", snet_syscall_name(sys));
+
+		seq_printf(m, " Exec: %u ", SNET_STATS_GET(SNET_STATS_EXEC, sys));
+		seq_printf(m, " Unreg: %u ", SNET_STATS_GET(SNET_STATS_UNREG, sys));
+		for (v = SNET_VERDICT_GRANT; v < SNET_NR_VERDICT_TYPES; v++) {
+			seq_printf(m, " %s: %u ", snet_verdict_name(v),
+				   SNET_STATS_GET(v, sys));
+		}
+		seq_printf(m, " Reg Error: %u ", SNET_STATS_GET(SNET_STATS_REG_ERROR, sys));
+		seq_printf(m, "}\n");
+	}
+	return 0;
+}
+
+static int snet_stats_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, snet_stats_show, NULL);
+}
+
+static const struct file_operations snet_stats_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= snet_stats_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+void snet_stats_init(void)
+{
+	snet_stats_dir = proc_mkdir(SNET_STATS_PROC_DIR, init_net.proc_net);
+	if (snet_stats_dir == NULL) {
+		printk(KERN_ERR "snet: can't create /proc/%s\n",
+		       SNET_STATS_PROC_DIR);
+		goto out;
+	}
+
+	snet_stats_pde = proc_create(SNET_STATS_PROC_INFO, 0644, snet_stats_dir,
+				     &snet_stats_proc_fops);
+out:
+	return;
+}
diff --git a/security/snet/snet_stats.h b/security/snet/snet_stats.h
new file mode 100644
index 0000000..05ab06d
--- /dev/null
+++ b/security/snet/snet_stats.h
@@ -0,0 +1,57 @@
+#ifndef _SNET_STATS_H
+#define _SNET_STATS_H
+
+#include <linux/snet.h>
+
+enum {
+	SNET_STATS_REG_GRANT = 0,
+	SNET_STATS_REG_DENY,
+	SNET_STATS_REG_PENDING,
+	SNET_STATS_REG_NONE,
+	SNET_STATS_REG_INVALID,
+	SNET_STATS_REG_ERROR,
+	SNET_STATS_UNREG,
+	SNET_STATS_EXEC,
+	__SNET_STATS_MAX
+};
+
+#define SNET_STATS_MAX __SNET_STATS_MAX
+
+struct snet_statistics {
+	atomic_t	mibs[SNET_NR_SOCKET_TYPES+1][SNET_STATS_MAX];
+};
+
+extern struct snet_statistics snet_stats;
+#define SNET_STATS_INC(field, syscall)	(atomic_inc(&snet_stats.mibs[syscall][field]))
+#define SNET_STATS_DEC(field, syscall)	(atomic_dec(&snet_stats.mibs[syscall][field]))
+#define SNET_STATS_GET(field, syscall)	(atomic_read(&snet_stats.mibs[syscall][field]))
+
+void snet_stats_init(void);
+
+static inline void snet_stats_inc_reg(const enum snet_verdict verdict,
+				      const enum snet_syscall syscall)
+{
+	switch (verdict) {
+	case SNET_VERDICT_GRANT:
+		SNET_STATS_INC(SNET_STATS_REG_GRANT, syscall);
+		break;
+	case SNET_VERDICT_DENY:
+		SNET_STATS_INC(SNET_STATS_REG_DENY, syscall);
+		break;
+	case SNET_VERDICT_PENDING:
+		SNET_STATS_INC(SNET_STATS_REG_PENDING, syscall);
+		break;
+	case SNET_VERDICT_NONE:
+		SNET_STATS_INC(SNET_STATS_REG_NONE, syscall);
+		break;
+	case SNET_VERDICT_INVALID:
+		SNET_STATS_INC(SNET_STATS_REG_INVALID, syscall);
+		break;
+	default:
+		SNET_STATS_INC(SNET_STATS_REG_ERROR, syscall);
+		break;
+	}
+	return;
+};
+
+#endif
-- 
1.7.4.1

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

* [RFC v4 10/11] snet: introduce snet_stats
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (36 preceding siblings ...)
  2011-05-05 13:59 ` [RFC v4 10/11] snet: introduce snet_stats y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (5 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds a /proc/snet/snet_stats entry to show statistics about
snet. this patch adds also the functions to manage the statistics.

 Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_stats.c |   65 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_stats.h |   57 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_stats.c
 create mode 100644 security/snet/snet_stats.h

diff --git a/security/snet/snet_stats.c b/security/snet/snet_stats.c
new file mode 100644
index 0000000..b946dda
--- /dev/null
+++ b/security/snet/snet_stats.c
@@ -0,0 +1,65 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <net/net_namespace.h>
+#include "snet_stats.h"
+#include "snet_utils.h"
+
+extern struct net init_net;
+
+#define SNET_STATS_PROC_DIR	"snet"
+#define SNET_STATS_PROC_INFO	"snet_stats"
+
+static struct proc_dir_entry *snet_stats_dir;
+static struct proc_dir_entry *snet_stats_pde;
+struct snet_statistics snet_stats;
+
+static int snet_stats_show(struct seq_file *m, void *v)
+{
+	unsigned int sys = 0;
+	seq_printf(m, "snet statistics\n");
+
+	for (sys = SNET_SOCKET_CREATE; sys < SNET_NR_SOCKET_TYPES; sys++) {
+		unsigned int v = 0;
+		seq_printf(m, "%s {", snet_syscall_name(sys));
+
+		seq_printf(m, " Exec: %u ", SNET_STATS_GET(SNET_STATS_EXEC, sys));
+		seq_printf(m, " Unreg: %u ", SNET_STATS_GET(SNET_STATS_UNREG, sys));
+		for (v = SNET_VERDICT_GRANT; v < SNET_NR_VERDICT_TYPES; v++) {
+			seq_printf(m, " %s: %u ", snet_verdict_name(v),
+				   SNET_STATS_GET(v, sys));
+		}
+		seq_printf(m, " Reg Error: %u ", SNET_STATS_GET(SNET_STATS_REG_ERROR, sys));
+		seq_printf(m, "}\n");
+	}
+	return 0;
+}
+
+static int snet_stats_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, snet_stats_show, NULL);
+}
+
+static const struct file_operations snet_stats_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= snet_stats_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+void snet_stats_init(void)
+{
+	snet_stats_dir = proc_mkdir(SNET_STATS_PROC_DIR, init_net.proc_net);
+	if (snet_stats_dir == NULL) {
+		printk(KERN_ERR "snet: can't create /proc/%s\n",
+		       SNET_STATS_PROC_DIR);
+		goto out;
+	}
+
+	snet_stats_pde = proc_create(SNET_STATS_PROC_INFO, 0644, snet_stats_dir,
+				     &snet_stats_proc_fops);
+out:
+	return;
+}
diff --git a/security/snet/snet_stats.h b/security/snet/snet_stats.h
new file mode 100644
index 0000000..05ab06d
--- /dev/null
+++ b/security/snet/snet_stats.h
@@ -0,0 +1,57 @@
+#ifndef _SNET_STATS_H
+#define _SNET_STATS_H
+
+#include <linux/snet.h>
+
+enum {
+	SNET_STATS_REG_GRANT = 0,
+	SNET_STATS_REG_DENY,
+	SNET_STATS_REG_PENDING,
+	SNET_STATS_REG_NONE,
+	SNET_STATS_REG_INVALID,
+	SNET_STATS_REG_ERROR,
+	SNET_STATS_UNREG,
+	SNET_STATS_EXEC,
+	__SNET_STATS_MAX
+};
+
+#define SNET_STATS_MAX __SNET_STATS_MAX
+
+struct snet_statistics {
+	atomic_t	mibs[SNET_NR_SOCKET_TYPES+1][SNET_STATS_MAX];
+};
+
+extern struct snet_statistics snet_stats;
+#define SNET_STATS_INC(field, syscall)	(atomic_inc(&snet_stats.mibs[syscall][field]))
+#define SNET_STATS_DEC(field, syscall)	(atomic_dec(&snet_stats.mibs[syscall][field]))
+#define SNET_STATS_GET(field, syscall)	(atomic_read(&snet_stats.mibs[syscall][field]))
+
+void snet_stats_init(void);
+
+static inline void snet_stats_inc_reg(const enum snet_verdict verdict,
+				      const enum snet_syscall syscall)
+{
+	switch (verdict) {
+	case SNET_VERDICT_GRANT:
+		SNET_STATS_INC(SNET_STATS_REG_GRANT, syscall);
+		break;
+	case SNET_VERDICT_DENY:
+		SNET_STATS_INC(SNET_STATS_REG_DENY, syscall);
+		break;
+	case SNET_VERDICT_PENDING:
+		SNET_STATS_INC(SNET_STATS_REG_PENDING, syscall);
+		break;
+	case SNET_VERDICT_NONE:
+		SNET_STATS_INC(SNET_STATS_REG_NONE, syscall);
+		break;
+	case SNET_VERDICT_INVALID:
+		SNET_STATS_INC(SNET_STATS_REG_INVALID, syscall);
+		break;
+	default:
+		SNET_STATS_INC(SNET_STATS_REG_ERROR, syscall);
+		break;
+	}
+	return;
+};
+
+#endif
-- 
1.7.4.1


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

* [RFC v4 10/11] snet: introduce snet_stats
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (38 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` [RFC v4 11/11] snet: introduce security/snet, Makefile and Kconfig changes y
                   ` (3 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds a /proc/snet/snet_stats entry to show statistics about
snet. this patch adds also the functions to manage the statistics.

 Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_stats.c |   65 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_stats.h |   57 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_stats.c
 create mode 100644 security/snet/snet_stats.h

diff --git a/security/snet/snet_stats.c b/security/snet/snet_stats.c
new file mode 100644
index 0000000..b946dda
--- /dev/null
+++ b/security/snet/snet_stats.c
@@ -0,0 +1,65 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <net/net_namespace.h>
+#include "snet_stats.h"
+#include "snet_utils.h"
+
+extern struct net init_net;
+
+#define SNET_STATS_PROC_DIR	"snet"
+#define SNET_STATS_PROC_INFO	"snet_stats"
+
+static struct proc_dir_entry *snet_stats_dir;
+static struct proc_dir_entry *snet_stats_pde;
+struct snet_statistics snet_stats;
+
+static int snet_stats_show(struct seq_file *m, void *v)
+{
+	unsigned int sys = 0;
+	seq_printf(m, "snet statistics\n");
+
+	for (sys = SNET_SOCKET_CREATE; sys < SNET_NR_SOCKET_TYPES; sys++) {
+		unsigned int v = 0;
+		seq_printf(m, "%s {", snet_syscall_name(sys));
+
+		seq_printf(m, " Exec: %u ", SNET_STATS_GET(SNET_STATS_EXEC, sys));
+		seq_printf(m, " Unreg: %u ", SNET_STATS_GET(SNET_STATS_UNREG, sys));
+		for (v = SNET_VERDICT_GRANT; v < SNET_NR_VERDICT_TYPES; v++) {
+			seq_printf(m, " %s: %u ", snet_verdict_name(v),
+				   SNET_STATS_GET(v, sys));
+		}
+		seq_printf(m, " Reg Error: %u ", SNET_STATS_GET(SNET_STATS_REG_ERROR, sys));
+		seq_printf(m, "}\n");
+	}
+	return 0;
+}
+
+static int snet_stats_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, snet_stats_show, NULL);
+}
+
+static const struct file_operations snet_stats_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= snet_stats_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+void snet_stats_init(void)
+{
+	snet_stats_dir = proc_mkdir(SNET_STATS_PROC_DIR, init_net.proc_net);
+	if (snet_stats_dir == NULL) {
+		printk(KERN_ERR "snet: can't create /proc/%s\n",
+		       SNET_STATS_PROC_DIR);
+		goto out;
+	}
+
+	snet_stats_pde = proc_create(SNET_STATS_PROC_INFO, 0644, snet_stats_dir,
+				     &snet_stats_proc_fops);
+out:
+	return;
+}
diff --git a/security/snet/snet_stats.h b/security/snet/snet_stats.h
new file mode 100644
index 0000000..05ab06d
--- /dev/null
+++ b/security/snet/snet_stats.h
@@ -0,0 +1,57 @@
+#ifndef _SNET_STATS_H
+#define _SNET_STATS_H
+
+#include <linux/snet.h>
+
+enum {
+	SNET_STATS_REG_GRANT = 0,
+	SNET_STATS_REG_DENY,
+	SNET_STATS_REG_PENDING,
+	SNET_STATS_REG_NONE,
+	SNET_STATS_REG_INVALID,
+	SNET_STATS_REG_ERROR,
+	SNET_STATS_UNREG,
+	SNET_STATS_EXEC,
+	__SNET_STATS_MAX
+};
+
+#define SNET_STATS_MAX __SNET_STATS_MAX
+
+struct snet_statistics {
+	atomic_t	mibs[SNET_NR_SOCKET_TYPES+1][SNET_STATS_MAX];
+};
+
+extern struct snet_statistics snet_stats;
+#define SNET_STATS_INC(field, syscall)	(atomic_inc(&snet_stats.mibs[syscall][field]))
+#define SNET_STATS_DEC(field, syscall)	(atomic_dec(&snet_stats.mibs[syscall][field]))
+#define SNET_STATS_GET(field, syscall)	(atomic_read(&snet_stats.mibs[syscall][field]))
+
+void snet_stats_init(void);
+
+static inline void snet_stats_inc_reg(const enum snet_verdict verdict,
+				      const enum snet_syscall syscall)
+{
+	switch (verdict) {
+	case SNET_VERDICT_GRANT:
+		SNET_STATS_INC(SNET_STATS_REG_GRANT, syscall);
+		break;
+	case SNET_VERDICT_DENY:
+		SNET_STATS_INC(SNET_STATS_REG_DENY, syscall);
+		break;
+	case SNET_VERDICT_PENDING:
+		SNET_STATS_INC(SNET_STATS_REG_PENDING, syscall);
+		break;
+	case SNET_VERDICT_NONE:
+		SNET_STATS_INC(SNET_STATS_REG_NONE, syscall);
+		break;
+	case SNET_VERDICT_INVALID:
+		SNET_STATS_INC(SNET_STATS_REG_INVALID, syscall);
+		break;
+	default:
+		SNET_STATS_INC(SNET_STATS_REG_ERROR, syscall);
+		break;
+	}
+	return;
+};
+
+#endif
-- 
1.7.4.1


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

* [RFC v4 10/11] snet: introduce snet_stats
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (35 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (6 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch adds a /proc/snet/snet_stats entry to show statistics about
snet. this patch adds also the functions to manage the statistics.

 Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/snet/snet_stats.c |   65 ++++++++++++++++++++++++++++++++++++++++++++
 security/snet/snet_stats.h |   57 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/snet_stats.c
 create mode 100644 security/snet/snet_stats.h

diff --git a/security/snet/snet_stats.c b/security/snet/snet_stats.c
new file mode 100644
index 0000000..b946dda
--- /dev/null
+++ b/security/snet/snet_stats.c
@@ -0,0 +1,65 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <net/net_namespace.h>
+#include "snet_stats.h"
+#include "snet_utils.h"
+
+extern struct net init_net;
+
+#define SNET_STATS_PROC_DIR	"snet"
+#define SNET_STATS_PROC_INFO	"snet_stats"
+
+static struct proc_dir_entry *snet_stats_dir;
+static struct proc_dir_entry *snet_stats_pde;
+struct snet_statistics snet_stats;
+
+static int snet_stats_show(struct seq_file *m, void *v)
+{
+	unsigned int sys = 0;
+	seq_printf(m, "snet statistics\n");
+
+	for (sys = SNET_SOCKET_CREATE; sys < SNET_NR_SOCKET_TYPES; sys++) {
+		unsigned int v = 0;
+		seq_printf(m, "%s {", snet_syscall_name(sys));
+
+		seq_printf(m, " Exec: %u ", SNET_STATS_GET(SNET_STATS_EXEC, sys));
+		seq_printf(m, " Unreg: %u ", SNET_STATS_GET(SNET_STATS_UNREG, sys));
+		for (v = SNET_VERDICT_GRANT; v < SNET_NR_VERDICT_TYPES; v++) {
+			seq_printf(m, " %s: %u ", snet_verdict_name(v),
+				   SNET_STATS_GET(v, sys));
+		}
+		seq_printf(m, " Reg Error: %u ", SNET_STATS_GET(SNET_STATS_REG_ERROR, sys));
+		seq_printf(m, "}\n");
+	}
+	return 0;
+}
+
+static int snet_stats_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, snet_stats_show, NULL);
+}
+
+static const struct file_operations snet_stats_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= snet_stats_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+void snet_stats_init(void)
+{
+	snet_stats_dir = proc_mkdir(SNET_STATS_PROC_DIR, init_net.proc_net);
+	if (snet_stats_dir == NULL) {
+		printk(KERN_ERR "snet: can't create /proc/%s\n",
+		       SNET_STATS_PROC_DIR);
+		goto out;
+	}
+
+	snet_stats_pde = proc_create(SNET_STATS_PROC_INFO, 0644, snet_stats_dir,
+				     &snet_stats_proc_fops);
+out:
+	return;
+}
diff --git a/security/snet/snet_stats.h b/security/snet/snet_stats.h
new file mode 100644
index 0000000..05ab06d
--- /dev/null
+++ b/security/snet/snet_stats.h
@@ -0,0 +1,57 @@
+#ifndef _SNET_STATS_H
+#define _SNET_STATS_H
+
+#include <linux/snet.h>
+
+enum {
+	SNET_STATS_REG_GRANT = 0,
+	SNET_STATS_REG_DENY,
+	SNET_STATS_REG_PENDING,
+	SNET_STATS_REG_NONE,
+	SNET_STATS_REG_INVALID,
+	SNET_STATS_REG_ERROR,
+	SNET_STATS_UNREG,
+	SNET_STATS_EXEC,
+	__SNET_STATS_MAX
+};
+
+#define SNET_STATS_MAX __SNET_STATS_MAX
+
+struct snet_statistics {
+	atomic_t	mibs[SNET_NR_SOCKET_TYPES+1][SNET_STATS_MAX];
+};
+
+extern struct snet_statistics snet_stats;
+#define SNET_STATS_INC(field, syscall)	(atomic_inc(&snet_stats.mibs[syscall][field]))
+#define SNET_STATS_DEC(field, syscall)	(atomic_dec(&snet_stats.mibs[syscall][field]))
+#define SNET_STATS_GET(field, syscall)	(atomic_read(&snet_stats.mibs[syscall][field]))
+
+void snet_stats_init(void);
+
+static inline void snet_stats_inc_reg(const enum snet_verdict verdict,
+				      const enum snet_syscall syscall)
+{
+	switch (verdict) {
+	case SNET_VERDICT_GRANT:
+		SNET_STATS_INC(SNET_STATS_REG_GRANT, syscall);
+		break;
+	case SNET_VERDICT_DENY:
+		SNET_STATS_INC(SNET_STATS_REG_DENY, syscall);
+		break;
+	case SNET_VERDICT_PENDING:
+		SNET_STATS_INC(SNET_STATS_REG_PENDING, syscall);
+		break;
+	case SNET_VERDICT_NONE:
+		SNET_STATS_INC(SNET_STATS_REG_NONE, syscall);
+		break;
+	case SNET_VERDICT_INVALID:
+		SNET_STATS_INC(SNET_STATS_REG_INVALID, syscall);
+		break;
+	default:
+		SNET_STATS_INC(SNET_STATS_REG_ERROR, syscall);
+		break;
+	}
+	return;
+};
+
+#endif
-- 
1.7.4.1


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

* [RFC v4 11/11] snet: introduce security/snet, Makefile and Kconfig changes
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (42 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch creates folder security/snet and adds changes for Kconfig and Makefile

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/Kconfig       |    6 ++++++
 security/Makefile      |    2 ++
 security/snet/Kconfig  |   11 +++++++++++
 security/snet/Makefile |   15 +++++++++++++++
 4 files changed, 34 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/Kconfig
 create mode 100644 security/snet/Makefile

diff --git a/security/Kconfig b/security/Kconfig
index 95accd4..7e393c4 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -184,6 +184,7 @@ source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
+source security/snet/Kconfig
 
 source security/integrity/ima/Kconfig
 
@@ -193,6 +194,7 @@ choice
 	default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
 	default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
 	default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
+	default DEFAULT_SECURITY_SNET if SECURITY_SNET
 	default DEFAULT_SECURITY_DAC
 
 	help
@@ -211,6 +213,9 @@ choice
 	config DEFAULT_SECURITY_APPARMOR
 		bool "AppArmor" if SECURITY_APPARMOR=y
 
+	config DEFAULT_SECURITY_SNET
+		bool "snet" if SECURITY_SNET=y
+
 	config DEFAULT_SECURITY_DAC
 		bool "Unix Discretionary Access Controls"
 
@@ -222,6 +227,7 @@ config DEFAULT_SECURITY
 	default "smack" if DEFAULT_SECURITY_SMACK
 	default "tomoyo" if DEFAULT_SECURITY_TOMOYO
 	default "apparmor" if DEFAULT_SECURITY_APPARMOR
+	default "snet" if DEFAULT_SECURITY_SNET
 	default "" if DEFAULT_SECURITY_DAC
 
 endmenu
diff --git a/security/Makefile b/security/Makefile
index 8bb0fe9..9e769b2 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -7,6 +7,7 @@ subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
 subdir-$(CONFIG_SECURITY_SMACK)		+= smack
 subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
+subdir-$(CONFIG_SECURITY_SNET)		+= snet
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -21,6 +22,7 @@ obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
 obj-$(CONFIG_AUDIT)			+= lsm_audit.o
 obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/built-in.o
 obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/built-in.o
+obj-$(CONFIG_SECURITY_SNET)		+= snet/built-in.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
diff --git a/security/snet/Kconfig b/security/snet/Kconfig
new file mode 100644
index 0000000..6dabd7d
--- /dev/null
+++ b/security/snet/Kconfig
@@ -0,0 +1,11 @@
+#
+# snet
+#
+
+config SECURITY_SNET
+	bool "snet - Security for NETwork syscalls"
+	depends on SECURITY_NETWORK
+	default n
+	---help---
+	If this option is enabled, the kernel will include support for reporting
+	networking's syscalls to userspace and wait for a verdict
diff --git a/security/snet/Makefile b/security/snet/Makefile
new file mode 100644
index 0000000..002c102
--- /dev/null
+++ b/security/snet/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for building the Security Network Events module.
+#
+obj-$(CONFIG_SECURITY_SNET) :=  snet.o
+
+snet-y := snet_event.o \
+	  snet_netlink_helper.o \
+	  snet_netlink.o \
+	  snet_verdict.o \
+	  snet_ticket_helper.o \
+	  snet_ticket.o \
+	  snet_hooks.o \
+	  snet_core.o \
+	  snet_stats.o \
+	  snet_utils.o
-- 
1.7.4.1


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

* [RFC v4 11/11] snet: introduce security/snet, Makefile and Kconfig changes
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (39 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
                   ` (2 subsequent siblings)
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch creates folder security/snet and adds changes for Kconfig and Makefile

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/Kconfig       |    6 ++++++
 security/Makefile      |    2 ++
 security/snet/Kconfig  |   11 +++++++++++
 security/snet/Makefile |   15 +++++++++++++++
 4 files changed, 34 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/Kconfig
 create mode 100644 security/snet/Makefile

diff --git a/security/Kconfig b/security/Kconfig
index 95accd4..7e393c4 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -184,6 +184,7 @@ source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
+source security/snet/Kconfig
 
 source security/integrity/ima/Kconfig
 
@@ -193,6 +194,7 @@ choice
 	default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
 	default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
 	default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
+	default DEFAULT_SECURITY_SNET if SECURITY_SNET
 	default DEFAULT_SECURITY_DAC
 
 	help
@@ -211,6 +213,9 @@ choice
 	config DEFAULT_SECURITY_APPARMOR
 		bool "AppArmor" if SECURITY_APPARMOR=y
 
+	config DEFAULT_SECURITY_SNET
+		bool "snet" if SECURITY_SNET=y
+
 	config DEFAULT_SECURITY_DAC
 		bool "Unix Discretionary Access Controls"
 
@@ -222,6 +227,7 @@ config DEFAULT_SECURITY
 	default "smack" if DEFAULT_SECURITY_SMACK
 	default "tomoyo" if DEFAULT_SECURITY_TOMOYO
 	default "apparmor" if DEFAULT_SECURITY_APPARMOR
+	default "snet" if DEFAULT_SECURITY_SNET
 	default "" if DEFAULT_SECURITY_DAC
 
 endmenu
diff --git a/security/Makefile b/security/Makefile
index 8bb0fe9..9e769b2 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -7,6 +7,7 @@ subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
 subdir-$(CONFIG_SECURITY_SMACK)		+= smack
 subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
+subdir-$(CONFIG_SECURITY_SNET)		+= snet
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -21,6 +22,7 @@ obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
 obj-$(CONFIG_AUDIT)			+= lsm_audit.o
 obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/built-in.o
 obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/built-in.o
+obj-$(CONFIG_SECURITY_SNET)		+= snet/built-in.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
diff --git a/security/snet/Kconfig b/security/snet/Kconfig
new file mode 100644
index 0000000..6dabd7d
--- /dev/null
+++ b/security/snet/Kconfig
@@ -0,0 +1,11 @@
+#
+# snet
+#
+
+config SECURITY_SNET
+	bool "snet - Security for NETwork syscalls"
+	depends on SECURITY_NETWORK
+	default n
+	---help---
+	If this option is enabled, the kernel will include support for reporting
+	networking's syscalls to userspace and wait for a verdict
diff --git a/security/snet/Makefile b/security/snet/Makefile
new file mode 100644
index 0000000..002c102
--- /dev/null
+++ b/security/snet/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for building the Security Network Events module.
+#
+obj-$(CONFIG_SECURITY_SNET) :=  snet.o
+
+snet-y := snet_event.o \
+	  snet_netlink_helper.o \
+	  snet_netlink.o \
+	  snet_verdict.o \
+	  snet_ticket_helper.o \
+	  snet_ticket.o \
+	  snet_hooks.o \
+	  snet_core.o \
+	  snet_stats.o \
+	  snet_utils.o
-- 
1.7.4.1


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

* [RFC v4 11/11] snet: introduce security/snet, Makefile and Kconfig changes
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (40 preceding siblings ...)
  2011-05-05 13:59 ` [RFC v4 11/11] snet: introduce security/snet, Makefile and Kconfig changes y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch creates folder security/snet and adds changes for Kconfig and Makefile

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/Kconfig       |    6 ++++++
 security/Makefile      |    2 ++
 security/snet/Kconfig  |   11 +++++++++++
 security/snet/Makefile |   15 +++++++++++++++
 4 files changed, 34 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/Kconfig
 create mode 100644 security/snet/Makefile

diff --git a/security/Kconfig b/security/Kconfig
index 95accd4..7e393c4 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -184,6 +184,7 @@ source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
+source security/snet/Kconfig
 
 source security/integrity/ima/Kconfig
 
@@ -193,6 +194,7 @@ choice
 	default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
 	default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
 	default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
+	default DEFAULT_SECURITY_SNET if SECURITY_SNET
 	default DEFAULT_SECURITY_DAC
 
 	help
@@ -211,6 +213,9 @@ choice
 	config DEFAULT_SECURITY_APPARMOR
 		bool "AppArmor" if SECURITY_APPARMOR=y
 
+	config DEFAULT_SECURITY_SNET
+		bool "snet" if SECURITY_SNET=y
+
 	config DEFAULT_SECURITY_DAC
 		bool "Unix Discretionary Access Controls"
 
@@ -222,6 +227,7 @@ config DEFAULT_SECURITY
 	default "smack" if DEFAULT_SECURITY_SMACK
 	default "tomoyo" if DEFAULT_SECURITY_TOMOYO
 	default "apparmor" if DEFAULT_SECURITY_APPARMOR
+	default "snet" if DEFAULT_SECURITY_SNET
 	default "" if DEFAULT_SECURITY_DAC
 
 endmenu
diff --git a/security/Makefile b/security/Makefile
index 8bb0fe9..9e769b2 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -7,6 +7,7 @@ subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
 subdir-$(CONFIG_SECURITY_SMACK)		+= smack
 subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
+subdir-$(CONFIG_SECURITY_SNET)		+= snet
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -21,6 +22,7 @@ obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
 obj-$(CONFIG_AUDIT)			+= lsm_audit.o
 obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/built-in.o
 obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/built-in.o
+obj-$(CONFIG_SECURITY_SNET)		+= snet/built-in.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
diff --git a/security/snet/Kconfig b/security/snet/Kconfig
new file mode 100644
index 0000000..6dabd7d
--- /dev/null
+++ b/security/snet/Kconfig
@@ -0,0 +1,11 @@
+#
+# snet
+#
+
+config SECURITY_SNET
+	bool "snet - Security for NETwork syscalls"
+	depends on SECURITY_NETWORK
+	default n
+	---help---
+	If this option is enabled, the kernel will include support for reporting
+	networking's syscalls to userspace and wait for a verdict
diff --git a/security/snet/Makefile b/security/snet/Makefile
new file mode 100644
index 0000000..002c102
--- /dev/null
+++ b/security/snet/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for building the Security Network Events module.
+#
+obj-$(CONFIG_SECURITY_SNET) :=  snet.o
+
+snet-y := snet_event.o \
+	  snet_netlink_helper.o \
+	  snet_netlink.o \
+	  snet_verdict.o \
+	  snet_ticket_helper.o \
+	  snet_ticket.o \
+	  snet_hooks.o \
+	  snet_core.o \
+	  snet_stats.o \
+	  snet_utils.o
-- 
1.7.4.1

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

* [RFC v4 11/11] snet: introduce security/snet, Makefile and Kconfig changes
       [not found] <1304603961-2517-1-git-send-email-y>
                   ` (41 preceding siblings ...)
  2011-05-05 13:59 ` y
@ 2011-05-05 13:59 ` y
  2011-05-05 13:59 ` y
  43 siblings, 0 replies; 44+ messages in thread
From: y @ 2011-05-05 13:59 UTC (permalink / raw)
  To: linux-security-module
  Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
	Grzegorz Nosek, Samir Bellabes

From: Samir Bellabes <sam@synack.fr>

this patch creates folder security/snet and adds changes for Kconfig and Makefile

Signed-off-by: Samir Bellabes <sam@synack.fr>
---
 security/Kconfig       |    6 ++++++
 security/Makefile      |    2 ++
 security/snet/Kconfig  |   11 +++++++++++
 security/snet/Makefile |   15 +++++++++++++++
 4 files changed, 34 insertions(+), 0 deletions(-)
 create mode 100644 security/snet/Kconfig
 create mode 100644 security/snet/Makefile

diff --git a/security/Kconfig b/security/Kconfig
index 95accd4..7e393c4 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -184,6 +184,7 @@ source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
+source security/snet/Kconfig
 
 source security/integrity/ima/Kconfig
 
@@ -193,6 +194,7 @@ choice
 	default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
 	default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
 	default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
+	default DEFAULT_SECURITY_SNET if SECURITY_SNET
 	default DEFAULT_SECURITY_DAC
 
 	help
@@ -211,6 +213,9 @@ choice
 	config DEFAULT_SECURITY_APPARMOR
 		bool "AppArmor" if SECURITY_APPARMOR=y
 
+	config DEFAULT_SECURITY_SNET
+		bool "snet" if SECURITY_SNET=y
+
 	config DEFAULT_SECURITY_DAC
 		bool "Unix Discretionary Access Controls"
 
@@ -222,6 +227,7 @@ config DEFAULT_SECURITY
 	default "smack" if DEFAULT_SECURITY_SMACK
 	default "tomoyo" if DEFAULT_SECURITY_TOMOYO
 	default "apparmor" if DEFAULT_SECURITY_APPARMOR
+	default "snet" if DEFAULT_SECURITY_SNET
 	default "" if DEFAULT_SECURITY_DAC
 
 endmenu
diff --git a/security/Makefile b/security/Makefile
index 8bb0fe9..9e769b2 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -7,6 +7,7 @@ subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
 subdir-$(CONFIG_SECURITY_SMACK)		+= smack
 subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
+subdir-$(CONFIG_SECURITY_SNET)		+= snet
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -21,6 +22,7 @@ obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
 obj-$(CONFIG_AUDIT)			+= lsm_audit.o
 obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/built-in.o
 obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/built-in.o
+obj-$(CONFIG_SECURITY_SNET)		+= snet/built-in.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
diff --git a/security/snet/Kconfig b/security/snet/Kconfig
new file mode 100644
index 0000000..6dabd7d
--- /dev/null
+++ b/security/snet/Kconfig
@@ -0,0 +1,11 @@
+#
+# snet
+#
+
+config SECURITY_SNET
+	bool "snet - Security for NETwork syscalls"
+	depends on SECURITY_NETWORK
+	default n
+	---help---
+	If this option is enabled, the kernel will include support for reporting
+	networking's syscalls to userspace and wait for a verdict
diff --git a/security/snet/Makefile b/security/snet/Makefile
new file mode 100644
index 0000000..002c102
--- /dev/null
+++ b/security/snet/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for building the Security Network Events module.
+#
+obj-$(CONFIG_SECURITY_SNET) :=  snet.o
+
+snet-y := snet_event.o \
+	  snet_netlink_helper.o \
+	  snet_netlink.o \
+	  snet_verdict.o \
+	  snet_ticket_helper.o \
+	  snet_ticket.o \
+	  snet_hooks.o \
+	  snet_core.o \
+	  snet_stats.o \
+	  snet_utils.o
-- 
1.7.4.1


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

end of thread, other threads:[~2011-05-05 14:07 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <1304603961-2517-1-git-send-email-y>
2011-05-05 13:59 ` [RFC v4 01/11] lsm: add security_socket_closed() y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` [RFC v4 02/11] lsm: reintroduce security_socket_post_accept() y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` [RFC v4 03/11] snet: introduce snet_core y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` [RFC v4 04/11] snet: introduce snet_event y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` [RFC v4 05/11] snet: introduce snet_hooks y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` [RFC v4 06/11] snet: introduce snet_netlink y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` [RFC v4 07/11] snet: introduce snet_verdict y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` [RFC v4 08/11] snet: introduce snet_ticket y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` [RFC v4 09/11] snet: introduce snet_utils y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` [RFC v4 10/11] snet: introduce snet_stats y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` [RFC v4 11/11] snet: introduce security/snet, Makefile and Kconfig changes y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y
2011-05-05 13:59 ` y

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).