All of lore.kernel.org
 help / color / mirror / Atom feed
From: Samir Bellabes <sam@synack.fr>
To: linux-security-module@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
	netfilter-devel@vger.kernel.org, jamal <hadi@cyberus.ca>,
	Patrick McHardy <kaber@trash.net>,
	Evgeniy Polyakov <zbr@ioremap.net>,
	Neil Horman <nhorman@tuxdriver.com>,
	Grzegorz Nosek <root@localdomain.pl>,
	Samir Bellabes <sam@synack.fr>
Subject: [RFC v2 09/10] snet: introduce snet_ticket
Date: Tue,  2 Mar 2010 21:23:13 +0100	[thread overview]
Message-ID: <1267561394-13626-10-git-send-email-sam@synack.fr> (raw)
In-Reply-To: <1267561394-13626-1-git-send-email-sam@synack.fr>

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

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

generic informations:
  timeout
  syscall
  protocol
  verdict

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

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

there are 3 modes:

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

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

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

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

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


  parent reply	other threads:[~2010-03-02 20:30 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-03-02 20:23 [RFC v2 00/10] snet: Security for NETwork syscalls Samir Bellabes
2010-03-02 20:23 ` [RFC v2 01/10] lsm: add security_socket_closed() Samir Bellabes
2010-03-02 20:23 ` [RFC v2 02/10] Revert "lsm: Remove the socket_post_accept() hook" Samir Bellabes
2010-03-02 20:23 ` [RFC v2 03/10] snet: introduce security/snet, Makefile and Kconfig changes Samir Bellabes
2010-03-03  0:03   ` Greg KH
2010-03-03  0:23     ` Samir Bellabes
2010-03-02 20:23 ` [RFC v2 04/10] snet: introduce snet_core Samir Bellabes
2010-03-02 20:23 ` [RFC v2 05/10] snet: introduce snet_event Samir Bellabes
2010-03-02 20:23 ` [RFC v2 06/10] snet: introduce snet_hooks Samir Bellabes
2010-03-02 20:23 ` [RFC v2 07/10] snet: introduce snet_netlink Samir Bellabes
2010-03-02 20:23 ` [RFC v2 08/10] snet: introduce snet_verdict Samir Bellabes
2010-03-02 20:23 ` Samir Bellabes [this message]
2010-03-02 20:23 ` [RFC v2 10/10] snet: introduce snet_utils Samir Bellabes
2010-03-03 17:55   ` Jan Engelhardt
2010-03-06 12:41     ` Samir Bellabes
2010-03-03  1:56 ` [RFC v2 00/10] snet: Security for NETwork syscalls Tetsuo Handa
2010-03-06 18:16   ` Samir Bellabes
2010-03-06 18:17   ` Samir Bellabes
2010-03-06 18:20   ` Samir Bellabes
2010-03-06 18:40   ` Samir Bellabes
2010-03-07  5:47     ` Tetsuo Handa
2010-03-06 18:47   ` Samir Bellabes
2010-03-07  5:45     ` Tetsuo Handa
2010-03-15 16:43       ` Samir Bellabes
2010-03-06 18:50   ` Samir Bellabes

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1267561394-13626-10-git-send-email-sam@synack.fr \
    --to=sam@synack.fr \
    --cc=hadi@cyberus.ca \
    --cc=kaber@trash.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=nhorman@tuxdriver.com \
    --cc=root@localdomain.pl \
    --cc=zbr@ioremap.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.