* [PATCH] Add packet filtering based on process's security context.
@ 2007-11-21 12:30 Tetsuo Handa
2007-11-22 13:35 ` [PATCH net-2.6.25] " Tetsuo Handa
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2007-11-21 12:30 UTC (permalink / raw)
To: herbert; +Cc: netdev, davem
Hello.
This patch comes from a thread at http://lkml.org/lkml/2007/11/16/155 .
I want to use IP/port based access control for incoming connections/datagrams.
This idea was discussed for several times, but there is no approach that satisfies
both "it can decide based on the recipient's process" and
"it can sleep so that the LSM hook can query userspace for an access decision".
Thus, I'd like to add one new LSM hook in net/core/datagram.c and
change return type of one existing LSM hook in net/socket.c .
Regards.
-----
Subject: Add packet filtering based on process's security context.
This patch allows LSM modules filter incoming connections/datagrams
based on the process's security context who is attempting to pick up.
There are already hooks to filter incoming connections/datagrams
based on the socket's security context, but these hooks are not
applicable when one wants to do TCP Wrapper-like filtering
(e.g. App1 is permitted to accept TCP connections from 192.168.0.0/16).
Precautions: This approach has a side effect which unlikely occurs.
If a socket is shared by multiple processes with differnt policy,
the process who should be able to accept this connection
will not be able to accept this connection
because socket_post_accept() aborts this connection.
But if socket_post_accept() doesn't abort this connection,
the process who must not be able to accept this connection
will repeat accept() forever, which is a worse side effect.
Similarly, if a socket is shared by multiple processes with differnt policy,
the process who should be able to pick up this datagram
will not be able to pick up this datagram
because socket_post_recv_datagram() discards this datagram.
But if socket_post_recv_datagram() doesn't discard this datagram,
the process who must not be able to pick up this datagram
will repeat recvmsg() forever, which is a worse side effect.
So, don't give different permissions between processes who shares one socket.
Otherwise, some connections/datagrams cannot be delivered to intended process.
Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
include/linux/security.h | 34 +++++++++++++++++++++++++++++-----
net/core/datagram.c | 26 ++++++++++++++++++++++++--
net/socket.c | 7 +++++--
security/dummy.c | 13 ++++++++++---
security/security.c | 10 ++++++++--
5 files changed, 76 insertions(+), 14 deletions(-)
--- linux-2.6-mm.orig/include/linux/security.h
+++ linux-2.6-mm/include/linux/security.h
@@ -778,8 +778,12 @@ struct request_sock;
* @socket_post_accept:
* This hook allows a security module to copy security
* information into the newly created socket's inode.
+ * This hook also allows a security module to filter connections
+ * from unwanted peers based on the process accepting this connection.
+ * The connection will be aborted if this hook returns nonzero.
* @sock contains the listening socket structure.
* @newsock contains the newly created server socket for connection.
+ * Return 0 if permission is granted.
* @socket_sendmsg:
* Check permission before transmitting a message to another socket.
* @sock contains the socket structure.
@@ -793,6 +797,15 @@ struct request_sock;
* @size contains the size of message structure.
* @flags contains the operational flags.
* Return 0 if permission is granted.
+ * @socket_post_recv_datagram:
+ * Check permission after receiving a datagram.
+ * This hook allows a security module to filter packets
+ * from unwanted peers based on the process receiving this datagram.
+ * The packet will be discarded if this hook returns nonzero.
+ * @sk contains the socket.
+ * @skb contains the socket buffer (may be NULL).
+ * @flags contains the operational flags.
+ * Return 0 if permission is granted.
* @socket_getsockname:
* Check permission before the local address (name) of the socket object
* @sock is retrieved.
@@ -1389,12 +1402,13 @@ 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_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,
struct msghdr * msg, int size, int flags);
+ int (*socket_post_recv_datagram) (struct sock *sk, struct sk_buff *skb,
+ unsigned int flags);
int (*socket_getsockname) (struct socket * sock);
int (*socket_getpeername) (struct socket * sock);
int (*socket_getsockopt) (struct socket * sock, int level, int optname);
@@ -2307,10 +2321,12 @@ int security_socket_bind(struct socket *
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_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);
+int security_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags);
int security_socket_getsockname(struct socket *sock);
int security_socket_getpeername(struct socket *sock);
int security_socket_getsockopt(struct socket *sock, int level, int optname);
@@ -2386,9 +2402,10 @@ static inline int security_socket_accept
return 0;
}
-static inline void security_socket_post_accept(struct socket * sock,
- struct socket * newsock)
+static inline int security_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
{
+ return 0;
}
static inline int security_socket_sendmsg(struct socket * sock,
@@ -2404,6 +2421,13 @@ static inline int security_socket_recvms
return 0;
}
+static inline int security_socket_post_recv_datagram(struct sock *sk,
+ struct sk_buff *skb,
+ unsigned int flags)
+{
+ return 0;
+}
+
static inline int security_socket_getsockname(struct socket * sock)
{
return 0;
--- linux-2.6-mm.orig/net/socket.c
+++ linux-2.6-mm/net/socket.c
@@ -1442,13 +1442,16 @@ asmlinkage long sys_accept(int fd, struc
goto out_fd;
}
+ /* Filter connections from unwanted peers. */
+ err = security_socket_post_accept(sock, newsock);
+ if (err)
+ goto out_fd;
+
/* File flags are not inherited via accept() unlike another OSes. */
fd_install(newfd, newfile);
err = newfd;
- security_socket_post_accept(sock, newsock);
-
out_put:
fput_light(sock->file, fput_needed);
out:
--- linux-2.6-mm.orig/security/dummy.c
+++ linux-2.6-mm/security/dummy.c
@@ -753,10 +753,10 @@ static int dummy_socket_accept (struct s
return 0;
}
-static void dummy_socket_post_accept (struct socket *sock,
- struct socket *newsock)
+static int dummy_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
{
- return;
+ return 0;
}
static int dummy_socket_sendmsg (struct socket *sock, struct msghdr *msg,
@@ -771,6 +771,12 @@ static int dummy_socket_recvmsg (struct
return 0;
}
+static int dummy_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags)
+{
+ return 0;
+}
+
static int dummy_socket_getsockname (struct socket *sock)
{
return 0;
@@ -1110,6 +1116,7 @@ void security_fixup_ops (struct security
set_to_dummy_if_null(ops, socket_post_accept);
set_to_dummy_if_null(ops, socket_sendmsg);
set_to_dummy_if_null(ops, socket_recvmsg);
+ set_to_dummy_if_null(ops, socket_post_recv_datagram);
set_to_dummy_if_null(ops, socket_getsockname);
set_to_dummy_if_null(ops, socket_getpeername);
set_to_dummy_if_null(ops, socket_setsockopt);
--- linux-2.6-mm.orig/net/core/datagram.c
+++ linux-2.6-mm/net/core/datagram.c
@@ -55,6 +55,7 @@
#include <net/checksum.h>
#include <net/sock.h>
#include <net/tcp_states.h>
+#include <linux/security.h>
/*
* Is a socket 'connection oriented' ?
@@ -148,6 +149,7 @@ struct sk_buff *skb_recv_datagram(struct
{
struct sk_buff *skb;
long timeo;
+ unsigned long cpu_flags;
/*
* Caller is allowed not to check sk->sk_err before skb_recv_datagram()
*/
@@ -166,8 +168,6 @@ struct sk_buff *skb_recv_datagram(struct
* However, this function was corrent in any case. 8)
*/
if (flags & MSG_PEEK) {
- unsigned long cpu_flags;
-
spin_lock_irqsave(&sk->sk_receive_queue.lock,
cpu_flags);
skb = skb_peek(&sk->sk_receive_queue);
@@ -178,6 +178,11 @@ struct sk_buff *skb_recv_datagram(struct
} else
skb = skb_dequeue(&sk->sk_receive_queue);
+ /* Filter packets from unwanted peers. */
+ error = security_socket_post_recv_datagram(sk, skb, flags);
+ if (error)
+ goto force_dequeue;
+
if (skb)
return skb;
@@ -190,6 +195,23 @@ struct sk_buff *skb_recv_datagram(struct
return NULL;
+force_dequeue:
+ /* Drop this packet because LSM says "Don't pass it to the caller". */
+ if (!(flags & MSG_PEEK))
+ goto no_peek;
+ /*
+ * If this packet is MSG_PEEK'ed, dequeue it forcibly
+ * so that this packet won't prevent the caller from picking up
+ * next packet.
+ */
+ spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
+ if (skb == skb_peek(&sk->sk_receive_queue)) {
+ __skb_unlink(skb, &sk->sk_receive_queue);
+ atomic_dec(&skb->users);
+ }
+ spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
+no_peek:
+ kfree_skb(skb);
no_packet:
*err = error;
return NULL;
--- linux-2.6-mm.orig/security/security.c
+++ linux-2.6-mm/security/security.c
@@ -875,9 +875,9 @@ int security_socket_accept(struct socket
return security_ops->socket_accept(sock, newsock);
}
-void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+int security_socket_post_accept(struct socket *sock, struct socket *newsock)
{
- security_ops->socket_post_accept(sock, newsock);
+ return security_ops->socket_post_accept(sock, newsock);
}
int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
@@ -891,6 +891,12 @@ int security_socket_recvmsg(struct socke
return security_ops->socket_recvmsg(sock, msg, size, flags);
}
+int security_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags)
+{
+ return security_ops->socket_post_recv_datagram(sk, skb, flags);
+}
+
int security_socket_getsockname(struct socket *sock)
{
return security_ops->socket_getsockname(sock);
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH net-2.6.25] Add packet filtering based on process's security context.
2007-11-21 12:30 [PATCH] Add packet filtering based on process's security context Tetsuo Handa
@ 2007-11-22 13:35 ` Tetsuo Handa
2007-11-22 23:29 ` James Morris
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2007-11-22 13:35 UTC (permalink / raw)
To: herbert; +Cc: netdev, davem
Hello.
Herbert Xu wrote:
> On Thu, Nov 22, 2007 at 09:57:14PM +0900, Tetsuo Handa wrote:
> >
> > But you say that I should make patches based on the net-2.6.25 tree.
> > Which tree ("-mm" or "net-2.6.25") should I use for making this patch?
>
> The net-2.6.25 tree is the one. Please use the tree at
>
> git://git.kernel.org/pub/scm/linux/kernel/git/herbert/net-2.6.25.git/
>
> while Dave is away.
Thank you.
I refreshed the patch based on net-2.6.25 tree.
Regards.
-----
Subject: Add packet filtering based on process's security context.
This patch allows LSM modules filter incoming connections/datagrams
based on the process's security context who is attempting to pick up.
There are already hooks to filter incoming connections/datagrams
based on the socket's security context, but these hooks are not
applicable when one wants to do TCP Wrapper-like filtering
(e.g. App1 is permitted to accept TCP connections from 192.168.0.0/16).
Precautions: This approach has a side effect which unlikely occurs.
If a socket is shared by multiple processes with differnt policy,
the process who should be able to accept this connection
will not be able to accept this connection
because socket_post_accept() aborts this connection.
But if socket_post_accept() doesn't abort this connection,
the process who must not be able to accept this connection
will repeat accept() forever, which is a worse side effect.
Similarly, if a socket is shared by multiple processes with differnt policy,
the process who should be able to pick up this datagram
will not be able to pick up this datagram
because socket_post_recv_datagram() discards this datagram.
But if socket_post_recv_datagram() doesn't discard this datagram,
the process who must not be able to pick up this datagram
will repeat recvmsg() forever, which is a worse side effect.
So, don't give different permissions between processes who share one socket.
Otherwise, some connections/datagrams cannot be delivered to intended process.
Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
include/linux/security.h | 34 +++++++++++++++++++++++++++++-----
net/core/datagram.c | 26 ++++++++++++++++++++++++--
net/socket.c | 7 +++++--
security/dummy.c | 13 ++++++++++---
security/security.c | 10 ++++++++--
5 files changed, 76 insertions(+), 14 deletions(-)
--- net-2.6.25.orig/include/linux/security.h
+++ net-2.6.25/include/linux/security.h
@@ -781,8 +781,12 @@ struct request_sock;
* @socket_post_accept:
* This hook allows a security module to copy security
* information into the newly created socket's inode.
+ * This hook also allows a security module to filter connections
+ * from unwanted peers based on the process accepting this connection.
+ * The connection will be aborted if this hook returns nonzero.
* @sock contains the listening socket structure.
* @newsock contains the newly created server socket for connection.
+ * Return 0 if permission is granted.
* @socket_sendmsg:
* Check permission before transmitting a message to another socket.
* @sock contains the socket structure.
@@ -796,6 +800,15 @@ struct request_sock;
* @size contains the size of message structure.
* @flags contains the operational flags.
* Return 0 if permission is granted.
+ * @socket_post_recv_datagram:
+ * Check permission after receiving a datagram.
+ * This hook allows a security module to filter packets
+ * from unwanted peers based on the process receiving this datagram.
+ * The packet will be discarded if this hook returns nonzero.
+ * @sk contains the socket.
+ * @skb contains the socket buffer (may be NULL).
+ * @flags contains the operational flags.
+ * Return 0 if permission is granted.
* @socket_getsockname:
* Check permission before the local address (name) of the socket object
* @sock is retrieved.
@@ -1387,12 +1400,13 @@ 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_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,
struct msghdr * msg, int size, int flags);
+ int (*socket_post_recv_datagram) (struct sock *sk, struct sk_buff *skb,
+ unsigned int flags);
int (*socket_getsockname) (struct socket * sock);
int (*socket_getpeername) (struct socket * sock);
int (*socket_getsockopt) (struct socket * sock, int level, int optname);
@@ -2297,10 +2311,12 @@ int security_socket_bind(struct socket *
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_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);
+int security_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags);
int security_socket_getsockname(struct socket *sock);
int security_socket_getpeername(struct socket *sock);
int security_socket_getsockopt(struct socket *sock, int level, int optname);
@@ -2376,9 +2392,10 @@ static inline int security_socket_accept
return 0;
}
-static inline void security_socket_post_accept(struct socket * sock,
- struct socket * newsock)
+static inline int security_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
{
+ return 0;
}
static inline int security_socket_sendmsg(struct socket * sock,
@@ -2394,6 +2411,13 @@ static inline int security_socket_recvms
return 0;
}
+static inline int security_socket_post_recv_datagram(struct sock *sk,
+ struct sk_buff *skb,
+ unsigned int flags)
+{
+ return 0;
+}
+
static inline int security_socket_getsockname(struct socket * sock)
{
return 0;
--- net-2.6.25.orig/net/socket.c
+++ net-2.6.25/net/socket.c
@@ -1455,13 +1455,16 @@ asmlinkage long sys_accept(int fd, struc
goto out_fd;
}
+ /* Filter connections from unwanted peers. */
+ err = security_socket_post_accept(sock, newsock);
+ if (err)
+ goto out_fd;
+
/* File flags are not inherited via accept() unlike another OSes. */
fd_install(newfd, newfile);
err = newfd;
- security_socket_post_accept(sock, newsock);
-
out_put:
fput_light(sock->file, fput_needed);
out:
--- net-2.6.25.orig/security/dummy.c
+++ net-2.6.25/security/dummy.c
@@ -748,10 +748,10 @@ static int dummy_socket_accept (struct s
return 0;
}
-static void dummy_socket_post_accept (struct socket *sock,
- struct socket *newsock)
+static int dummy_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
{
- return;
+ return 0;
}
static int dummy_socket_sendmsg (struct socket *sock, struct msghdr *msg,
@@ -766,6 +766,12 @@ static int dummy_socket_recvmsg (struct
return 0;
}
+static int dummy_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags)
+{
+ return 0;
+}
+
static int dummy_socket_getsockname (struct socket *sock)
{
return 0;
@@ -1099,6 +1105,7 @@ void security_fixup_ops (struct security
set_to_dummy_if_null(ops, socket_post_accept);
set_to_dummy_if_null(ops, socket_sendmsg);
set_to_dummy_if_null(ops, socket_recvmsg);
+ set_to_dummy_if_null(ops, socket_post_recv_datagram);
set_to_dummy_if_null(ops, socket_getsockname);
set_to_dummy_if_null(ops, socket_getpeername);
set_to_dummy_if_null(ops, socket_setsockopt);
--- net-2.6.25.orig/net/core/datagram.c
+++ net-2.6.25/net/core/datagram.c
@@ -55,6 +55,7 @@
#include <net/checksum.h>
#include <net/sock.h>
#include <net/tcp_states.h>
+#include <linux/security.h>
/*
* Is a socket 'connection oriented' ?
@@ -148,6 +149,7 @@ struct sk_buff *skb_recv_datagram(struct
{
struct sk_buff *skb;
long timeo;
+ unsigned long cpu_flags;
/*
* Caller is allowed not to check sk->sk_err before skb_recv_datagram()
*/
@@ -166,8 +168,6 @@ struct sk_buff *skb_recv_datagram(struct
* However, this function was corrent in any case. 8)
*/
if (flags & MSG_PEEK) {
- unsigned long cpu_flags;
-
spin_lock_irqsave(&sk->sk_receive_queue.lock,
cpu_flags);
skb = skb_peek(&sk->sk_receive_queue);
@@ -178,6 +178,11 @@ struct sk_buff *skb_recv_datagram(struct
} else
skb = skb_dequeue(&sk->sk_receive_queue);
+ /* Filter packets from unwanted peers. */
+ error = security_socket_post_recv_datagram(sk, skb, flags);
+ if (error)
+ goto force_dequeue;
+
if (skb)
return skb;
@@ -190,6 +195,23 @@ struct sk_buff *skb_recv_datagram(struct
return NULL;
+force_dequeue:
+ /* Drop this packet because LSM says "Don't pass it to the caller". */
+ if (!(flags & MSG_PEEK))
+ goto no_peek;
+ /*
+ * If this packet is MSG_PEEK'ed, dequeue it forcibly
+ * so that this packet won't prevent the caller from picking up
+ * next packet.
+ */
+ spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
+ if (skb == skb_peek(&sk->sk_receive_queue)) {
+ __skb_unlink(skb, &sk->sk_receive_queue);
+ atomic_dec(&skb->users);
+ }
+ spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
+no_peek:
+ kfree_skb(skb);
no_packet:
*err = error;
return NULL;
--- net-2.6.25.orig/security/security.c
+++ net-2.6.25/security/security.c
@@ -869,9 +869,9 @@ int security_socket_accept(struct socket
return security_ops->socket_accept(sock, newsock);
}
-void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+int security_socket_post_accept(struct socket *sock, struct socket *newsock)
{
- security_ops->socket_post_accept(sock, newsock);
+ return security_ops->socket_post_accept(sock, newsock);
}
int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
@@ -885,6 +885,12 @@ int security_socket_recvmsg(struct socke
return security_ops->socket_recvmsg(sock, msg, size, flags);
}
+int security_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags)
+{
+ return security_ops->socket_post_recv_datagram(sk, skb, flags);
+}
+
int security_socket_getsockname(struct socket *sock)
{
return security_ops->socket_getsockname(sock);
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process's security context.
2007-11-22 13:35 ` [PATCH net-2.6.25] " Tetsuo Handa
@ 2007-11-22 23:29 ` James Morris
2007-11-24 2:14 ` [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context Tetsuo Handa
2007-12-03 7:58 ` [PATCH net-2.6.25] Add packet filtering based on process's security context Patrick McHardy
0 siblings, 2 replies; 21+ messages in thread
From: James Morris @ 2007-11-22 23:29 UTC (permalink / raw)
To: Tetsuo Handa
Cc: Herbert Xu, netdev, David S. Miller, linux-security-module,
Patrick McHardy, netfilter-devel, Stephen Smalley
On Thu, 22 Nov 2007, Tetsuo Handa wrote:
> This patch allows LSM modules filter incoming connections/datagrams
> based on the process's security context who is attempting to pick up.
>
> There are already hooks to filter incoming connections/datagrams
> based on the socket's security context, but these hooks are not
> applicable when one wants to do TCP Wrapper-like filtering
> (e.g. App1 is permitted to accept TCP connections from 192.168.0.0/16).
This functionality looks like it could be useful in that we currently have
no direct security mapping from incoming packet to user process, but only
to the receiving socket, as you mention. For SELinux, it may help us
simplify/clarify policy.
It's also been long-desired for netfilter/iptables, to allow ipt_owner to
work cleanly for incoming packets.
So, this probably needs to be implemented in a way which works for both LSM
and netfilter. There have been several discussions on the issue from the
netfilter side, although I don't know what the latest status of that is
(I've expanded the cc list to hopefully get some more feedback).
>From memory, one approach under discussion was to add netfilter hooks to
the transport layer, which could be invoked correctly by each type of
protocol when the target process is selected.
If this is done for netfilter, then an LSM hook is probably not needed at
all, as security modules can utilize netfilter hooks directly.
> Precautions: This approach has a side effect which unlikely occurs.
>
> If a socket is shared by multiple processes with differnt policy,
> the process who should be able to accept this connection
> will not be able to accept this connection
> because socket_post_accept() aborts this connection.
> But if socket_post_accept() doesn't abort this connection,
> the process who must not be able to accept this connection
> will repeat accept() forever, which is a worse side effect.
>
> Similarly, if a socket is shared by multiple processes with differnt policy,
> the process who should be able to pick up this datagram
> will not be able to pick up this datagram
> because socket_post_recv_datagram() discards this datagram.
> But if socket_post_recv_datagram() doesn't discard this datagram,
> the process who must not be able to pick up this datagram
> will repeat recvmsg() forever, which is a worse side effect.
>
> So, don't give different permissions between processes who share one socket.
> Otherwise, some connections/datagrams cannot be delivered to intended process.
These semantics changes are concerning, and lead me to wonder if there are
any more. Needs more review by networking folk.
- James
--
James Morris
<jmorris@namei.org>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context.
2007-11-22 23:29 ` James Morris
@ 2007-11-24 2:14 ` Tetsuo Handa
2007-11-29 4:57 ` Samir Bellabes
2007-12-03 7:58 ` [PATCH net-2.6.25] Add packet filtering based on process's security context Patrick McHardy
1 sibling, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2007-11-24 2:14 UTC (permalink / raw)
To: jmorris
Cc: herbert, netdev, davem, linux-security-module, kaber,
netfilter-devel, sds
Hello.
James Morris wrote:
> From memory, one approach under discussion was to add netfilter hooks to
> the transport layer, which could be invoked correctly by each type of
> protocol when the target process is selected.
>
> If this is done for netfilter, then an LSM hook is probably not needed at
> all, as security modules can utilize netfilter hooks directly.
Patrick McHardy says (at http://marc.info/?l=linux-netdev&m=118495005800410&w=2 )
"Even with socket filters netfilter doesn't know the final receipient
process, that is not known until it calls recvmsg and the data is read,
which is too late for netfilter."
> > Precautions: This approach has a side effect which unlikely occurs.
> >
> > If a socket is shared by multiple processes with different policy,
> > the process who should be able to accept this connection
> > will not be able to accept this connection
> > because socket_post_accept() aborts this connection.
> > But if socket_post_accept() doesn't abort this connection,
> > the process who must not be able to accept this connection
> > will repeat accept() forever, which is a worse side effect.
I think this change is needed regardless of whether to use connection filtering or not.
Currently, SELinux doesn't use socket_post_accept().
| * @socket_post_accept:
| * This hook allows a security module to copy security
| * information into the newly created socket's inode.
But if one wants to *copy* security information to accept()ed socket,
the location after fd_install() is too late to copy
because the userland process can access accept()ed socket's fd
whose security information is not copied yet.
Also, if one wants to *assign* security information to accept()ed socket,
it might attend memory allocation which can fail.
So, use of void for socket_post_accept() deprives a security module of a chance to
abort this connection if the security module failed to *assign* security information.
Regards.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context.
2007-11-24 2:14 ` [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context Tetsuo Handa
@ 2007-11-29 4:57 ` Samir Bellabes
2007-11-30 14:07 ` Tetsuo Handa
0 siblings, 1 reply; 21+ messages in thread
From: Samir Bellabes @ 2007-11-29 4:57 UTC (permalink / raw)
To: Tetsuo Handa
Cc: jmorris, herbert, netdev, davem, linux-security-module, kaber,
netfilter-devel, sds
[-- Attachment #1: Type: text/plain, Size: 3177 bytes --]
Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> writes:
> Hello.
>
> James Morris wrote:
>> From memory, one approach under discussion was to add netfilter hooks to
>> the transport layer, which could be invoked correctly by each type of
>> protocol when the target process is selected.
>>
>> If this is done for netfilter, then an LSM hook is probably not needed at
>> all, as security modules can utilize netfilter hooks directly.
>
> Patrick McHardy says (at http://marc.info/?l=linux-netdev&m=118495005800410&w=2 )
> "Even with socket filters netfilter doesn't know the final receipient
> process, that is not known until it calls recvmsg and the data is read,
> which is too late for netfilter."
Indeed, but there is another approach, using libnetfilter_queue.
I have the same goal as TOMOYO, let the user decided filtering, with
informations coming from LSM hook, which give more datas to the users,
specially with the relationship between the socket and the
user/process.
My approach is to get the informations regarding the socket from
socket(), bind() and and accept() syscalls hooks.
Pushing this informations to userspace. Here the user can refuse or
accept the sycalls.
Then, for incoming data, we are using libnetfilter_queue from userspace.
And we can make a relation between the first delivered packet by
netfilter with our informations from the LSM hooks at socket(), bind() and
accept().
Let me get a example based on TCP/IP:
process X is executing this :
- userspace : socket()
- kernel : sys_socket()
- LSM : socket_create() -> pushing (process id, userid, protocol,
IP/ports) infos to userspace
- userspace : bind()
- kernel : sys_bind()
- LSM : socket_bind() -> pushing (process id, userid, protocol,
IP/ports) infos to userspace
- userspace : listen()
- kernel : sys_listen() -> pushing (process id, userid, protocol,
IP/ports) infos to userspace
As now the application is listening on a port, we are waiting for the
TCP SYN packet coming from remote side.
When TCP SYN arrive, we can capture it with libnetfilter_queue.
Then we can make a relation between informations in the hearders from
the TCP SYN packet: (src/dst address IPs, dst/src/ ports, procotol) ie
the netfilter tuple, and our informations from the LSM hooks, ie we know
that process X did socket(), bind(), listen() on the same src address
IP, src port and protocol.
Then we can decide to drop or accept this packet, with
libnetfilter_queue.
this approach seems better because we already have *all* the tools from
the kernel side, except form the managing LSM interface with userspace,
but I already have some improvements on this (patch attached)
Please consider this approach as a better side because using netfilter
for filtering packets is the best solution, and that we can make a
relation between socket and packets with this idea, and this feature can
stay in userspace.
FYI, this project is named "network events connector", some people from
netfilter-devel list may already know it, because I made a talk about it
for the last netfilter workshop.
thank you.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: cn_net.patch --]
[-- Type: text/x-patch, Size: 35258 bytes --]
diff --git a/drivers/connector/Kconfig b/drivers/connector/Kconfig
index 100bfd4..0d410f9 100644
--- a/drivers/connector/Kconfig
+++ b/drivers/connector/Kconfig
@@ -19,4 +19,12 @@ config PROC_EVENTS
Provide a connector that reports process events to userspace. Send
events such as fork, exec, id change (uid, gid, suid, etc), and exit.
+config NET_EVENTS
+ boolean "Report network events to userspace"
+ depends on CONNECTOR=y && SECURITY_NETWORK
+ default y
+ ---help---
+ Provide a connector that reports networking's events to userspace.
+ Send events such as DCCP/TCP listen/close and UDP bind/close.
+
endif # CONNECTOR
diff --git a/drivers/connector/Makefile b/drivers/connector/Makefile
index 1f255e4..436bb5d 100644
--- a/drivers/connector/Makefile
+++ b/drivers/connector/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_CONNECTOR) += cn.o
obj-$(CONFIG_PROC_EVENTS) += cn_proc.o
+obj-$(CONFIG_NET_EVENTS) += cn_net.o
cn-y += cn_queue.o connector.o
diff --git a/drivers/connector/cn_net.c b/drivers/connector/cn_net.c
new file mode 100644
index 0000000..4fde17f
--- /dev/null
+++ b/drivers/connector/cn_net.c
@@ -0,0 +1,1118 @@
+/*
+ * drivers/connector/cn_net.c
+ *
+ * Network events connector
+ * Samir Bellabes <sam@synack.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/security.h>
+#include <linux/netlink.h>
+#include <linux/connector.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <net/inet_sock.h>
+#include <linux/in.h>
+#include <linux/ipv6.h>
+#include <linux/in6.h>
+#include <linux/rbtree.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/cn_net.h>
+#include <asm/unaligned.h>
+
+/* when waiting for a verdict, you get added to this */
+DECLARE_WAIT_QUEUE_HEAD(cn_net_wq);
+
+static atomic_t net_event_num_listeners = ATOMIC_INIT(0);
+static struct cb_id cn_net_event_id = { CN_IDX_NET, CN_VAL_NET };
+static char cn_net_event_name[] = "cn_net_event";
+static int secondary = 0;
+
+static struct rb_root event_tree = RB_ROOT;
+static rwlock_t event_lock = RW_LOCK_UNLOCKED;
+static struct rb_root verdict_tree = RB_ROOT;
+static rwlock_t verdict_lock = RW_LOCK_UNLOCKED;
+
+/* should we print out debug messages */
+static int debug = 0;
+static int delay = 5;
+
+module_param(debug, bool, 0600);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+module_param(delay, uint, 0600);
+MODULE_PARM_DESC(delay, "Set the default pending delay for events");
+
+#if defined(CONFIG_NET_EVENTS)
+#define MY_NAME THIS_MODULE->name
+#else
+#define MY_NAME "cn_net"
+#endif
+
+#define cn_net_dbg(fmt, arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: %s: " fmt , \
+ MY_NAME , __FUNCTION__ , \
+ ## arg); \
+ } while (0)
+
+static const char *cn_net_syscall_name(const u8 syscall)
+{
+ static const char *syscall_name[] = {
+ [CN_NET_SOCKET_LISTEN] = "LISTEN",
+ [CN_NET_SOCKET_BIND] = "BIND",
+ [CN_NET_SOCKET_CONNECT] = "CONNECT",
+ [CN_NET_SOCKET_SHUTDOWN] = "SHUTDOWN",
+ [CN_NET_SK_FREE_SECURITY] = "SK_FREE",
+ [CN_NET_SOCKET_ACCEPT] = "ACCEPT",
+ [CN_NET_SOCKET_POST_ACCEPT] = "POST_ACCEPT",
+ };
+ return syscall_name[syscall];
+};
+
+static const char *cn_net_msg_type_name(const u8 msg_type)
+{
+ static const char *msg_type_name[] = {
+ [CN_NET_NONE] = "CN_NET_NONE",
+ [CN_NET_ACK] = "CN_NET_ACK",
+ [CN_NET_DATA] = "CN_NET_DATA",
+ [CN_NET_VERDICT] = "CN_NET_VERDICT",
+ [CN_NET_CONFIG] = "CN_NET_CONFIG",
+ [CN_NET_LISTEN] = "CN_NET_LISTEN",
+ [CN_NET_IGNORE] = "CN_NET_IGNORE",
+ [CN_NET_DUMP] = "CN_NET_DUMP",
+ };
+ return msg_type_name[msg_type];
+};
+
+static const char *cn_net_config_name(const u8 config)
+{
+ static const char *config_name[] = {
+ [CN_NET_CONFIG_ADD] = "CN_NET_CONFIG_ADD",
+ [CN_NET_CONFIG_DEL] = "CN_NET_CONFIG_DEL",
+ [CN_NET_CONFIG_FLUSH] = "CN_NET_CONFIG_FLUSH",
+ };
+ return config_name[config];
+}
+
+static const char *cn_net_verdict_name(const u8 verdict)
+{
+ static const char *verdict_name[] = {
+ [CN_NET_VERDICT_ACCEPT] = "CN_NET_VERDICT_ACCEPT",
+ [CN_NET_VERDICT_DENY] = "CN_NET_VERDICT_DENY",
+ [CN_NET_VERDICT_PENDING] = "CN_NET_VERDICT_PENDING",
+ [CN_NET_VERDICT_POLICY] = "CN_NET_VERDICT_POLICY",
+ };
+ return verdict_name[verdict];
+}
+
+/**
+ * is_same_event()
+ * check if two events are the same or not
+ */
+static unsigned int is_same_event(struct event one, struct event two)
+{
+ return ((one.syscall_num == two.syscall_num) &&
+ (one.protocol == two.protocol));
+}
+
+/**
+ * lookup_event()
+ * look for a match in the rbtree
+ * returns address of the wanted element if it is in the rbtree, or NULL
+ */
+static struct event_node *lookup_event(struct event ev)
+{
+ struct rb_node *rb_node = event_tree.rb_node;
+
+ read_lock(&event_lock);
+
+ while (rb_node) {
+ struct event_node *cur = rb_entry(rb_node, struct event_node, ev_node);
+
+ if (is_same_event(cur->ev, ev)) {
+ read_unlock(&event_lock);
+ return cur;
+ }
+
+ if (ev.syscall_num < cur->ev.syscall_num)
+ rb_node = rb_node->rb_left;
+ else if (ev.syscall_num > cur->ev.syscall_num)
+ rb_node = rb_node->rb_right;
+ else if (ev.protocol < cur->ev.protocol)
+ rb_node = rb_node->rb_left;
+ else if (ev.protocol > cur->ev.protocol)
+ rb_node = rb_node->rb_right;
+ }
+
+ read_unlock(&event_lock);
+ return NULL;
+}
+
+/**
+ * check_wanted_event()
+ * we don't send unwanted informations to userspace, according to the
+ * dynamic configuration in the rbtree
+ */
+static int check_wanted_event(struct sock *sk, enum cn_net_socket syscall_num)
+{
+ int err = -EINVAL;
+ struct event ev;
+
+ if (!sk)
+ return err;
+
+ ev.syscall_num = syscall_num;
+ ev.protocol = sk->sk_protocol;
+
+ /* check if the event is registered */
+ if (lookup_event(ev))
+ err = 0;
+
+ return err;
+}
+
+/**
+ * dump_event()
+ * dump the entire rbtree in log
+ */
+static void dump_event(void)
+{
+ struct rb_node *p = NULL ;
+ struct event_node *tmp = NULL;
+ struct verdict_node *vtmp = NULL;
+
+ read_lock(&event_lock);
+ p = rb_first(&event_tree);
+ while (p) {
+ tmp = rb_entry(p, struct event_node, ev_node);
+ printk(KERN_INFO "%d:%s\n", tmp->ev.protocol,
+ cn_net_syscall_name(tmp->ev.syscall_num));
+ p = rb_next(p);
+ }
+ read_unlock(&event_lock);
+
+ /* FIXME delete this code */
+ read_lock(&verdict_lock);
+ p = rb_first(&verdict_tree);
+ while (p) {
+ vtmp = rb_entry(p, struct verdict_node, v_node);
+ printk(KERN_INFO "0x%x:%s\n", vtmp->verdict.id,
+ cn_net_verdict_name(vtmp->verdict.v));
+ p = rb_next(p);
+ }
+ read_unlock(&verdict_lock);
+}
+
+/**
+ * remove_all_events()
+ * delete all events registered in the rbtree
+ */
+static enum ack_err remove_all_events(void)
+{
+ struct rb_node *p = NULL;
+ struct event_node *cur = NULL;
+
+ write_lock(&event_lock);
+ while ((p = rb_first(&event_tree)) != NULL) {
+ cur = rb_entry(p, struct event_node, ev_node);
+ rb_erase(p, &event_tree);
+ kfree(cur);
+ }
+ write_unlock(&event_lock);
+
+ /* rbtree is now empty */
+ return CN_NET_ACK_SUCCES;
+}
+
+/**
+ * alloc_event()
+ * alloc memory for a event_node, and return pointer to it
+ */
+static struct event_node *alloc_event(void)
+{
+ struct event_node *evn = NULL;
+ evn = kzalloc(sizeof(struct event_node), GFP_KERNEL);
+ return evn;
+}
+
+/**
+ * insert_event()
+ * insert a event in the rbtree
+ * we are checking if we are trying to register a same event twice
+ */
+static enum ack_err insert_event(struct event ev)
+{
+ struct rb_node **rb_link, *rb_parent;
+ struct event_node *new_evn;
+ enum ack_err ret = CN_NET_ACK_SUCCES;
+
+ rb_link = &event_tree.rb_node;
+ rb_parent = NULL;
+
+ if ((new_evn = alloc_event()) != NULL) {
+ new_evn->ev.syscall_num = ev.syscall_num;
+ new_evn->ev.protocol = ev.protocol;
+ } else {
+ /* can't allocate memory, exiting */
+ ret = CN_NET_ACK_ENOMEM;
+ goto out;
+ }
+
+ write_lock(&event_lock);
+
+ while(*rb_link) {
+ struct event_node *tmp;
+
+ rb_parent = *rb_link;
+ tmp = rb_entry(rb_parent, struct event_node, ev_node);
+
+ if (ev.syscall_num < tmp->ev.syscall_num)
+ rb_link = &rb_parent->rb_left;
+ else if (ev.syscall_num > tmp->ev.syscall_num)
+ rb_link = &rb_parent->rb_right;
+ else if (ev.protocol < tmp->ev.protocol)
+ rb_link = &rb_parent->rb_left;
+ else if (ev.protocol > tmp->ev.protocol)
+ rb_link = &rb_parent->rb_right;
+ else {
+ /* event is already registered */
+ write_unlock(&event_lock);
+ ret = CN_NET_ACK_EINCONFIG;
+ goto out_free;
+ }
+ }
+
+ /* no match: event is added to the tree */
+ rb_link_node(&new_evn->ev_node, rb_parent, rb_link);
+ rb_insert_color(&new_evn->ev_node, &event_tree);
+ write_unlock(&event_lock);
+ return ret;
+
+out_free:
+ kfree(new_evn);
+out:
+ return ret;
+}
+
+/**
+ * remove_event()
+ * delete a entry from the rbtree
+ * we are checking if we are trying to delete a unregistered event
+ */
+static enum ack_err remove_event(struct event ev)
+{
+ struct event_node *cur = NULL;
+ enum ack_err ret = CN_NET_ACK_EINCONFIG;
+ struct rb_node *rb_node = event_tree.rb_node;
+
+ write_lock(&event_lock);
+
+ while (rb_node) {
+ cur = rb_entry(rb_node, struct event_node, ev_node);
+
+ if (is_same_event(cur->ev, ev))
+ break;
+
+ if (ev.syscall_num < cur->ev.syscall_num)
+ rb_node = rb_node->rb_left;
+ else if (ev.syscall_num > cur->ev.syscall_num)
+ rb_node = rb_node->rb_right;
+ else if (ev.protocol < cur->ev.protocol)
+ rb_node = rb_node->rb_left;
+ else if (ev.protocol > cur->ev.protocol)
+ rb_node = rb_node->rb_right;
+ }
+
+ if (rb_node) {
+ rb_erase(&cur->ev_node, &event_tree);
+ kfree(cur);
+ ret = CN_NET_ACK_SUCCES;
+ }
+
+ write_unlock(&event_lock);
+ return ret;
+}
+
+/* routine for verdict_tree */
+
+/**
+ * __lookup_verdict()
+ * look for a match in the rbtree
+ * this function is not lock-safe, remember to lock verdict_lock when using
+ * returns address of the verdict_node if it is in the rbtree, or NULL
+ */
+static struct verdict_node *__lookup_verdict(__u32 id)
+{
+ struct rb_node *rb_node = verdict_tree.rb_node;
+
+ while (rb_node) {
+ struct verdict_node *cur = rb_entry(rb_node, struct verdict_node, v_node);
+
+ if (cur->verdict.id == id) {
+ return cur;
+ }
+
+ if (id < cur->verdict.id)
+ rb_node = rb_node->rb_left;
+ else if (id > cur->verdict.id)
+ rb_node = rb_node->rb_right;
+ }
+
+ return NULL;
+}
+
+/**
+ * apply_policy()
+ * when timer expires for a verdict, we apply the policy by default
+ */
+static void apply_policy(unsigned long arg)
+{
+ struct verdict_node *vnode;
+ __u32 id = (__u32) arg;
+
+ write_lock(&verdict_lock);
+ vnode = __lookup_verdict(id);
+ if (vnode) {
+ vnode->verdict.v = DEFAULT_POLICY;
+ cn_net_dbg("apply_policy: id=0x%x verdict %d %s\n", /* FIXME debug to delete */
+ vnode->verdict.id,
+ (int)vnode->verdict.v,
+ cn_net_verdict_name((int)vnode->verdict.v));
+ }
+ write_unlock(&verdict_lock);
+ wake_up(&cn_net_wq);
+}
+
+/**
+ * alloc_verdict()
+ * alloc memory for a verdict_node, and return pointer to it
+ */
+static struct verdict_node *alloc_verdict(void)
+{
+ struct verdict_node *vnode = NULL;
+ vnode = kzalloc(sizeof(struct verdict_node), GFP_KERNEL);
+ return vnode;
+}
+
+/**
+ * insert_verdict()
+ * insert a verdict in the rbtree
+ * we are checking if we are trying to register a same verdict twice
+ * Returns 0 if insertion is ok, else -1
+ */
+static int insert_verdict(__u32 id)
+{
+ struct rb_node **rb_link, *rb_parent;
+ struct verdict_node *new_verdict;
+ enum ack_err ret = CN_NET_ACK_SUCCES;
+
+ rb_link = &verdict_tree.rb_node;
+ rb_parent = NULL;
+
+ if ((new_verdict = alloc_verdict()) != NULL) {
+ new_verdict->verdict.id = id;
+ new_verdict->verdict.v = CN_NET_VERDICT_PENDING;
+ } else {
+ /* can't allocate memory, exiting */
+ ret = CN_NET_ACK_ENOMEM;
+ goto out;
+ }
+
+ write_lock(&verdict_lock);
+
+ while(*rb_link) {
+ struct verdict_node *tmp;
+
+ rb_parent = *rb_link;
+ tmp = rb_entry(rb_parent, struct verdict_node, v_node);
+
+ if (id < tmp->verdict.id)
+ rb_link = &rb_parent->rb_left;
+ else if (id > tmp->verdict.id)
+ rb_link = &rb_parent->rb_right;
+ else {
+ /* verdict is already registered */
+ write_unlock(&verdict_lock);
+ ret = CN_NET_ACK_EINCONFIG;
+ goto out_free;
+ }
+ }
+
+ /* setting timer to set defaut policy at expiration */
+ init_timer(&new_verdict->timer);
+ new_verdict->timer.expires = jiffies + delay*HZ;
+ new_verdict->timer.data = (unsigned long)id;
+ new_verdict->timer.function = apply_policy;
+ /* no match: verdict is added to the tree */
+ rb_link_node(&new_verdict->v_node, rb_parent, rb_link);
+ rb_insert_color(&new_verdict->v_node, &verdict_tree);
+ write_unlock(&verdict_lock);
+ return 0;
+
+out_free:
+ kfree(new_verdict);
+out:
+ if (ret != CN_NET_ACK_SUCCES) /* error occured, inform userspace with CN_NET_ACK */
+ cn_net_ack(ret, id, 0, CN_NET_ACK);
+ return -1;
+}
+
+/* routine for verdict_tree */
+
+
+/**
+ * do_register()
+ * check if userpace protocol version is same as kernel protocol version
+ */
+static enum ack_err do_register(__u32 version)
+{
+ enum ack_err err = CN_NET_ACK_SUCCES;
+
+ cn_net_dbg("do_register: %d %d\n",
+ version, CN_NET_VERSION);
+
+ if (version == CN_NET_VERSION)
+ atomic_inc(&net_event_num_listeners);
+ else
+ err = CN_NET_ACK_EBADPROTO;
+ return err;
+}
+
+/**
+ * do_config()
+ * execute config asked by userspace
+ * return enum ack_err
+ */
+static enum ack_err do_config(struct config_msg *cfg)
+{
+ enum ack_err err = CN_NET_ACK_SUCCES;
+
+ cn_net_dbg("do_config: %s %s %d\n",
+ cn_net_config_name(cfg->config_cmd),
+ cn_net_syscall_name(cfg->ev.syscall_num),
+ cfg->ev.protocol);
+
+ switch (cfg->config_cmd) {
+ case CN_NET_CONFIG_ADD:
+ err = insert_event(cfg->ev);
+ break;
+ case CN_NET_CONFIG_DEL:
+ err= remove_event(cfg->ev);
+ break;
+ case CN_NET_CONFIG_FLUSH:
+ err = remove_all_events();
+ break;
+ default:
+ err = CN_NET_ACK_EINTYPE;
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * do_verdict()
+ * execute verdict for a CN_NET_DATA message
+ * return enum ack_err
+ */
+static enum ack_err do_verdict(struct verdict_msg *vdt)
+{
+ enum ack_err err = CN_NET_ACK_SUCCES;
+ struct verdict_node *vnode = NULL;
+
+ cn_net_dbg("do_verdict: 0x%x %d\n",
+ vdt->id, vdt->v);
+
+ switch (vdt->v) {
+ case CN_NET_VERDICT_ACCEPT:
+ case CN_NET_VERDICT_DENY:
+ case CN_NET_VERDICT_POLICY:
+ write_lock(&verdict_lock);
+ vnode = __lookup_verdict(vdt->id);
+ if (vnode) {
+ vnode->verdict.v = vdt->v;
+ write_unlock(&verdict_lock);
+ wake_up(&cn_net_wq);
+ } else {
+ write_unlock(&verdict_lock);
+ err = CN_NET_ACK_EINCONFIG;
+ }
+ break;
+ default:
+ err = CN_NET_ACK_EINTYPE;
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * cn_net_ack
+ * Send an acknowledgement message to userspace
+ *
+ * Use 0 for SUCCESS, EFOO otherwise. (see enum ack_err)
+ */
+static void cn_net_ack(enum ack_err err, unsigned int rcvd_seq,
+ unsigned int rcvd_ack, enum msg_type msg_type)
+{
+ struct cn_msg *m;
+ struct net_event *net_ev;
+ struct timespec ts;
+ __u8 buffer[CN_NET_MSG_SIZE];
+
+ cn_net_dbg("cn_net_ack: listen=%d\n",
+ atomic_read(&net_event_num_listeners));
+
+ /* no listener, no response, excepted for notifying that
+ protocol version is not the same as in kernel */
+ if (atomic_read(&net_event_num_listeners) < 1 &&
+ err != CN_NET_ACK_EBADPROTO)
+ return;
+
+ m = (struct cn_msg *) buffer;
+ net_ev = (struct net_event *) m->data;
+
+ net_ev->msg_type = msg_type;
+/* ktime_get_real_ts(&net_ev->timestamp); */
+ ktime_get_real_ts(&ts);
+ put_unaligned(timespec_to_ns(&ts), (__u64 *)&net_ev->timestamp_ns);
+ net_ev->net_event_data.ack = err;
+ memcpy(&m->id, &cn_net_event_id, sizeof(m->id));
+ m->seq = rcvd_seq;
+ m->ack = rcvd_ack + 1;
+ m->len = sizeof(struct net_event);
+ cn_netlink_send(m, CN_IDX_NET, gfp_any());
+}
+
+/**
+ * cn_net_ctl
+ * connector callback
+ * @data: message receive from userspace via the connector
+ */
+void cn_net_ctl(void *data)
+{
+ struct cn_msg *m = data;
+ struct net_event *net_ev = NULL;
+ enum msg_type msg_type;
+ enum ack_err err = CN_NET_ACK_SUCCES;
+
+ if (m->len != sizeof(struct net_event)) {
+ cn_net_dbg("cn_net_ctl : message with "
+ "bad size, discard\n");
+ return;
+ }
+
+ net_ev = (struct net_event *) m->data;
+
+ if (net_ev->msg_type != CN_NET_LISTEN &&
+ atomic_read(&net_event_num_listeners) < 1) {
+ cn_net_dbg("cn_net_ctl : register first\n");
+ return;
+ }
+ msg_type = CN_NET_ACK; /* default response message type */
+
+ switch (net_ev->msg_type) {
+ case CN_NET_NONE: /* want to play ping pong ? */
+ msg_type = net_ev->msg_type;
+ break;
+ case CN_NET_ACK: /* userspace is ack'ing - check that */
+ /* FIXME: we don't send an ACK to an ACK */
+ /* we just check which message is ack */
+ goto out;
+ break;
+ case CN_NET_DATA: /* CN_NET_DATA can't be used by userspace */
+ err = CN_NET_ACK_EINTYPE;
+ break;
+ case CN_NET_VERDICT: /* userspace give verdict for the CN_NET_DATA msg*/
+ err = do_verdict(&(net_ev->net_event_data.verdict));
+ break;
+ case CN_NET_CONFIG: /* configuring kernel's behaviour */
+ err = do_config(&(net_ev->net_event_data.config));
+ break;
+ case CN_NET_LISTEN: /* userspace is registering */
+ err = do_register(net_ev->net_event_data.version);
+ break;
+ case CN_NET_IGNORE: /* userspace is unregistering */
+ atomic_dec(&net_event_num_listeners);
+ break;
+ case CN_NET_DUMP: /* dumping the rbtree -- debug purpose */
+ dump_event();
+ break;
+ default:
+ err = CN_NET_ACK_EINTYPE;
+ break;
+ }
+ cn_net_ack(err, m->seq, m->ack, msg_type);
+out:
+ return;
+}
+
+/**
+ * cn_net_send_event
+ * send data to userspace
+ * @sock: sock which sock->sk->sk_state change to the state identified by @event
+ */
+static __u32 cn_net_send_event(struct sock *sk, struct sockaddr *address,
+ enum cn_net_socket syscall_num)
+{
+ struct net_event *net_ev;
+ struct cn_msg *m;
+ struct inet_sock *inet = inet_sk(sk);
+ struct sockaddr_in *addr4 = NULL;
+ struct sockaddr_in6 *addr6 = NULL;
+ struct task_struct *me = current;
+ struct verdict_node *vnode = NULL;
+ struct timespec ts;
+ int insertion = -1;
+
+ __u8 buffer[CN_NET_MSG_SIZE];
+
+ cn_net_dbg("cn_net_ack: listen=%d\n",
+ atomic_read(&net_event_num_listeners));
+
+ if (atomic_read(&net_event_num_listeners) < 1)
+ goto out;
+
+ m = (struct cn_msg *) buffer;
+ net_ev = (struct net_event *) m->data;
+
+ switch (syscall_num) {
+ case CN_NET_SOCKET_LISTEN:
+ case CN_NET_SOCKET_SHUTDOWN:
+ case CN_NET_SK_FREE_SECURITY:
+ switch (sk->sk_family) {
+ case AF_INET:
+ net_ev->net_event_data.data.saddr.ipv4 = inet->saddr;
+ net_ev->net_event_data.data.daddr.ipv4 = inet->daddr;
+ break;
+ case AF_INET6:
+ memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
+ &(inet->pinet6->saddr), sizeof(struct in6_addr));
+ memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
+ &(inet->pinet6->daddr), sizeof(struct in6_addr));
+ break;
+ default:
+ /* other protocol, sending nothing */
+ goto out;
+ break;
+ };
+ net_ev->net_event_data.data.sport = ntohs(inet->sport);
+ net_ev->net_event_data.data.dport = ntohs(inet->dport);
+ break;
+ case CN_NET_SOCKET_BIND:
+ switch (sk->sk_family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *) address;
+ net_ev->net_event_data.data.saddr.ipv4 = addr4->sin_addr.s_addr;
+ net_ev->net_event_data.data.daddr.ipv4 = inet->daddr;
+ net_ev->net_event_data.data.sport = ntohs(addr4->sin_port);
+ net_ev->net_event_data.data.dport = ntohs(inet->dport);
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *) address;
+ memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
+ &(addr6->sin6_addr), sizeof(struct in6_addr));
+ memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
+ &(inet->pinet6->daddr), sizeof(struct in6_addr));
+ net_ev->net_event_data.data.sport = ntohs(addr6->sin6_port);
+ net_ev->net_event_data.data.dport = ntohs(inet->dport);
+ break;
+ default:
+ /* other protocol, sending nothing */
+ goto out;
+ break;
+ };
+ break;
+ case CN_NET_SOCKET_CONNECT:
+ switch (sk->sk_family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *) address;
+ net_ev->net_event_data.data.saddr.ipv4 = inet->saddr;
+ net_ev->net_event_data.data.daddr.ipv4 = addr4->sin_addr.s_addr;
+ net_ev->net_event_data.data.sport = ntohs(inet->sport);
+ net_ev->net_event_data.data.dport = ntohs(addr4->sin_port);
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *) address;
+ memcpy(&(net_ev->net_event_data.data.saddr.ipv6),
+ &(inet->pinet6->saddr), sizeof(struct in6_addr));
+ memcpy(&(net_ev->net_event_data.data.daddr.ipv6),
+ &(addr6->sin6_addr), sizeof(struct in6_addr));
+ net_ev->net_event_data.data.sport = ntohs(inet->sport);
+ net_ev->net_event_data.data.dport = ntohs(addr6->sin6_port);
+ break;
+ default:
+ /* other protocol, sending nothing */
+ goto out;
+ break;
+ };
+ break;
+ default:
+ /* Bad syscall_num */
+ break;
+ };
+ net_ev->msg_type = CN_NET_DATA;
+/* ktime_get_real_ts(&net_ev->timestamp); */
+ ktime_get_real_ts(&ts);
+ put_unaligned(timespec_to_ns(&ts), (__u64 *)&net_ev->timestamp_ns);
+ net_ev->net_event_data.data.uid = me->uid;
+/* get_task_comm(net_ev->net_event_data.data.taskname, me); */
+ net_ev->net_event_data.data.pid = me->pid;
+ net_ev->net_event_data.data.ev.protocol = sk->sk_protocol;
+ net_ev->net_event_data.data.ev.syscall_num = syscall_num;
+ net_ev->net_event_data.data.family = sk->sk_family;
+ memcpy(&m->id, &cn_net_event_id, sizeof(m->id));
+ m->seq = get_random_int();
+ m->ack = 0;
+ m->len = sizeof(struct net_event);
+
+ /* adding node entry to verdict_tree */
+ insertion = insert_verdict(m->seq);
+
+ if (insertion == 0) { /* sending CN_NET_DATA */
+ cn_netlink_send(m, CN_IDX_NET, gfp_any());
+ /* starting timer for default policy */
+ write_lock(&verdict_lock);
+ vnode = __lookup_verdict(m->seq);
+ if (vnode)
+ add_timer(&vnode->timer);
+ write_unlock(&verdict_lock);
+ }
+out:
+ return m->seq;
+}
+
+static __u32 get_secure_verdict(__u32 id)
+{
+ struct verdict_node *vnode = NULL;
+ __u32 v;
+
+ read_lock(&verdict_lock);
+ vnode = __lookup_verdict(id);
+ if (vnode)
+ v = vnode->verdict.v;
+ else
+ v = DEFAULT_POLICY;
+ read_unlock(&verdict_lock);
+ return v;
+}
+
+static int cn_net_socket_listen(struct socket *sock, int backlog)
+{
+ struct verdict_node *vnode = NULL;
+ enum verdict v;
+ __u32 id = 0;
+
+ DECLARE_WAITQUEUE(myself, current);
+
+ cn_net_dbg("cn_net_socket_listen\n");
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_LISTEN) == 0)
+ id = cn_net_send_event(sock->sk, NULL, CN_NET_SOCKET_LISTEN);
+
+/* if (!vnode) { */
+/* cn_net_dbg("cn_net_socket_listen: apply default_policy vnode == NULL\n"); */
+/* v = DEFAULT_POLICY; */
+/* } else { */
+ add_wait_queue(&cn_net_wq, &myself);
+ for(;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if ((v = get_secure_verdict(id)) != CN_NET_VERDICT_PENDING)
+ break;
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&cn_net_wq, &myself);
+ /* remove verdict_node for rbtree */
+ write_lock(&verdict_lock);
+ vnode = __lookup_verdict(id);
+ if (vnode) {
+ rb_erase(&vnode->v_node, &verdict_tree);
+ del_timer_sync(&vnode->timer);
+ }
+ write_unlock(&verdict_lock);
+ kfree(vnode);
+/* } */
+ cn_net_dbg("cn_net_socket_listen: verdict %d %s\n", (int) v,
+ cn_net_verdict_name((int)v));
+ return v == CN_NET_VERDICT_POLICY ? DEFAULT_POLICY : (int)v;
+}
+
+static int cn_net_socket_bind(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct verdict_node *vnode = NULL;
+ enum verdict v;
+ __u32 id = 0;
+
+ DECLARE_WAITQUEUE(myself, current);
+
+ cn_net_dbg("cn_net_socket_bind\n");
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_BIND) == 0)
+ id = cn_net_send_event(sock->sk, address, CN_NET_SOCKET_BIND);
+
+/* if (!vnode) { */
+/* cn_net_dbg("cn_net_socket_bind: apply default_policy vnode == NULL\n"); */
+/* v = DEFAULT_POLICY; */
+/* } else { */
+ add_wait_queue(&cn_net_wq, &myself);
+ for(;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if ((v = get_secure_verdict(id)) != CN_NET_VERDICT_PENDING)
+ break;
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&cn_net_wq, &myself);
+ /* remove verdict_node for rbtree */
+ write_lock(&verdict_lock);
+ vnode = __lookup_verdict(id);
+ if (vnode) {
+ rb_erase(&vnode->v_node, &verdict_tree);
+ del_timer_sync(&vnode->timer);
+ }
+ write_unlock(&verdict_lock);
+ kfree(vnode);
+/* } */
+ cn_net_dbg("cn_net_socket_bind: verdict %d %s\n", (int) v,
+ cn_net_verdict_name((int)v));
+ return v == CN_NET_VERDICT_POLICY ? DEFAULT_POLICY : (int)v;
+
+}
+
+static int cn_net_socket_connect(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct verdict_node *vnode = NULL;
+ enum verdict v;
+ __u32 id = 0;
+
+ DECLARE_WAITQUEUE(myself, current);
+
+ cn_net_dbg("cn_net_socket_connect\n");
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_CONNECT) == 0)
+ id = cn_net_send_event(sock->sk, address, CN_NET_SOCKET_CONNECT);
+
+/* if (!vnode) { */
+/* cn_net_dbg("cn_net_socket_connect: apply default_policy vnode == NULL\n"); */
+/* v = DEFAULT_POLICY; */
+/* } else { */
+ add_wait_queue(&cn_net_wq, &myself);
+ for(;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if ((v = get_secure_verdict(id)) != CN_NET_VERDICT_PENDING)
+ break;
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&cn_net_wq, &myself);
+ /* remove verdict_node for rbtree */
+ write_lock(&verdict_lock);
+ vnode = __lookup_verdict(id);
+ if (vnode) {
+ rb_erase(&vnode->v_node, &verdict_tree);
+ del_timer_sync(&vnode->timer);
+ }
+ write_unlock(&verdict_lock);
+ kfree(vnode);
+/* } */
+ cn_net_dbg("cn_net_socket_connect: verdict %d %s\n", (int) v,
+ cn_net_verdict_name((int)v));
+ return v == CN_NET_VERDICT_POLICY ? DEFAULT_POLICY : (int)v;
+}
+
+static int cn_net_socket_shutdown(struct socket *sock, int how)
+{
+ struct verdict_node *vnode = NULL;
+ enum verdict v;
+ __u32 id = 0;
+
+ DECLARE_WAITQUEUE(myself, current);
+
+ cn_net_dbg("cn_net_socket_shutdown\n");
+ if (check_wanted_event(sock->sk, CN_NET_SOCKET_SHUTDOWN) == 0)
+ id = cn_net_send_event(sock->sk, NULL, CN_NET_SOCKET_SHUTDOWN);
+
+/* if (!vnode) { */
+/* cn_net_dbg("cn_net_socket_shutdown: apply default_policy vnode == NULL\n"); */
+/* v = DEFAULT_POLICY; */
+/* } else { */
+ add_wait_queue(&cn_net_wq, &myself);
+ for(;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if ((v = get_secure_verdict(id)) != CN_NET_VERDICT_PENDING)
+ break;
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&cn_net_wq, &myself);
+ /* remove verdict_node for rbtree */
+ write_lock(&verdict_lock);
+ vnode = __lookup_verdict(id);
+ if (vnode) {
+ rb_erase(&vnode->v_node, &verdict_tree);
+ del_timer_sync(&vnode->timer);
+ }
+ write_unlock(&verdict_lock);
+ kfree(vnode);
+/* } */
+ cn_net_dbg("cn_net_socket_shutdown: verdict %d %s\n", (int) v,
+ cn_net_verdict_name((int)v));
+ return v == CN_NET_VERDICT_POLICY ? DEFAULT_POLICY : (int)v;
+}
+
+static void cn_net_sk_free_security(struct sock *sk)
+{
+ struct verdict_node *vnode = NULL;
+ enum verdict v;
+ __u32 id = 0;
+
+ DECLARE_WAITQUEUE(myself, current);
+
+ cn_net_dbg("cn_net_sk_free_security\n");
+ if (check_wanted_event(sk, CN_NET_SK_FREE_SECURITY) == 0)
+ id = cn_net_send_event(sk, NULL, CN_NET_SK_FREE_SECURITY);
+
+/* if (!vnode) { */
+/* cn_net_dbg("cn_net_socket_free_security: apply default_policy vnode == NULL\n"); */
+/* v = DEFAULT_POLICY; */
+/* } else { */
+ add_wait_queue(&cn_net_wq, &myself);
+ for(;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if ((v = get_secure_verdict(id)) != CN_NET_VERDICT_PENDING)
+ break;
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&cn_net_wq, &myself);
+ /* remove verdict_node for rbtree */
+ write_lock(&verdict_lock);
+ vnode = __lookup_verdict(id);
+ if (vnode) {
+ rb_erase(&vnode->v_node, &verdict_tree);
+ del_timer_sync(&vnode->timer);
+ }
+ write_unlock(&verdict_lock);
+ kfree(vnode);
+/* } */
+ cn_net_dbg("cn_net_socket_free_security: verdict %d %s\n", (int) v,
+ cn_net_verdict_name((int)v));
+ return;
+}
+
+static void cn_net_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+ struct inet_sock *inet;
+ struct inet_sock *newinet;
+
+ inet = inet_sk(sock->sk);
+ newinet = inet_sk(newsock->sk);
+
+ cn_net_dbg("cn_net_socket_post_accept\n");
+/* if (check_wanted_event(sk, CN_NET_SOCKET_ACCEPT) == 0) */
+/* cn_net_socket_accept(sk, NULL, CN_NET_SOCKET_ACCEPT); */
+ printk(KERN_INFO "post_accept: type: %d:%d sock=("NIPQUAD_FMT":%u -> "NIPQUAD_FMT":%u) "
+ "newsock=("NIPQUAD_FMT":%u -> "NIPQUAD_FMT":%u)\n",
+ sock->type, newsock->type,
+ NIPQUAD(inet->saddr), ntohs(inet->sport),
+ NIPQUAD(inet->daddr), ntohs(inet->dport),
+ NIPQUAD(newinet->saddr), ntohs(newinet->sport),
+ NIPQUAD(newinet->daddr), ntohs(newinet->dport));
+
+ return;
+}
+
+static int cn_net_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ struct inet_sock *inet;
+
+ inet = inet_sk(sock->sk);
+
+ cn_net_dbg("cn_net_socket_accept\n");
+/* if (check_wanted_event(sk, CN_NET_SOCKET_ACCEPT) == 0) */
+/* cn_net_socket_accept(sk, NULL, CN_NET_SOCKET_ACCEPT); */
+ printk(KERN_INFO "accept: %d: sock=("NIPQUAD_FMT":%u -> "NIPQUAD_FMT":%u)\n",
+ sock->type,
+ NIPQUAD(inet->saddr), ntohs(inet->sport),
+ NIPQUAD(inet->daddr), ntohs(inet->dport));
+
+ return 0;
+}
+
+static struct security_operations cn_net_security_ops = {
+ .socket_listen = cn_net_socket_listen,
+ .socket_bind = cn_net_socket_bind,
+ .socket_connect = cn_net_socket_connect,
+ .socket_shutdown = cn_net_socket_shutdown,
+ .sk_free_security = cn_net_sk_free_security,
+ .socket_accept = cn_net_socket_accept,
+ .socket_post_accept = cn_net_socket_post_accept,
+};
+
+static int __init init(void)
+{
+ int err = 0;
+
+ err = cn_add_callback(&cn_net_event_id, cn_net_event_name, &cn_net_ctl);
+ if (err) {
+ printk(KERN_WARNING "cn_net: failure add connector callback\n");
+ goto out_callback;
+ }
+
+ if (register_security(&cn_net_security_ops)) {
+ printk(KERN_INFO "cn_net: failure registering with kernel\n");
+ if (mod_reg_security(MY_NAME, &cn_net_security_ops)) {
+ printk(KERN_WARNING
+ "cn_net: failure registering with "
+ "primary security module\n");
+ err = -EINVAL;
+ goto out_security;
+ }
+ secondary = 1;
+ }
+
+ printk(KERN_INFO "cn_net: network events module loaded\n");
+ return 0;
+
+out_security:
+ cn_del_callback(&cn_net_event_id);
+
+out_callback:
+ return err;
+}
+
+static void __exit fini(void)
+{
+ if (secondary) {
+ if (mod_unreg_security(MY_NAME, &cn_net_security_ops))
+ printk(KERN_INFO "cn_net: failure unregistering with "
+ "primary security module\n");
+ } else {
+ if (unregister_security(&cn_net_security_ops))
+ printk(KERN_INFO "cn_net: failure unregistering with "
+ "kernel\n");
+ }
+
+ cn_del_callback(&cn_net_event_id);
+
+ /* clean memory */
+ remove_all_events();
+
+ printk(KERN_INFO "cn_net: network events module unloaded\n");
+}
+
+module_init(init);
+module_exit(fini);
+
+MODULE_DESCRIPTION("Network events module");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samir Bellabes <sam@synack.fr>");
diff --git a/include/linux/cn_net.h b/include/linux/cn_net.h
new file mode 100644
index 0000000..8c8ba56
--- /dev/null
+++ b/include/linux/cn_net.h
@@ -0,0 +1,146 @@
+/*
+ * include/linux/cn_net.h
+ *
+ * Network events connector
+ * Samir Bellabes <sam@synack.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef CN_NET_H
+#define CN_NET_H
+
+#include <linux/time.h>
+#include <linux/ipv6.h>
+
+#define CN_NET_VERSION 0x1
+#define DEFAULT_POLICY CN_NET_VERDICT_ACCEPT
+
+#define CN_NET_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct net_event))
+
+/**
+ * identify which syscall has been called.
+ */
+enum cn_net_socket {
+ CN_NET_SOCKET_LISTEN = 0,
+ CN_NET_SOCKET_BIND,
+ CN_NET_SOCKET_CONNECT,
+ CN_NET_SOCKET_SHUTDOWN,
+ CN_NET_SK_FREE_SECURITY,
+ CN_NET_SOCKET_ACCEPT,
+ CN_NET_SOCKET_POST_ACCEPT,
+ CN_NET_SOCKET_MAX,
+};
+
+/**
+ * Protocol message type
+ */
+enum msg_type {
+ CN_NET_NONE = 0,
+ CN_NET_ACK,
+ CN_NET_DATA,
+ CN_NET_VERDICT,
+ CN_NET_CONFIG,
+ CN_NET_LISTEN,
+ CN_NET_IGNORE,
+ CN_NET_DUMP,
+};
+
+/**
+ * values for CN_NET_ACK messages
+ */
+enum ack_err {
+ CN_NET_ACK_SUCCES = 0,
+ CN_NET_ACK_ENOMEM,
+ CN_NET_ACK_EINCONFIG,
+ CN_NET_ACK_EINTYPE,
+ CN_NET_ACK_EBADPROTO,
+};
+
+/**
+ * values for CN_NET_VERDICT messages
+ */
+enum verdict {
+ CN_NET_VERDICT_ACCEPT = 0,
+ CN_NET_VERDICT_DENY,
+ CN_NET_VERDICT_PENDING,
+ CN_NET_VERDICT_POLICY,
+};
+
+/**
+ * values for CN_NET_CONFIG messages
+ */
+enum config_cmd {
+ CN_NET_CONFIG_ADD = 0,
+ CN_NET_CONFIG_DEL,
+ CN_NET_CONFIG_FLUSH,
+};
+
+struct event {
+ enum cn_net_socket syscall_num;
+ __u8 protocol;
+};
+
+struct event_node {
+ struct rb_node ev_node;
+ struct event ev;
+};
+
+struct config_msg {
+ enum config_cmd config_cmd;
+ struct event ev;
+};
+
+struct verdict_msg {
+ __u32 id;
+ enum verdict v;
+};
+
+struct verdict_node {
+ struct rb_node v_node;
+ struct timer_list timer;
+ struct verdict_msg verdict;
+};
+
+struct net_event {
+ enum msg_type msg_type;
+ __u64 __attribute__((aligned(8))) timestamp_ns;
+ union {
+ /* protocol version number */
+ __u32 version;
+
+ /* generic ack for both userspace and kernel */
+ enum ack_err ack;
+
+ /* send data to userspace */
+ struct {
+ struct event ev;
+ uid_t uid;
+ pid_t pid;
+ unsigned int family;
+ union {
+ struct in6_addr ipv6;
+ __u32 ipv4;
+ } saddr;
+ union {
+ struct in6_addr ipv6;
+ __u32 ipv4;
+ } daddr;
+ unsigned int sport;
+ unsigned int dport;
+ } data;
+
+ /* send config to kernel */
+ struct config_msg config;
+ /* send verdict to kernel */
+ struct verdict_msg verdict;
+ } net_event_data;
+};
+
+static void cn_net_ack(enum ack_err err, unsigned int rcvd_seq,
+ unsigned int rcvd_ack, enum msg_type msg_type);
+
+#endif /* CN_NET_H */
diff --git a/include/linux/connector.h b/include/linux/connector.h
index 10eb56b..3042360 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -36,9 +36,10 @@ #define CN_IDX_CIFS 0x2
#define CN_VAL_CIFS 0x1
#define CN_W1_IDX 0x3 /* w1 communication */
#define CN_W1_VAL 0x1
+#define CN_IDX_NET 0x4
+#define CN_VAL_NET 0x1
-
-#define CN_NETLINK_USERS 4
+#define CN_NETLINK_USERS 5
/*
* Maximum connector's message size.
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context.
2007-11-29 4:57 ` Samir Bellabes
@ 2007-11-30 14:07 ` Tetsuo Handa
2007-11-30 14:29 ` Samir Bellabes
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2007-11-30 14:07 UTC (permalink / raw)
To: sam
Cc: jmorris, herbert, netdev, davem, linux-security-module, kaber,
netfilter-devel, sds
Hello.
Thank you for feedback.
I have some questions.
(1) Your module uses "struct security_operations" and
is registered with register_security().
TOMOYO also uses "struct security_operations" and
must be registered with register_security().
Can your module and TOMOYO coexist?
(2) Can your module handle incoming UDP's datagram too?
(3) How do you handle race window between security_socket_accept()
and sock->ops->accept()?
Samir Bellabes wrote:
> My approach is to get the informations regarding the socket from
> socket(), bind() and and accept() syscalls hooks.
> Pushing this informations to userspace. Here the user can refuse or
> accept the sycalls.
If a socket is shared by multiple processes and
the user said "Accept this connection" at security_socket_accept(),
the process does not always pick up what the user wanted to pick up
because there is no mutex between security_socket_accept() and
sock->ops->accept().
(4) If (3) is my misunderstanding (i.e. the user doesn't judge at security_socket_accept()),
it means that the user judges it before select() says "ready to accept".
Then ... what happens if a socket is shared by multiple programs?
The user has to judge without knowing which program will pick up this connection.
TOMOYO lets the user judge whether to accept or not
with knowing the program's name who picks up this connection.
I think getting information at socket(), bind() and accept() is helpful for detailed judgement,
but I think it doesn't solve race problem as long as the user judges *before* sock->ops->accept().
Regards.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context.
2007-11-30 14:07 ` Tetsuo Handa
@ 2007-11-30 14:29 ` Samir Bellabes
2007-11-30 14:59 ` Tetsuo Handa
0 siblings, 1 reply; 21+ messages in thread
From: Samir Bellabes @ 2007-11-30 14:29 UTC (permalink / raw)
To: Tetsuo Handa
Cc: jmorris, herbert, netdev, davem, linux-security-module, kaber,
netfilter-devel, sds
Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> writes:
> Hello.
>
> Thank you for feedback.
>
> I have some questions.
>
> (1) Your module uses "struct security_operations" and
> is registered with register_security().
>
> TOMOYO also uses "struct security_operations" and
> must be registered with register_security().
>
> Can your module and TOMOYO coexist?
Not sure about that.
I already had concerns about the coexistance of my module and SELinux.
I have to take time for that.
> (2) Can your module handle incoming UDP's datagram too?
yes, and better.
In fact, information is not dependant of the protocol at all.
What I mean is that we focus on network's syscalls, so for me, protocol
informations is let to the user for his/her decision.
When the module is catching bind(), we don't care if it's
TCP/DCCP/UDP/. What I care is to give the procotol information to the
user, to let him decide.
> (3) How do you handle race window between security_socket_accept()
> and sock->ops->accept()?
>
> Samir Bellabes wrote:
>> My approach is to get the informations regarding the socket from
>> socket(), bind() and and accept() syscalls hooks.
>> Pushing this informations to userspace. Here the user can refuse or
>> accept the sycalls.
>
> If a socket is shared by multiple processes and
> the user said "Accept this connection" at security_socket_accept(),
> the process does not always pick up what the user wanted to pick up
> because there is no mutex between security_socket_accept() and
> sock->ops->accept().
at security_socket_accept(), the user only accept the fact that the
application is able to go to sock->ops->accept(). That's the purpose of
this hook.
After, when packet are coming, we can catch them with
libnetfilter_queue, and deal with filtering packets.
> (4) If (3) is my misunderstanding (i.e. the user doesn't judge at security_socket_accept()),
> it means that the user judges it before select() says "ready to accept".
> Then ... what happens if a socket is shared by multiple programs?
> The user has to judge without knowing which program will pick up this connection.
> TOMOYO lets the user judge whether to accept or not
> with knowing the program's name who picks up this connection.
>
> I think getting information at socket(), bind() and accept() is helpful for detailed judgement,
> but I think it doesn't solve race problem as long as the user judges *before* sock->ops->accept().
here we agree. *but* in my module, the user don't judge before
sock->ops->accept(). He judges when packets are coming, throught the
libnetfilter_queue API, in userspace, and reinject packet if it's ok.
We are really trying to do the same thing, but I'm almost sure this can
be achieved with existing tool in fact, and put more "decision code" in
the userspace, and only keep a managing/detecting tool in the kernelspace.
regards,
sam
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context.
2007-11-30 14:29 ` Samir Bellabes
@ 2007-11-30 14:59 ` Tetsuo Handa
2007-11-30 16:07 ` Samir Bellabes
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2007-11-30 14:59 UTC (permalink / raw)
To: sam
Cc: jmorris, herbert, netdev, davem, linux-security-module, kaber,
netfilter-devel, sds
Hello.
Samir Bellabes wrote:
> at security_socket_accept(), the user only accept the fact that the
> application is able to go to sock->ops->accept(). That's the purpose of
> this hook.
Yes. This hook can't perform filtering.
> After, when packet are coming, we can catch them with
> libnetfilter_queue, and deal with filtering packets.
Is this performed inside sock->ops->accept()?
> here we agree. *but* in my module, the user don't judge before
> sock->ops->accept(). He judges when packets are coming, throught the
> libnetfilter_queue API, in userspace, and reinject packet if it's ok.
I didn't understand what is happening.
Is there a hook which can perform filtering inside sock->ops->accept()?
Regards.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context.
2007-11-30 14:59 ` Tetsuo Handa
@ 2007-11-30 16:07 ` Samir Bellabes
2007-12-01 3:48 ` Tetsuo Handa
0 siblings, 1 reply; 21+ messages in thread
From: Samir Bellabes @ 2007-11-30 16:07 UTC (permalink / raw)
To: Tetsuo Handa
Cc: jmorris, herbert, netdev, davem, linux-security-module, kaber,
netfilter-devel, sds
Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> writes:
> Hello.
>
> Samir Bellabes wrote:
>> at security_socket_accept(), the user only accept the fact that the
>> application is able to go to sock->ops->accept(). That's the purpose of
>> this hook.
> Yes. This hook can't perform filtering.
By "filtering", you should mean "packets filtring", shouldn't you ?
because this hook is able to deny the accept() syscall for a process, so
it's a kind of "filtring" too.
>> After, when packet are coming, we can catch them with
>> libnetfilter_queue, and deal with filtering packets.
> Is this performed inside sock->ops->accept()?
No, it's performed from the userspace. the goal is to don't touch the
network stack at all.
after accepting a successful security_socket_accept(), the processus
will wait for client. first packet will arrived. Here we do the
filtering, and give the ability to the user to deny packets.
Dropping the TCP SYN for a TCP connection, for example, then the
process don't see the connection arrived and was dropped.
Another important point which make me sure we should need to use the
libnetfilter_queue is that the we need to work with the conntrack tool
to automatically let some connections going throught the firewall.
One example, a ftp client will listen on a arbitrary port for the
incoming data channel. This job is already handle with the conntrack_ftp
extension by netfilter.
With your tool, you will have to learn the ftp client to let incoming
connection, reach this dynamic port, so does this mean, as it's dynamic,
to allow TCP port 1024-65535 ?
With the approach of using the libnetfilter_queue, we can first put the
network packet inside the conntrack, let the packets in the RELATED
state going trought the firewall and be ACCEPTED, and for others
packets, let the user decide.
>> here we agree. *but* in my module, the user don't judge before
>> sock->ops->accept(). He judges when packets are coming, throught the
>> libnetfilter_queue API, in userspace, and reinject packet if it's ok.
> I didn't understand what is happening.
> Is there a hook which can perform filtering inside sock->ops->accept()?
I'm not speaking anymore about socket filtering inside
sock->aps->accept(). I do filtering for sock->aps->accept() with the
netfilter tool, by handling packets directly.
So there's two thing, first dealing with socket filtering for socket(),
bind(), listen(), accept() (just to let application to have the right to
execute the syscall)
Then for managing the data inside the connection, I will use the packet
by packet way, with netfilter and libnetfilter_queue.
I really need time to made a real release, to show code. I will take it.
regards,
sam
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context.
2007-11-30 16:07 ` Samir Bellabes
@ 2007-12-01 3:48 ` Tetsuo Handa
2007-12-01 7:18 ` Samir Bellabes
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2007-12-01 3:48 UTC (permalink / raw)
To: sam
Cc: jmorris, herbert, netdev, davem, linux-security-module, kaber,
netfilter-devel, sds
Hello.
Thank you for detailed explanation.
Samir Bellabes wrote:
> By "filtering", you should mean "packets filtring", shouldn't you ?
> because this hook is able to deny the accept() syscall for a process, so
> it's a kind of "filtring" too.
Yes, you are right.
> No, it's performed from the userspace. the goal is to don't touch the
> network stack at all.
OK. One thing I'm worrying.
Use of userspace process assumes that it shall not be killed by some reason
(SIGKILL or OOM-killer or something).
What happens if userspace process is dead?
TOMOYO wants to continue packet filtering functionality even if userspace process is dead.
So, TOMOYO loads all filtering rules inside kernel and performs filtering inside kernel.
TOMOYO's goals are
(1) perform simple stateless packet filtering without any assistance outside the kernel
(2) provide simple approach for people (including me) who disable iptables
or worry the correctness of configurations because of very complicated syntax.
(Many users disable both iptables/SELinux inside local network because
their very-very-flexible-configurations give them a headache.)
(3) allow people describe per-an-application firewall rules in understandable syntax.
> Dropping the TCP SYN for a TCP connection, for example, then the
> process don't see the connection arrived and was dropped.
That's an advantage.
Making select() not to say "ready to accept" is what TOMOYO wants
(although it is very difficult to do so because of (3)).
> With your tool, you will have to learn the ftp client to let incoming
> connection, reach this dynamic port, so does this mean, as it's dynamic,
> to allow TCP port 1024-65535 ?
Yes. Users add lines like
<kernel> /usr/sbin/vsftpd
allow_network TCP accept xxx.xxx.xxx.xxx-yyy.yyy.yyy.yyy 1024-65535
which means "/usr/sbin/vsftpd is able to accept incoming TCP connections
if source IP address is between xxx.xxx.xxx.xxx-yyy.yyy.yyy.yyy and
source port is between 1024-65535" and
<kernel> ... /usr/bin/ftp
allow_network TCP accept zzz.zzz.zzz.zzz 1024-65535
which means "/usr/bin/ftp is able to accept incoming TCP connections if
source IP address is zzz.zzz.zzz.zzz and source port is between 1024-65535".
Users have to add explicitly, but it is very easy and understandable.
> I'm not speaking anymore about socket filtering inside
> sock->aps->accept(). I do filtering for sock->aps->accept() with the
> netfilter tool, by handling packets directly.
For TOMOYO, (3) is more important than making select() not to say "ready to accept".
Packet filtering without knowing the program's name who picks up this connection is not a TOMOYO's goal.
So, TOMOYO still wants to do filtering inside sock->aps->accept() or security_socket_post_accept().
Thank you.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context.
2007-12-01 3:48 ` Tetsuo Handa
@ 2007-12-01 7:18 ` Samir Bellabes
[not found] ` <200712011715.JFJ39500.QOJFStHFMOOLVF@I-love.SAKURA.ne.jp>
0 siblings, 1 reply; 21+ messages in thread
From: Samir Bellabes @ 2007-12-01 7:18 UTC (permalink / raw)
To: Tetsuo Handa
Cc: jmorris, herbert, netdev, davem, linux-security-module, kaber,
netfilter-devel, sds
Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> writes:
> Hello.
> Thank you for detailed explanation.
> Samir Bellabes wrote:
>
>> No, it's performed from the userspace. the goal is to don't touch the
>> network stack at all.
> OK. One thing I'm worrying.
> Use of userspace process assumes that it shall not be killed by some reason
> (SIGKILL or OOM-killer or something).
> What happens if userspace process is dead?
here is the way:
1. a syscall is executed.
2. we send the informations to userspace daemon, and we are waiting for
what I'm calling a "verdict" on it, there is of course a timer on
that.
3. userspace daemon asks user - or something that TOMOYO don't seems to
handle because it's living all in the kernel, a automatic decision
process, throught a database rules, a XML file rules, etc..
4. userspace daemon give its verdict.
5. kernel execute the verdict.
Now, what if the userspace daemon died. There's 2 possibilities for the
moment.
1/ apply a DEFAULT_POLICY after timer expires. this default verdict are
- DENY
- GRANT
- NOTIFY (in kernel log)
- DELAY (with a fixed limit time).
2/ something I'm currently designing, is to keep a cache of
rules/decision in the kernel part. The goal is that the system will
auto-learn about the decision of the user, and keep rules decisions
somewhere (database, XML file, ..). When starting, we put this rules
inside the kernel cache rules.
When a event occures (syscall):
- we are looking for a macthe in the cached rules.
- if we don't have a verdict in this cache, we are going to ask the
userspace part for a verdict in this case, and save the decision
for same matched conditions
When a new rules is applied, deleted or modified for userspace, we are
updating the kernel cache in real-time.
I want to have a tool nearly working as iptables for netfilter rules,
what I mean is that the "brain"/decision part is in the userspace, not
in the kernel.
there is also other reasons to that last point, because with all the
informations coming from the networking event like networking syscalls,
network packets, we can do more than a personnal security tool, we can
do with minimal cost:
- monitoring, just save the network events (already done)
- taking others "network decisions" : adding route inside the
network, auto-mount distant filesystem, starting distant services
(I have no shame to said that is currently a really big dream :)
> TOMOYO wants to continue packet filtering functionality even if userspace process is dead.
> So, TOMOYO loads all filtering rules inside kernel and performs
> filtering inside kernel.
Yes, as I said this is also something I want to do.
> TOMOYO's goals are
> (1) perform simple stateless packet filtering without any assistance
> outside the kernel
what differences between you approach and netfilter in this case ? if
it's about packet filtering, you already have all you wishes in
netfilter project.
> (2) provide simple approach for people (including me) who disable iptables
> or worry the correctness of configurations because of very complicated syntax.
> (Many users disable both iptables/SELinux inside local network because
> their very-very-flexible-configurations give them a headache.)
No. I'm sorry you can't disable iptables in your model.
security :
what about crafted/bad/bogus packets ? what about stealth scans ?
netfilter is here to protect from this.
usuability:
what about forwarding ?
My goal is that someone having a gateway and a LAN box, he can, by
starting a service which is listening, on the LAN box, automatically
have a forwarding rule added to the gateway, for its service, the time
the process service is living.
It's the same for someone executing a connect() from the LAN to a
outside web service, we are dectecting this syscall, a put a rule on the
gateway to authorize the outgoing forwarding
It's also true for the DMZ, ie start sshd, then we are detecting the
listen() in sshd process, and we are putting a forwarding rule on the
router.
Think about the target is projects like linux-embedded routers.
> (3) allow people describe per-an-application firewall rules in
> understandable syntax.
Yes, that was also my starting point too. helping basics users.
>> Dropping the TCP SYN for a TCP connection, for example, then the
>> process don't see the connection arrived and was dropped.
> That's an advantage.
> Making select() not to say "ready to accept" is what TOMOYO wants
> (although it is very difficult to do so because of (3)).
thanks.
>> With your tool, you will have to learn the ftp client to let incoming
>> connection, reach this dynamic port, so does this mean, as it's dynamic,
>> to allow TCP port 1024-65535 ?
>
> <kernel> ... /usr/bin/ftp
> allow_network TCP accept zzz.zzz.zzz.zzz 1024-65535
>
> which means "/usr/bin/ftp is able to accept incoming TCP connections if
> source IP address is zzz.zzz.zzz.zzz and source port is between 1024-65535".
> Users have to add explicitly, but it is very easy and understandable.
so we are losing in granularity against the netfilter conntrack in this
case, because the conntrack will get the exact listening port, and not
all the unprivileged port.
We have to use the conntrack tool, first to continue having all the
existing features (like transparently get a RELATED connection working),
second because the libnetfilter_queue is the place to deal with packets,
and yes, if you want to filtering packets, there only one possibility
for the moment, netfilter.
What is really important with this new way of filtering is the win
regarding the granularity. With netfiler it's basically : IP/port src,
IP/port dst, protocol.
My view is to always have 2 more dimensions : userid and process, and
never fall inside a less granular case.
>> I'm not speaking anymore about socket filtering inside
>> sock->aps->accept(). I do filtering for sock->aps->accept() with the
>> netfilter tool, by handling packets directly.
> For TOMOYO, (3) is more important than making select() not to say "ready to accept".
> Packet filtering without knowing the program's name who picks up this connection is not a TOMOYO's goal.
> So, TOMOYO still wants to do filtering inside sock->aps->accept() or security_socket_post_accept().
ok, same goals, differents ways.
thanks for this discussion, it's really interesting to share the same idea.
regards
sam
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process's security context.
2007-11-22 23:29 ` James Morris
2007-11-24 2:14 ` [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context Tetsuo Handa
@ 2007-12-03 7:58 ` Patrick McHardy
2007-12-03 13:17 ` [PATCH net-2.6.25] Add packet filtering based on process's securitycontext Tetsuo Handa
1 sibling, 1 reply; 21+ messages in thread
From: Patrick McHardy @ 2007-12-03 7:58 UTC (permalink / raw)
To: James Morris
Cc: Tetsuo Handa, Herbert Xu, netdev, David S. Miller,
linux-security-module, netfilter-devel, Stephen Smalley
James Morris wrote:
> On Thu, 22 Nov 2007, Tetsuo Handa wrote:
>
>> This patch allows LSM modules filter incoming connections/datagrams
>> based on the process's security context who is attempting to pick up.
>>
>> There are already hooks to filter incoming connections/datagrams
>> based on the socket's security context, but these hooks are not
>> applicable when one wants to do TCP Wrapper-like filtering
>> (e.g. App1 is permitted to accept TCP connections from 192.168.0.0/16).
>
> This functionality looks like it could be useful in that we currently have
> no direct security mapping from incoming packet to user process, but only
> to the receiving socket, as you mention. For SELinux, it may help us
> simplify/clarify policy.
>
> It's also been long-desired for netfilter/iptables, to allow ipt_owner to
> work cleanly for incoming packets.
>
> So, this probably needs to be implemented in a way which works for both LSM
> and netfilter. There have been several discussions on the issue from the
> netfilter side, although I don't know what the latest status of that is
> (I've expanded the cc list to hopefully get some more feedback).
No news on that. I'm also a bit sceptical if adding all this complexity
and overhead would really be worth it (considering only netfilter) just
to use the owner match and UID/GID matching. It wouldn't even be
accurate because there is not 1:1 mapping of sockets and processes.
I actually like Samir Bellabes' approach, which doesn't suffer from
these problems IIRC.
>>From memory, one approach under discussion was to add netfilter hooks to
> the transport layer, which could be invoked correctly by each type of
> protocol when the target process is selected.
We can only invoke the hooks after the socket lookup, but we don't
know which process is going to call recvmsg() for that socket.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process's securitycontext.
2007-12-03 7:58 ` [PATCH net-2.6.25] Add packet filtering based on process's security context Patrick McHardy
@ 2007-12-03 13:17 ` Tetsuo Handa
0 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2007-12-03 13:17 UTC (permalink / raw)
To: kaber, jmorris
Cc: herbert, netdev, davem, linux-security-module, netfilter-devel,
sds
Hello.
Patrick McHardy wrote:
> No news on that. I'm also a bit sceptical if adding all this complexity
> and overhead would really be worth it (considering only netfilter) just
> to use the owner match and UID/GID matching. It wouldn't even be
> accurate because there is not 1:1 mapping of sockets and processes.
Considering only LSM, socket_post_accept()/socket_post_recv_datagram() hooks are
not complicated at all.
A socket may be mapped to multiple processes, but at the moment of picking up
(i.e. accept()/recvmsg()), I think it is accurate 1:1 mapping.
I'm more interested in "Who picks this connection/datagram up?" than
"Which socket enqueues this connection/datagram?"
It may be indifferent for netfilter, but it is region of interest for me.
> I actually like Samir Bellabes' approach, which doesn't suffer from
> these problems IIRC.
Oh, I found him at http://nfws.inl.fr/en/?p=50 . (Sorry, I didn't know.)
He is the person who was discussing with me a few days ago.
> >>From memory, one approach under discussion was to add netfilter hooks to
> > the transport layer, which could be invoked correctly by each type of
> > protocol when the target process is selected.
>
> We can only invoke the hooks after the socket lookup, but we don't
> know which process is going to call recvmsg() for that socket.
Right. Thus, I'm proposing LSM hooks at accept()/recvmsg() time.
Regards.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context.
[not found] ` <200712042200.CDH65164.OOFSMFFLHJOtVQ@I-love.SAKURA.ne.jp>
@ 2007-12-09 8:06 ` Tetsuo Handa
2007-12-09 16:05 ` Samir Bellabes
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2007-12-09 8:06 UTC (permalink / raw)
To: sam
Cc: jmorris, herbert, netdev, davem, linux-security-module, kaber,
netfilter-devel, sds
Hello, Samir.
Did you receive the following messages?
Since these messages were dropped at vger.kernel.org ,
I'm worrying that you couldn't receive the following messages.
Tetsuo Handa wrote:
> Hello.
>
> Samir Bellabes wrote:
> > >> what differences between you approach and netfilter in this case ? if
> > >> it's about packet filtering, you already have all you wishes in
> > >> netfilter project.
> > > Except a hook for making decision with the name of process who picks that packet up known.
> >
> > I think that we really don't need it, because we can catch the
> > informations as I explained.
>
> Well, I haven't understood yet why we don't need it.
>
> How can you know the the name of process who copies that datagram to its userspace memory?
> A socket may be shared by multiple different executable files,
> so the name of the executable file is not known until
> one of processes who share the socket issues accept()/recvmsg() syscall.
>
> Are you saying that I should not use the name of the executable file?
>
> Regards.
>
Tetsuo Handa wrote:
> Hello.
>
> I made an example.
>
> Usage:
>
> Compile app1 and app2 and run /tmp/app1 .
>
> Run something like
> curl http://localhost:10000/
> to connect to /tmp/app1.
>
> I want to know that */tmp/app2* accepts TCP connection
> so that the user can control
> whether this TCP connection from 127.0.0.1 port N
> should be accepted by */tmp/app2* or not.
>
> How can we do this without socket_post_accept() change?
>
> Regards.
>
> ---------- app1.c start ----------
> /* gcc -Wall -O2 -o /tmp/app1 app1.c */
> #include <fcntl.h>
> #include <netinet/in.h>
> #include <stdio.h>
> #include <string.h>
> #include <sys/select.h>
> #include <sys/socket.h>
> #include <sys/types.h>
> #include <unistd.h>
>
> int main(int argc, char *argv[]) {
> const int fd = socket(PF_INET, SOCK_STREAM, 0);
> struct sockaddr_in addr;
> char buf[16];
> memset(&addr, 0, sizeof(addr));
> addr.sin_family = AF_INET;
> addr.sin_addr.s_addr = htonl(INADDR_ANY);
> addr.sin_port = htons(10000);
> fprintf(stderr, "%s started.\n", argv[0]);
> if (bind(fd, (struct sockaddr *) &addr, sizeof(addr))) {
> fprintf(stderr, "Can't bind()\n");
> return 1;
> } else if (listen(fd, 5)) {
> fprintf(stderr, "Can't listen()\n");
> return 1;
> }
> while (1) {
> fd_set rfds;
> FD_ZERO(&rfds);
> FD_SET(fd, &rfds);
> select(fd + 1, &rfds, NULL, NULL, NULL);
> if (FD_ISSET(fd, &rfds)) break;
> fprintf(stderr, "Can't select()\n");
> return 1;
> }
> if (fcntl(fd, FD_CLOEXEC, 0)) {
> fprintf(stderr, "Can't fcntl()\n");
> return 1;
> }
> snprintf(buf, sizeof(buf), "%d", fd);
> execlp("/tmp/app2", "app2", buf, NULL);
> fprintf(stderr, "Can't execve()\n");
> return 1;
> }
> ---------- app1.c end ----------
>
> ---------- app2.c start ----------
> /* gcc -Wall -O2 -o /tmp/app2 app2.c */
> #include <netinet/in.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <sys/socket.h>
> #include <sys/types.h>
> #include <unistd.h>
>
> int main(int argc, char *argv[]) {
> int lfd;
> if (argc != 2) {
> fprintf(stderr, "Bad parameter.\n");
> return 1;
> }
> fprintf(stderr, "%s started.\n", argv[0]);
> lfd = atoi(argv[1]);
> while (1) {
> struct sockaddr_in addr;
> socklen_t size = sizeof(addr);
> int fd = accept(lfd, (struct sockaddr *) &addr, &size);
> char c;
> if (fd == EOF) {
> fprintf(stderr, "Can't accept()\n");
> return 1;
> }
> while (read(fd, &c, 1) == 1 && write(fd, &c, 1) == 1);
> close(fd);
> }
> return 0;
> }
> ---------- app2.c end ----------
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context.
2007-12-09 8:06 ` Tetsuo Handa
@ 2007-12-09 16:05 ` Samir Bellabes
0 siblings, 0 replies; 21+ messages in thread
From: Samir Bellabes @ 2007-12-09 16:05 UTC (permalink / raw)
To: Tetsuo Handa
Cc: jmorris, herbert, netdev, davem, linux-security-module, kaber,
netfilter-devel, sds
Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> writes:
> Hello, Samir.
>
> Did you receive the following messages?
> Since these messages were dropped at vger.kernel.org ,
> I'm worrying that you couldn't receive the following messages.
Yes, I got it.
I will take time to investigate your example.
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH net-2.6.25] Add packet filtering based on process's security context.
@ 2007-12-31 6:21 Tetsuo Handa
0 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2007-12-31 6:21 UTC (permalink / raw)
To: netdev, netfilter-devel, linux-security-module; +Cc: sam
Hello.
This is a repost of a patch submitted at http://lkml.org/lkml/2007/11/19/545 .
Current status is that I'm waiting for Samir Bellabes's answer
whether we can handle the following example without this patch.
Tetsuo Handa wrote:
> Hello.
>
> Samir Bellabes wrote:
> > >> what differences between you approach and netfilter in this case ? if
> > >> it's about packet filtering, you already have all you wishes in
> > >> netfilter project.
> > > Except a hook for making decision with the name of process who picks that packet up known.
> >
> > I think that we really don't need it, because we can catch the
> > informations as I explained.
>
> Well, I haven't understood yet why we don't need it.
>
> How can you know the the name of process who copies that datagram to its userspace memory?
> A socket may be shared by multiple different executable files,
> so the name of the executable file is not known until
> one of processes who share the socket issues accept()/recvmsg() syscall.
>
> Are you saying that I should not use the name of the executable file?
>
> Regards.
>
Tetsuo Handa wrote:
> Hello.
>
> I made an example.
>
> Usage:
>
> Compile app1 and app2 and run /tmp/app1 .
>
> Run something like
> curl http://localhost:10000/
> to connect to /tmp/app1.
>
> I want to know that */tmp/app2* accepts TCP connection
> so that the user can control
> whether this TCP connection from 127.0.0.1 port N
> should be accepted by */tmp/app2* or not.
>
> How can we do this without socket_post_accept() change?
>
> Regards.
>
> ---------- app1.c start ----------
> /* gcc -Wall -O2 -o /tmp/app1 app1.c */
> #include <fcntl.h>
> #include <netinet/in.h>
> #include <stdio.h>
> #include <string.h>
> #include <sys/select.h>
> #include <sys/socket.h>
> #include <sys/types.h>
> #include <unistd.h>
>
> int main(int argc, char *argv[]) {
> const int fd = socket(PF_INET, SOCK_STREAM, 0);
> struct sockaddr_in addr;
> char buf[16];
> memset(&addr, 0, sizeof(addr));
> addr.sin_family = AF_INET;
> addr.sin_addr.s_addr = htonl(INADDR_ANY);
> addr.sin_port = htons(10000);
> fprintf(stderr, "%s started.\n", argv[0]);
> if (bind(fd, (struct sockaddr *) &addr, sizeof(addr))) {
> fprintf(stderr, "Can't bind()\n");
> return 1;
> } else if (listen(fd, 5)) {
> fprintf(stderr, "Can't listen()\n");
> return 1;
> }
> while (1) {
> fd_set rfds;
> FD_ZERO(&rfds);
> FD_SET(fd, &rfds);
> select(fd + 1, &rfds, NULL, NULL, NULL);
> if (FD_ISSET(fd, &rfds)) break;
> fprintf(stderr, "Can't select()\n");
> return 1;
> }
> if (fcntl(fd, FD_CLOEXEC, 0)) {
> fprintf(stderr, "Can't fcntl()\n");
> return 1;
> }
> snprintf(buf, sizeof(buf), "%d", fd);
> execlp("/tmp/app2", "app2", buf, NULL);
> fprintf(stderr, "Can't execve()\n");
> return 1;
> }
> ---------- app1.c end ----------
>
> ---------- app2.c start ----------
> /* gcc -Wall -O2 -o /tmp/app2 app2.c */
> #include <netinet/in.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <sys/socket.h>
> #include <sys/types.h>
> #include <unistd.h>
>
> int main(int argc, char *argv[]) {
> int lfd;
> if (argc != 2) {
> fprintf(stderr, "Bad parameter.\n");
> return 1;
> }
> fprintf(stderr, "%s started.\n", argv[0]);
> lfd = atoi(argv[1]);
> while (1) {
> struct sockaddr_in addr;
> socklen_t size = sizeof(addr);
> int fd = accept(lfd, (struct sockaddr *) &addr, &size);
> char c;
> if (fd == EOF) {
> fprintf(stderr, "Can't accept()\n");
> return 1;
> }
> while (read(fd, &c, 1) == 1 && write(fd, &c, 1) == 1);
> close(fd);
> }
> return 0;
> }
> ---------- app2.c end ----------
>
Regards.
----------
Subject: Add packet filtering based on process's security context.
This patch allows LSM modules filter incoming connections/datagrams
based on the process's security context who is attempting to pick up.
There are already hooks to filter incoming connections/datagrams
based on the socket's security context, but these hooks are not
applicable when one wants to do TCP Wrapper-like filtering
(e.g. App1 is permitted to accept TCP connections from 192.168.0.0/16).
Precautions: This approach has a side effect which unlikely occurs.
If a socket is shared by multiple processes with differnt policy,
the process who should be able to accept this connection
will not be able to accept this connection
because socket_post_accept() aborts this connection.
But if socket_post_accept() doesn't abort this connection,
the process who must not be able to accept this connection
will repeat accept() forever, which is a worse side effect.
Similarly, if a socket is shared by multiple processes with differnt policy,
the process who should be able to pick up this datagram
will not be able to pick up this datagram
because socket_post_recv_datagram() discards this datagram.
But if socket_post_recv_datagram() doesn't discard this datagram,
the process who must not be able to pick up this datagram
will repeat recvmsg() forever, which is a worse side effect.
So, don't give different permissions between processes who shares one socket.
Otherwise, some connections/datagrams cannot be delivered to intended process.
Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
include/linux/security.h | 34 +++++++++++++++++++++++++++++-----
net/core/datagram.c | 29 ++++++++++++++++++++++++++++-
net/socket.c | 7 +++++--
security/dummy.c | 13 ++++++++++---
security/security.c | 10 ++++++++--
5 files changed, 80 insertions(+), 13 deletions(-)
--- net-2.6.25.orig/include/linux/security.h
+++ net-2.6.25/include/linux/security.h
@@ -781,8 +781,12 @@ struct request_sock;
* @socket_post_accept:
* This hook allows a security module to copy security
* information into the newly created socket's inode.
+ * This hook also allows a security module to filter connections
+ * from unwanted peers based on the process accepting this connection.
+ * The connection will be aborted if this hook returns nonzero.
* @sock contains the listening socket structure.
* @newsock contains the newly created server socket for connection.
+ * Return 0 if permission is granted.
* @socket_sendmsg:
* Check permission before transmitting a message to another socket.
* @sock contains the socket structure.
@@ -796,6 +800,15 @@ struct request_sock;
* @size contains the size of message structure.
* @flags contains the operational flags.
* Return 0 if permission is granted.
+ * @socket_post_recv_datagram:
+ * Check permission after receiving a datagram.
+ * This hook allows a security module to filter packets
+ * from unwanted peers based on the process receiving this datagram.
+ * The packet will be discarded if this hook returns nonzero.
+ * @sk contains the socket.
+ * @skb contains the socket buffer.
+ * @flags contains the operational flags.
+ * Return 0 if permission is granted.
* @socket_getsockname:
* Check permission before the local address (name) of the socket object
* @sock is retrieved.
@@ -1387,12 +1400,13 @@ 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_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,
struct msghdr * msg, int size, int flags);
+ int (*socket_post_recv_datagram) (struct sock *sk, struct sk_buff *skb,
+ unsigned int flags);
int (*socket_getsockname) (struct socket * sock);
int (*socket_getpeername) (struct socket * sock);
int (*socket_getsockopt) (struct socket * sock, int level, int optname);
@@ -2297,10 +2311,12 @@ int security_socket_bind(struct socket *
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_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);
+int security_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags);
int security_socket_getsockname(struct socket *sock);
int security_socket_getpeername(struct socket *sock);
int security_socket_getsockopt(struct socket *sock, int level, int optname);
@@ -2376,9 +2392,10 @@ static inline int security_socket_accept
return 0;
}
-static inline void security_socket_post_accept(struct socket * sock,
- struct socket * newsock)
+static inline int security_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
{
+ return 0;
}
static inline int security_socket_sendmsg(struct socket * sock,
@@ -2394,6 +2411,13 @@ static inline int security_socket_recvms
return 0;
}
+static inline int security_socket_post_recv_datagram(struct sock *sk,
+ struct sk_buff *skb,
+ unsigned int flags)
+{
+ return 0;
+}
+
static inline int security_socket_getsockname(struct socket * sock)
{
return 0;
--- net-2.6.25.orig/net/socket.c
+++ net-2.6.25/net/socket.c
@@ -1454,13 +1454,16 @@ asmlinkage long sys_accept(int fd, struc
goto out_fd;
}
+ /* Filter connections from unwanted peers. */
+ err = security_socket_post_accept(sock, newsock);
+ if (err)
+ goto out_fd;
+
/* File flags are not inherited via accept() unlike another OSes. */
fd_install(newfd, newfile);
err = newfd;
- security_socket_post_accept(sock, newsock);
-
out_put:
fput_light(sock->file, fput_needed);
out:
--- net-2.6.25.orig/security/dummy.c
+++ net-2.6.25/security/dummy.c
@@ -748,10 +748,10 @@ static int dummy_socket_accept (struct s
return 0;
}
-static void dummy_socket_post_accept (struct socket *sock,
- struct socket *newsock)
+static int dummy_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
{
- return;
+ return 0;
}
static int dummy_socket_sendmsg (struct socket *sock, struct msghdr *msg,
@@ -766,6 +766,12 @@ static int dummy_socket_recvmsg (struct
return 0;
}
+static int dummy_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags)
+{
+ return 0;
+}
+
static int dummy_socket_getsockname (struct socket *sock)
{
return 0;
@@ -1099,6 +1105,7 @@ void security_fixup_ops (struct security
set_to_dummy_if_null(ops, socket_post_accept);
set_to_dummy_if_null(ops, socket_sendmsg);
set_to_dummy_if_null(ops, socket_recvmsg);
+ set_to_dummy_if_null(ops, socket_post_recv_datagram);
set_to_dummy_if_null(ops, socket_getsockname);
set_to_dummy_if_null(ops, socket_getpeername);
set_to_dummy_if_null(ops, socket_setsockopt);
--- net-2.6.25.orig/net/core/datagram.c
+++ net-2.6.25/net/core/datagram.c
@@ -55,6 +55,7 @@
#include <net/checksum.h>
#include <net/sock.h>
#include <net/tcp_states.h>
+#include <linux/security.h>
/*
* Is a socket 'connection oriented' ?
@@ -148,6 +149,7 @@ struct sk_buff *__skb_recv_datagram(stru
{
struct sk_buff *skb;
long timeo;
+ unsigned long cpu_flags;
/*
* Caller is allowed not to check sk->sk_err before skb_recv_datagram()
*/
@@ -165,7 +167,6 @@ struct sk_buff *__skb_recv_datagram(stru
* Look at current nfs client by the way...
* However, this function was corrent in any case. 8)
*/
- unsigned long cpu_flags;
spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
skb = skb_peek(&sk->sk_receive_queue);
@@ -179,6 +180,14 @@ struct sk_buff *__skb_recv_datagram(stru
}
spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
+ /* Filter packets from unwanted peers. */
+ if (skb) {
+ error = security_socket_post_recv_datagram(sk, skb,
+ flags);
+ if (error)
+ goto force_dequeue;
+ }
+
if (skb)
return skb;
@@ -191,6 +200,24 @@ struct sk_buff *__skb_recv_datagram(stru
return NULL;
+force_dequeue:
+ /* Drop this packet because LSM says "Don't pass it to the caller". */
+ if (!(flags & MSG_PEEK))
+ goto no_peek;
+ /*
+ * If this packet is MSG_PEEK'ed, dequeue it forcibly
+ * so that this packet won't prevent the caller from picking up
+ * next packet.
+ */
+ spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
+ if (skb == skb_peek(&sk->sk_receive_queue)) {
+ __skb_unlink(skb, &sk->sk_receive_queue);
+ atomic_dec(&skb->users);
+ /* Do I have something to do with skb->peeked ? */
+ }
+ spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
+no_peek:
+ kfree_skb(skb);
no_packet:
*err = error;
return NULL;
--- net-2.6.25.orig/security/security.c
+++ net-2.6.25/security/security.c
@@ -869,9 +869,9 @@ int security_socket_accept(struct socket
return security_ops->socket_accept(sock, newsock);
}
-void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+int security_socket_post_accept(struct socket *sock, struct socket *newsock)
{
- security_ops->socket_post_accept(sock, newsock);
+ return security_ops->socket_post_accept(sock, newsock);
}
int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
@@ -885,6 +885,12 @@ int security_socket_recvmsg(struct socke
return security_ops->socket_recvmsg(sock, msg, size, flags);
}
+int security_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags)
+{
+ return security_ops->socket_post_recv_datagram(sk, skb, flags);
+}
+
int security_socket_getsockname(struct socket *sock)
{
return security_ops->socket_getsockname(sock);
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH net-2.6.25] Add packet filtering based on process's security context.
@ 2008-01-22 15:16 Tetsuo Handa
2008-01-22 16:49 ` Casey Schaufler
2008-01-24 11:47 ` [PATCH net-2.6.25] Add packet filtering based on process's " Tetsuo Handa
0 siblings, 2 replies; 21+ messages in thread
From: Tetsuo Handa @ 2008-01-22 15:16 UTC (permalink / raw)
To: netdev, davem; +Cc: linux-security-module, netfilter-devel
This patch modifies security_socket_post_accept() and introduces
security_socket_post_recv_datagram() LSM hooks.
Currently, security_socket_post_accept() is called *after* fd_install()
at sys_accept(). This means that userland process might access accept()ed
but not yet properly labeled socket.
I believe this security_socket_post_accept() should be called *before*
fd_install() for the three reasons.
* The userland process will always access proper context labeled by
security_socket_post_accept() rather than default context labeled by
security_inode_alloc() (called from alloc_inode() from new_inode() from
sock_alloc() from sys_accept()). This gives a security module
a chance to perform access control based on proper context.
* If security_socket_post_accept() failed to assign proper context
(e.g. -ENOMEM), the accept()ed socket can't have proper context.
Use of void for security_socket_post_accept() deprives a security module
of a chance to abandon the accept()ed socket.
* A security module can perform connection filtering based on
process's security context.
If security_socket_post_accept() is called after fd_install(),
it is too late to do so because the accept()ed socket is already visible
to userland processes.
Currently, there is no way to directly map security context from incoming
packet to user process. This is because the creator or owner of a socket is
not always the receiver of an incoming packet. The userland process who
receives the incoming packet is not known until a process calls sys_recvmsg().
So, I want to add a LSM hook to give a security module a chance to control
after the recipient of the incoming packet is known.
Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
include/linux/security.h | 34 +++++++++++++++++++++++++++++-----
net/core/datagram.c | 29 ++++++++++++++++++++++++++++-
net/socket.c | 7 +++++--
security/dummy.c | 13 ++++++++++---
security/security.c | 10 ++++++++--
5 files changed, 80 insertions(+), 13 deletions(-)
--- net-2.6.25.orig/include/linux/security.h
+++ net-2.6.25/include/linux/security.h
@@ -781,8 +781,12 @@ struct request_sock;
* @socket_post_accept:
* This hook allows a security module to copy security
* information into the newly created socket's inode.
+ * This hook also allows a security module to filter connections
+ * from unwanted peers based on the process accepting this connection.
+ * The connection will be aborted if this hook returns nonzero.
* @sock contains the listening socket structure.
* @newsock contains the newly created server socket for connection.
+ * Return 0 if permission is granted.
* @socket_sendmsg:
* Check permission before transmitting a message to another socket.
* @sock contains the socket structure.
@@ -796,6 +800,15 @@ struct request_sock;
* @size contains the size of message structure.
* @flags contains the operational flags.
* Return 0 if permission is granted.
+ * @socket_post_recv_datagram:
+ * Check permission after receiving a datagram.
+ * This hook allows a security module to filter packets
+ * from unwanted peers based on the process receiving this datagram.
+ * The packet will be discarded if this hook returns nonzero.
+ * @sk contains the socket.
+ * @skb contains the socket buffer.
+ * @flags contains the operational flags.
+ * Return 0 if permission is granted.
* @socket_getsockname:
* Check permission before the local address (name) of the socket object
* @sock is retrieved.
@@ -1387,12 +1400,13 @@ 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_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,
struct msghdr * msg, int size, int flags);
+ int (*socket_post_recv_datagram) (struct sock *sk, struct sk_buff *skb,
+ unsigned int flags);
int (*socket_getsockname) (struct socket * sock);
int (*socket_getpeername) (struct socket * sock);
int (*socket_getsockopt) (struct socket * sock, int level, int optname);
@@ -2297,10 +2311,12 @@ int security_socket_bind(struct socket *
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_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);
+int security_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags);
int security_socket_getsockname(struct socket *sock);
int security_socket_getpeername(struct socket *sock);
int security_socket_getsockopt(struct socket *sock, int level, int optname);
@@ -2376,9 +2392,10 @@ static inline int security_socket_accept
return 0;
}
-static inline void security_socket_post_accept(struct socket * sock,
- struct socket * newsock)
+static inline int security_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
{
+ return 0;
}
static inline int security_socket_sendmsg(struct socket * sock,
@@ -2394,6 +2411,13 @@ static inline int security_socket_recvms
return 0;
}
+static inline int security_socket_post_recv_datagram(struct sock *sk,
+ struct sk_buff *skb,
+ unsigned int flags)
+{
+ return 0;
+}
+
static inline int security_socket_getsockname(struct socket * sock)
{
return 0;
--- net-2.6.25.orig/net/socket.c
+++ net-2.6.25/net/socket.c
@@ -1454,13 +1454,16 @@ asmlinkage long sys_accept(int fd, struc
goto out_fd;
}
+ /* Filter connections from unwanted peers. */
+ err = security_socket_post_accept(sock, newsock);
+ if (err)
+ goto out_fd;
+
/* File flags are not inherited via accept() unlike another OSes. */
fd_install(newfd, newfile);
err = newfd;
- security_socket_post_accept(sock, newsock);
-
out_put:
fput_light(sock->file, fput_needed);
out:
--- net-2.6.25.orig/security/dummy.c
+++ net-2.6.25/security/dummy.c
@@ -748,10 +748,10 @@ static int dummy_socket_accept (struct s
return 0;
}
-static void dummy_socket_post_accept (struct socket *sock,
- struct socket *newsock)
+static int dummy_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
{
- return;
+ return 0;
}
static int dummy_socket_sendmsg (struct socket *sock, struct msghdr *msg,
@@ -766,6 +766,12 @@ static int dummy_socket_recvmsg (struct
return 0;
}
+static int dummy_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags)
+{
+ return 0;
+}
+
static int dummy_socket_getsockname (struct socket *sock)
{
return 0;
@@ -1099,6 +1105,7 @@ void security_fixup_ops (struct security
set_to_dummy_if_null(ops, socket_post_accept);
set_to_dummy_if_null(ops, socket_sendmsg);
set_to_dummy_if_null(ops, socket_recvmsg);
+ set_to_dummy_if_null(ops, socket_post_recv_datagram);
set_to_dummy_if_null(ops, socket_getsockname);
set_to_dummy_if_null(ops, socket_getpeername);
set_to_dummy_if_null(ops, socket_setsockopt);
--- net-2.6.25.orig/net/core/datagram.c
+++ net-2.6.25/net/core/datagram.c
@@ -55,6 +55,7 @@
#include <net/checksum.h>
#include <net/sock.h>
#include <net/tcp_states.h>
+#include <linux/security.h>
/*
* Is a socket 'connection oriented' ?
@@ -148,6 +149,7 @@ struct sk_buff *__skb_recv_datagram(stru
{
struct sk_buff *skb;
long timeo;
+ unsigned long cpu_flags;
/*
* Caller is allowed not to check sk->sk_err before skb_recv_datagram()
*/
@@ -165,7 +167,6 @@ struct sk_buff *__skb_recv_datagram(stru
* Look at current nfs client by the way...
* However, this function was corrent in any case. 8)
*/
- unsigned long cpu_flags;
spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
skb = skb_peek(&sk->sk_receive_queue);
@@ -179,6 +180,14 @@ struct sk_buff *__skb_recv_datagram(stru
}
spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
+ /* Filter packets from unwanted peers. */
+ if (skb) {
+ error = security_socket_post_recv_datagram(sk, skb,
+ flags);
+ if (error)
+ goto force_dequeue;
+ }
+
if (skb)
return skb;
@@ -191,6 +200,24 @@ struct sk_buff *__skb_recv_datagram(stru
return NULL;
+force_dequeue:
+ /* Drop this packet because LSM says "Don't pass it to the caller". */
+ if (!(flags & MSG_PEEK))
+ goto no_peek;
+ /*
+ * If this packet is MSG_PEEK'ed, dequeue it forcibly
+ * so that this packet won't prevent the caller from picking up
+ * next packet.
+ */
+ spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
+ if (skb == skb_peek(&sk->sk_receive_queue)) {
+ __skb_unlink(skb, &sk->sk_receive_queue);
+ atomic_dec(&skb->users);
+ /* Do I have something to do with skb->peeked ? */
+ }
+ spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
+no_peek:
+ kfree_skb(skb);
no_packet:
*err = error;
return NULL;
--- net-2.6.25.orig/security/security.c
+++ net-2.6.25/security/security.c
@@ -869,9 +869,9 @@ int security_socket_accept(struct socket
return security_ops->socket_accept(sock, newsock);
}
-void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+int security_socket_post_accept(struct socket *sock, struct socket *newsock)
{
- security_ops->socket_post_accept(sock, newsock);
+ return security_ops->socket_post_accept(sock, newsock);
}
int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
@@ -885,6 +885,12 @@ int security_socket_recvmsg(struct socke
return security_ops->socket_recvmsg(sock, msg, size, flags);
}
+int security_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags)
+{
+ return security_ops->socket_post_recv_datagram(sk, skb, flags);
+}
+
int security_socket_getsockname(struct socket *sock)
{
return security_ops->socket_getsockname(sock);
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process's security context.
2008-01-22 15:16 Tetsuo Handa
@ 2008-01-22 16:49 ` Casey Schaufler
2008-01-23 1:26 ` [PATCH net-2.6.25] Add packet filtering based on process\'s " Tetsuo Handa
2008-01-24 11:47 ` [PATCH net-2.6.25] Add packet filtering based on process's " Tetsuo Handa
1 sibling, 1 reply; 21+ messages in thread
From: Casey Schaufler @ 2008-01-22 16:49 UTC (permalink / raw)
To: Tetsuo Handa, netdev, davem; +Cc: linux-security-module, netfilter-devel
--- Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:
> ...
>
> Currently, there is no way to directly map security context from incoming
> packet to user process. This is because the creator or owner of a socket is
> not always the receiver of an incoming packet. The userland process who
> receives the incoming packet is not known until a process calls
> sys_recvmsg().
> So, I want to add a LSM hook to give a security module a chance to control
> after the recipient of the incoming packet is known.
Do you have a real situation where two user processes with different
security contexts share a socket? How do you get into that situation,
and is it appropriate to have that situation in your security scheme?
Can this occur without using privilege?
Casey Schaufler
casey@schaufler-ca.com
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process\'s security context.
2008-01-22 16:49 ` Casey Schaufler
@ 2008-01-23 1:26 ` Tetsuo Handa
0 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2008-01-23 1:26 UTC (permalink / raw)
To: casey; +Cc: linux-security-module, netfilter-devel, netdev, davem
Hello.
Casey Schaufler wrote:
> Do you have a real situation where two user processes with different
> security contexts share a socket? How do you get into that situation,
> and is it appropriate to have that situation in your security scheme?
> Can this occur without using privilege?
I hope such situation won't occur, as I have mentioned in the previous
posting.
| Precautions: This approach has a side effect which unlikely occurs.
|
| If a socket is shared by multiple processes with differnt policy,
| the process who should be able to accept this connection
| will not be able to accept this connection
| because socket_post_accept() aborts this connection.
| But if socket_post_accept() doesn't abort this connection,
| the process who must not be able to accept this connection
| will repeat accept() forever, which is a worse side effect.
|
| Similarly, if a socket is shared by multiple processes with differnt policy,
| the process who should be able to pick up this datagram
| will not be able to pick up this datagram
| because socket_post_recv_datagram() discards this datagram.
| But if socket_post_recv_datagram() doesn't discard this datagram,
| the process who must not be able to pick up this datagram
| will repeat recvmsg() forever, which is a worse side effect.
|
| So, don't give different permissions between processes who shares one socket.
| Otherwise, some connections/datagrams cannot be delivered to intended process.
But it is possible to write a code like
---------- app3.c start ----------
/* gcc -Wall -O2 -o /tmp/app3 app3.c */
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
const int fd1 = socket(PF_INET, SOCK_DGRAM, 0), fd2 = socket(PF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr;
char buf[16];
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(10000);
fprintf(stderr, "%s started.\n", argv[0]);
if (bind(fd1, (struct sockaddr *) &addr, sizeof(addr))) {
fprintf(stderr, "Can't bind()\n");
return 1;
}
if (sendto(fd2, "hello\n", 6, 0, (struct sockaddr *) &addr, sizeof(addr)) != 6 ||
sendto(fd2, "world\n", 6, 0, (struct sockaddr *) &addr, sizeof(addr)) != 6) {
fprintf(stderr, "Can't sendto()\n");
return 1;
}
while (1) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd1, &rfds);
select(fd1 + 1, &rfds, NULL, NULL, NULL);
if (FD_ISSET(fd1, &rfds)) break;
fprintf(stderr, "Can't select()\n");
return 1;
}
if (fcntl(fd1, FD_CLOEXEC, 0)) {
fprintf(stderr, "Can't fcntl()\n");
return 1;
}
snprintf(buf, sizeof(buf), "%d", fd1);
execlp("/tmp/app4", "app4", buf, NULL);
fprintf(stderr, "Can't execve()\n");
return 1;
}
---------- app3.c end ----------
---------- app4.c start ----------
/* gcc -Wall -O2 -o /tmp/app4 app4.c */
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int fd;
if (argc != 2) {
fprintf(stderr, "Bad parameter.\n");
return 1;
}
fprintf(stderr, "%s started.\n", argv[0]);
fd = atoi(argv[1]);
while (1) {
struct sockaddr_in addr;
socklen_t size = sizeof(addr);
char buffer[1024];
int len;
len = recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &size);
if (len == EOF) {
fprintf(stderr, "Can't recvfrom()\n");
return 1;
}
write(1, buffer, len);
}
return 0;
}
---------- app4.c end ----------
and assign different policy to /tmp/app1 and /tmp/app2 .
Therefore, I want to check at sys_recvmsg() time.
(Usage: Compile app3 and app4 and run /tmp/app3 .)
For TCP's case, see http://www.mail-archive.com/linux-security-module@vger.kernel.org/msg02531.html
What I want to do is perform connection/packet filtering
with the recipient of the incoming connection/packet known.
My security scheme controls based on the recipient of the incoming connection/packet.
In this case, not /tmp/app1 or /tmp/app3 , but /tmp/app2 or /tmp/app4.
This case occurs without using privilege.
Regards.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process's security context.
2008-01-22 15:16 Tetsuo Handa
2008-01-22 16:49 ` Casey Schaufler
@ 2008-01-24 11:47 ` Tetsuo Handa
2008-01-24 15:03 ` Paul Moore
1 sibling, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2008-01-24 11:47 UTC (permalink / raw)
To: netdev, davem; +Cc: linux-security-module, netfilter-devel
Hello.
Are there any remaining questions/problems about this patch?
If none, I want this patch applied to net-2.6.25 tree.
Regards.
---------------
This patch modifies security_socket_post_accept() and introduces
security_socket_post_recv_datagram() LSM hooks.
Currently, security_socket_post_accept() is called *after* fd_install()
at sys_accept(). This means that userland process might access accept()ed
but not yet properly labeled socket.
I believe this security_socket_post_accept() should be called *before*
fd_install() for the three reasons.
* The userland process will always access proper context labeled by
security_socket_post_accept() rather than default context labeled by
security_inode_alloc() (called from alloc_inode() from new_inode() from
sock_alloc() from sys_accept()). This gives a security module
a chance to perform access control based on proper context.
* If security_socket_post_accept() failed to assign proper context
(e.g. -ENOMEM), the accept()ed socket can't have proper context.
Use of void for security_socket_post_accept() deprives a security module
of a chance to abandon the accept()ed socket.
* A security module can perform connection filtering based on
process's security context.
If security_socket_post_accept() is called after fd_install(),
it is too late to do so because the accept()ed socket is already visible
to userland processes.
Currently, there is no way to directly map security context from incoming
packet to user process. This is because the creator or owner of a socket is
not always the receiver of an incoming packet. The userland process who
receives the incoming packet is not known until a process calls sys_recvmsg().
So, I want to add a LSM hook to give a security module a chance to control
after the recipient of the incoming packet is known.
Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
include/linux/security.h | 34 +++++++++++++++++++++++++++++-----
net/core/datagram.c | 29 ++++++++++++++++++++++++++++-
net/socket.c | 7 +++++--
security/dummy.c | 13 ++++++++++---
security/security.c | 10 ++++++++--
5 files changed, 80 insertions(+), 13 deletions(-)
--- net-2.6.25.orig/include/linux/security.h
+++ net-2.6.25/include/linux/security.h
@@ -781,8 +781,12 @@ struct request_sock;
* @socket_post_accept:
* This hook allows a security module to copy security
* information into the newly created socket's inode.
+ * This hook also allows a security module to filter connections
+ * from unwanted peers based on the process accepting this connection.
+ * The connection will be aborted if this hook returns nonzero.
* @sock contains the listening socket structure.
* @newsock contains the newly created server socket for connection.
+ * Return 0 if permission is granted.
* @socket_sendmsg:
* Check permission before transmitting a message to another socket.
* @sock contains the socket structure.
@@ -796,6 +800,15 @@ struct request_sock;
* @size contains the size of message structure.
* @flags contains the operational flags.
* Return 0 if permission is granted.
+ * @socket_post_recv_datagram:
+ * Check permission after receiving a datagram.
+ * This hook allows a security module to filter packets
+ * from unwanted peers based on the process receiving this datagram.
+ * The packet will be discarded if this hook returns nonzero.
+ * @sk contains the socket.
+ * @skb contains the socket buffer.
+ * @flags contains the operational flags.
+ * Return 0 if permission is granted.
* @socket_getsockname:
* Check permission before the local address (name) of the socket object
* @sock is retrieved.
@@ -1387,12 +1400,13 @@ 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_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,
struct msghdr * msg, int size, int flags);
+ int (*socket_post_recv_datagram) (struct sock *sk, struct sk_buff *skb,
+ unsigned int flags);
int (*socket_getsockname) (struct socket * sock);
int (*socket_getpeername) (struct socket * sock);
int (*socket_getsockopt) (struct socket * sock, int level, int optname);
@@ -2297,10 +2311,12 @@ int security_socket_bind(struct socket *
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_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);
+int security_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags);
int security_socket_getsockname(struct socket *sock);
int security_socket_getpeername(struct socket *sock);
int security_socket_getsockopt(struct socket *sock, int level, int optname);
@@ -2376,9 +2392,10 @@ static inline int security_socket_accept
return 0;
}
-static inline void security_socket_post_accept(struct socket * sock,
- struct socket * newsock)
+static inline int security_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
{
+ return 0;
}
static inline int security_socket_sendmsg(struct socket * sock,
@@ -2394,6 +2411,13 @@ static inline int security_socket_recvms
return 0;
}
+static inline int security_socket_post_recv_datagram(struct sock *sk,
+ struct sk_buff *skb,
+ unsigned int flags)
+{
+ return 0;
+}
+
static inline int security_socket_getsockname(struct socket * sock)
{
return 0;
--- net-2.6.25.orig/net/socket.c
+++ net-2.6.25/net/socket.c
@@ -1454,13 +1454,16 @@ asmlinkage long sys_accept(int fd, struc
goto out_fd;
}
+ /* Filter connections from unwanted peers. */
+ err = security_socket_post_accept(sock, newsock);
+ if (err)
+ goto out_fd;
+
/* File flags are not inherited via accept() unlike another OSes. */
fd_install(newfd, newfile);
err = newfd;
- security_socket_post_accept(sock, newsock);
-
out_put:
fput_light(sock->file, fput_needed);
out:
--- net-2.6.25.orig/security/dummy.c
+++ net-2.6.25/security/dummy.c
@@ -748,10 +748,10 @@ static int dummy_socket_accept (struct s
return 0;
}
-static void dummy_socket_post_accept (struct socket *sock,
- struct socket *newsock)
+static int dummy_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
{
- return;
+ return 0;
}
static int dummy_socket_sendmsg (struct socket *sock, struct msghdr *msg,
@@ -766,6 +766,12 @@ static int dummy_socket_recvmsg (struct
return 0;
}
+static int dummy_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags)
+{
+ return 0;
+}
+
static int dummy_socket_getsockname (struct socket *sock)
{
return 0;
@@ -1099,6 +1105,7 @@ void security_fixup_ops (struct security
set_to_dummy_if_null(ops, socket_post_accept);
set_to_dummy_if_null(ops, socket_sendmsg);
set_to_dummy_if_null(ops, socket_recvmsg);
+ set_to_dummy_if_null(ops, socket_post_recv_datagram);
set_to_dummy_if_null(ops, socket_getsockname);
set_to_dummy_if_null(ops, socket_getpeername);
set_to_dummy_if_null(ops, socket_setsockopt);
--- net-2.6.25.orig/net/core/datagram.c
+++ net-2.6.25/net/core/datagram.c
@@ -55,6 +55,7 @@
#include <net/checksum.h>
#include <net/sock.h>
#include <net/tcp_states.h>
+#include <linux/security.h>
/*
* Is a socket 'connection oriented' ?
@@ -148,6 +149,7 @@ struct sk_buff *__skb_recv_datagram(stru
{
struct sk_buff *skb;
long timeo;
+ unsigned long cpu_flags;
/*
* Caller is allowed not to check sk->sk_err before skb_recv_datagram()
*/
@@ -165,7 +167,6 @@ struct sk_buff *__skb_recv_datagram(stru
* Look at current nfs client by the way...
* However, this function was corrent in any case. 8)
*/
- unsigned long cpu_flags;
spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
skb = skb_peek(&sk->sk_receive_queue);
@@ -179,6 +180,14 @@ struct sk_buff *__skb_recv_datagram(stru
}
spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
+ /* Filter packets from unwanted peers. */
+ if (skb) {
+ error = security_socket_post_recv_datagram(sk, skb,
+ flags);
+ if (error)
+ goto force_dequeue;
+ }
+
if (skb)
return skb;
@@ -191,6 +200,24 @@ struct sk_buff *__skb_recv_datagram(stru
return NULL;
+force_dequeue:
+ /* Drop this packet because LSM says "Don't pass it to the caller". */
+ if (!(flags & MSG_PEEK))
+ goto no_peek;
+ /*
+ * If this packet is MSG_PEEK'ed, dequeue it forcibly
+ * so that this packet won't prevent the caller from picking up
+ * next packet.
+ */
+ spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
+ if (skb == skb_peek(&sk->sk_receive_queue)) {
+ __skb_unlink(skb, &sk->sk_receive_queue);
+ atomic_dec(&skb->users);
+ /* Do I have something to do with skb->peeked ? */
+ }
+ spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
+no_peek:
+ kfree_skb(skb);
no_packet:
*err = error;
return NULL;
--- net-2.6.25.orig/security/security.c
+++ net-2.6.25/security/security.c
@@ -869,9 +869,9 @@ int security_socket_accept(struct socket
return security_ops->socket_accept(sock, newsock);
}
-void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+int security_socket_post_accept(struct socket *sock, struct socket *newsock)
{
- security_ops->socket_post_accept(sock, newsock);
+ return security_ops->socket_post_accept(sock, newsock);
}
int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
@@ -885,6 +885,12 @@ int security_socket_recvmsg(struct socke
return security_ops->socket_recvmsg(sock, msg, size, flags);
}
+int security_socket_post_recv_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags)
+{
+ return security_ops->socket_post_recv_datagram(sk, skb, flags);
+}
+
int security_socket_getsockname(struct socket *sock)
{
return security_ops->socket_getsockname(sock);
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-2.6.25] Add packet filtering based on process's security context.
2008-01-24 11:47 ` [PATCH net-2.6.25] Add packet filtering based on process's " Tetsuo Handa
@ 2008-01-24 15:03 ` Paul Moore
0 siblings, 0 replies; 21+ messages in thread
From: Paul Moore @ 2008-01-24 15:03 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: netdev, davem, linux-security-module, netfilter-devel
On Thursday 24 January 2008 6:47:55 am Tetsuo Handa wrote:
> Are there any remaining questions/problems about this patch?
> If none, I want this patch applied to net-2.6.25 tree.
Hello,
Taking into consideration that there are no current in-tree users of
this patch and the only known user of this functionality is TOMOYO,
which is still dealing with some unresolved VFS issues, I suggest not
merging this patch at the current time. My recommendation is to
continue to work on resolving the VFS issues (which it appears you are
working on) and then submitting all of the required TOMOYO changes at
once.
As a general rule, removing functionality from the kernel tends to be
much more difficult then adding it.
--
paul moore
linux security @ hp
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2008-01-24 15:03 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-11-21 12:30 [PATCH] Add packet filtering based on process's security context Tetsuo Handa
2007-11-22 13:35 ` [PATCH net-2.6.25] " Tetsuo Handa
2007-11-22 23:29 ` James Morris
2007-11-24 2:14 ` [PATCH net-2.6.25] Add packet filtering based on process'ssecurity context Tetsuo Handa
2007-11-29 4:57 ` Samir Bellabes
2007-11-30 14:07 ` Tetsuo Handa
2007-11-30 14:29 ` Samir Bellabes
2007-11-30 14:59 ` Tetsuo Handa
2007-11-30 16:07 ` Samir Bellabes
2007-12-01 3:48 ` Tetsuo Handa
2007-12-01 7:18 ` Samir Bellabes
[not found] ` <200712011715.JFJ39500.QOJFStHFMOOLVF@I-love.SAKURA.ne.jp>
[not found] ` <200712021224.CBH90604.HMtJFFOOFLQVSO@I-love.SAKURA.ne.jp>
[not found] ` <m28x4a1vy8.fsf@synack.fr>
[not found] ` <200712042200.CDH65164.OOFSMFFLHJOtVQ@I-love.SAKURA.ne.jp>
2007-12-09 8:06 ` Tetsuo Handa
2007-12-09 16:05 ` Samir Bellabes
2007-12-03 7:58 ` [PATCH net-2.6.25] Add packet filtering based on process's security context Patrick McHardy
2007-12-03 13:17 ` [PATCH net-2.6.25] Add packet filtering based on process's securitycontext Tetsuo Handa
-- strict thread matches above, loose matches on Subject: below --
2007-12-31 6:21 [PATCH net-2.6.25] Add packet filtering based on process's security context Tetsuo Handa
2008-01-22 15:16 Tetsuo Handa
2008-01-22 16:49 ` Casey Schaufler
2008-01-23 1:26 ` [PATCH net-2.6.25] Add packet filtering based on process\'s " Tetsuo Handa
2008-01-24 11:47 ` [PATCH net-2.6.25] Add packet filtering based on process's " Tetsuo Handa
2008-01-24 15:03 ` Paul Moore
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).