All of lore.kernel.org
 help / color / mirror / Atom feed
From: Olaf Dietsche <olaf+list.linux-kernel@olafdietsche.de>
To: linux-kernel@vger.kernel.org
Cc: Rod Cordova <rcordova@ethernet.org>
Subject: Re: [PATCH] 2.6.38: access permission filesystem 0.24
Date: Sun, 10 Apr 2011 23:50:06 +0200	[thread overview]
Message-ID: <87sjtpai9d.fsf@rat.lan> (raw)
In-Reply-To: Pine.LNX.4.64.1103291321440.32194@fatboy.ethernet.org

[-- Attachment #1: Type: text/plain, Size: 1293 bytes --]

This *untested* patch adds a new permission managing file system.
Furthermore, it adds two modules, which make use of this file system.

One module allows granting capabilities based on user-/groupid. The
second module allows to grant access to lower numbered ports based on
user-/groupid, too.

Changes:
- updated to 2.6.38

This patch is available at:
<http://www.olafdietsche.de/linux/accessfs/>

and attached inline below.

Regards, Olaf

 Documentation/filesystems/accessfs.txt |   41 +++
 fs/Kconfig                             |    1 +
 fs/Makefile                            |    1 +
 fs/accessfs/Kconfig                    |   63 +++++
 fs/accessfs/Makefile                   |   11 +
 fs/accessfs/capabilities.c             |  108 ++++++++
 fs/accessfs/inode.c                    |  432 ++++++++++++++++++++++++++++++++
 fs/accessfs/ip.c                       |  101 ++++++++
 include/linux/accessfs_fs.h            |   42 +++
 include/net/sock.h                     |   43 ++++
 net/Kconfig                            |   12 +
 net/Makefile                           |    1 +
 net/hooks.c                            |   55 ++++
 net/ipv4/af_inet.c                     |    2 +-
 net/ipv6/af_inet6.c                    |    2 +-
 15 files changed, 913 insertions(+), 2 deletions(-)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: access permission filesystem --]
[-- Type: text/x-diff, Size: 27242 bytes --]

diff --git a/Documentation/filesystems/accessfs.txt b/Documentation/filesystems/accessfs.txt
new file mode 100644
index 0000000..bf135b5
--- /dev/null
+++ b/Documentation/filesystems/accessfs.txt
@@ -0,0 +1,41 @@
+Accessfs is a permission managing filesystem. It allows to control access to
+system resources, based on file permissions. The recommended mount point for
+this file-system is /proc/access, which will appear automatically in the
+/proc filesystem.
+
+Currently there are two modules using accessfs, userports and usercaps.
+
+With userports, you will be able to control access to IP ports based
+on user-/groupid.
+
+There's no need anymore to run internet daemons as root. You can
+individually configure which user/program can bind to protected ports
+(by default, below 1024).
+
+For example, you can say, user www is allowed to bind to port 80 or
+user mail is allowed to bind to port 25. Then, you can run apache as
+user www and sendmail as user mail. Now, you don't have to rely on
+apache or sendmail giving up superuser rights to enhance security.
+
+To use this option, you need to mount the access file system
+and do a chown on the appropriate ports:
+
+# mount -t accessfs none /proc/access
+# chown www /proc/access/net/ip/bind/80
+# chown mail /proc/access/net/ip/bind/25
+
+You can grant access to a group for individual ports as well. Just say:
+
+# chgrp lp /proc/access/net/ip/bind/515
+# chown g+x /proc/access/net/ip/bind/515
+
+With usercaps, you will be able to grant capabilities based on
+user-/groupid (root by default).
+
+For example you can create a group raw and change the capability
+net_raw to this group:
+
+# chgrp raw /proc/access/capabilities/net_raw
+# chmod ug+x /proc/access/capabilities/net_raw
+# chgrp raw /sbin/ping
+# chmod u-s /sbin/ping; chmod g+s /sbin/ping
diff --git a/fs/Kconfig b/fs/Kconfig
index 3db9caa..595f6c6 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -190,6 +190,7 @@ source "fs/romfs/Kconfig"
 source "fs/sysv/Kconfig"
 source "fs/ufs/Kconfig"
 source "fs/exofs/Kconfig"
+source "fs/accessfs/Kconfig"
 
 endif # MISC_FILESYSTEMS
 
diff --git a/fs/Makefile b/fs/Makefile
index a7f7cef..281f68e 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -120,4 +120,5 @@ obj-$(CONFIG_OCFS2_FS)		+= ocfs2/
 obj-$(CONFIG_BTRFS_FS)		+= btrfs/
 obj-$(CONFIG_GFS2_FS)           += gfs2/
 obj-$(CONFIG_EXOFS_FS)          += exofs/
+obj-$(CONFIG_ACCESS_FS)		+= accessfs/
 obj-$(CONFIG_CEPH_FS)		+= ceph/
diff --git a/fs/accessfs/Kconfig b/fs/accessfs/Kconfig
new file mode 100644
index 0000000..9e56180
--- /dev/null
+++ b/fs/accessfs/Kconfig
@@ -0,0 +1,63 @@
+config ACCESS_FS
+	tristate "Accessfs support (Experimental)"
+	depends on EXPERIMENTAL
+	default n
+	help
+	  This is a new file system to manage permissions. It is not very
+	  useful on its own. You need to enable other options below.
+
+	  If you're unsure, say N.
+
+config ACCESSFS_USER_PORTS
+	tristate "User permission based IP ports"
+	depends on ACCESS_FS
+	select NET_HOOKS
+	default n
+	help
+	  If you say Y here, you will be able to control access to IP ports
+	  based on user-/groupid. For this to work, you must say Y
+	  to CONFIG_NET_HOOKS.
+
+	  If you're unsure, say N.
+
+config ACCESSFS_PROT_SOCK
+	int "Range of protected ports (1024-65536)"
+	depends on ACCESSFS_USER_PORTS
+	default 1024
+	help
+	  Here you can extend the range of protected ports. This is
+	  from 1-1023 inclusive on normal unix systems. One use for this
+	  could be to reserve ports for X11 (port 6000) or database
+	  servers (port 3306 for mysql), so nobody else could grab this port.
+	  The default permission for extended ports is --x--x--x.
+
+	  If you build this as a module, you can specify the range of
+	  protected ports at module load time (max_prot_sock).
+
+	  If you're unsure, say 1024.
+
+config ACCESSFS_IGNORE_NET_BIND_SERVICE
+	bool "Ignore CAP_NET_BIND_SERVICE capability"
+	depends on ACCESSFS_USER_PORTS
+	default n
+	help
+	  This option lets you decide, wether a user with
+	  CAP_NET_BIND_SERVICE capability is able to override
+	  your userport configuration.
+
+	  If you build this as a module, you can specify this
+	  option at module load time (ignore_net_bind_service).
+
+	  If you're unsure, say n.
+
+config ACCESSFS_USER_CAPABILITIES
+	tristate "User permission based capabilities"
+	depends on ACCESS_FS
+	select SECURITY
+	default n
+	help
+	  If you say Y here, you will be able to grant capabilities based on
+	  user-/groupid (root by default). For this to work, you must say M or
+	  N to CONFIG_SECURITY_CAPABILITIES.
+
+	  If you're unsure, say N.
diff --git a/fs/accessfs/Makefile b/fs/accessfs/Makefile
new file mode 100644
index 0000000..63a5647
--- /dev/null
+++ b/fs/accessfs/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the linux accessfs routines.
+#
+
+obj-$(CONFIG_ACCESS_FS) += accessfs.o
+obj-$(CONFIG_ACCESSFS_USER_CAPABILITIES) += usercaps.o
+obj-$(CONFIG_ACCESSFS_USER_PORTS) += userports.o
+
+accessfs-objs := inode.o
+usercaps-objs := capabilities.o
+userports-objs := ip.o
diff --git a/fs/accessfs/capabilities.c b/fs/accessfs/capabilities.c
new file mode 100644
index 0000000..8c6f6ef
--- /dev/null
+++ b/fs/accessfs/capabilities.c
@@ -0,0 +1,108 @@
+/* Copyright (c) 2002-2006 Olaf Dietsche
+ *
+ * User based capabilities for Linux.
+ */
+
+#include <linux/accessfs_fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/security.h>
+
+/* perl -n -e 'print "\"", lc($1), "\",\n" if (m/^#define\s+CAP_(.+?)\s+\d+$/);' include/linux/capability.h */
+static const char *names[] = {
+	"chown",
+	"dac_override",
+	"dac_read_search",
+	"fowner",
+	"fsetid",
+	"kill",
+	"setgid",
+	"setuid",
+	"setpcap",
+	"linux_immutable",
+	"net_bind_service",
+	"net_broadcast",
+	"net_admin",
+	"net_raw",
+	"ipc_lock",
+	"ipc_owner",
+	"sys_module",
+	"sys_rawio",
+	"sys_chroot",
+	"sys_ptrace",
+	"sys_pacct",
+	"sys_admin",
+	"sys_boot",
+	"sys_nice",
+	"sys_resource",
+	"sys_time",
+	"sys_tty_config",
+	"mknod",
+	"lease",
+	"audit_write",
+	"audit_control",
+	"setfcap",
+	"mac_override",
+	"mac_admin",
+	"syslog",
+};
+
+static struct access_attr caps[ARRAY_SIZE(names)];
+
+static int accessfs_capable(struct task_struct *tsk, const struct cred *cred, int cap, int audit)
+{
+	if (accessfs_permitted(&caps[cap], MAY_EXEC)) {
+		/* capability granted */
+		return 0;
+	}
+
+	/* capability denied */
+	return -EPERM;
+}
+
+static struct security_operations accessfs_security_ops = {
+	.name = "usercaps",
+	.capable =	accessfs_capable,
+};
+
+static void unregister_capabilities(struct accessfs_direntry *dir, int n)
+{
+	int	i;
+	for (i = 0; i < n; ++i)
+		accessfs_unregister(dir, names[i]);
+}
+
+static int __init init_capabilities(void)
+{
+	struct accessfs_direntry *dir;
+	int i, err;
+	dir = accessfs_make_dirpath("capabilities");
+	if (dir == 0)
+		return -ENOTDIR;
+
+	for (i = 0; i < ARRAY_SIZE(caps); ++i) {
+		caps[i].uid = 0;
+		caps[i].gid = 0;
+		caps[i].mode = S_IXUSR;
+		err = accessfs_register(dir, names[i], &caps[i]);
+		if (err) {
+			unregister_capabilities(dir, i);
+			return err;
+		}
+	}
+
+	if (!security_module_enable(&accessfs_security_ops))
+		return -EAGAIN;
+
+	err = register_security(&accessfs_security_ops);
+	if (err != 0)
+		unregister_capabilities(dir, ARRAY_SIZE(names));
+
+	return err;
+}
+
+security_initcall(init_capabilities);
+
+MODULE_AUTHOR("Olaf Dietsche");
+MODULE_DESCRIPTION("User based capabilities");
+MODULE_LICENSE("GPL v2");
diff --git a/fs/accessfs/inode.c b/fs/accessfs/inode.c
new file mode 100644
index 0000000..832867af
--- /dev/null
+++ b/fs/accessfs/inode.c
@@ -0,0 +1,432 @@
+/* Copyright (c) 2001-2006 Olaf Dietsche
+ *
+ * Access permission filesystem for Linux.
+ *
+ * 2002 Ben Clifford, create mount point at /proc/access
+ * 2002 Ben Clifford, trying to make it work under 2.5.5-dj2
+ *          (see comments: BENC255 for reminders and todos)
+ *
+ *
+ * BENC255: the kernel doesn't lock BKL for us when entering methods
+ *          (see Documentation/fs/porting.txt)
+ *          Need to look at code here and see if we need either the BKL
+ *          or our own lock - I think probably not.
+ *
+ */
+
+#include <linux/accessfs_fs.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <asm/statfs.h>
+#include <asm/uaccess.h>
+
+#define ACCESSFS_MAGIC	0x3c1d36e7
+
+static struct proc_dir_entry *mountdir = NULL;
+
+static DEFINE_MUTEX(accessfs_sem);
+
+static struct inode_operations accessfs_inode_operations;
+static struct file_operations accessfs_dir_file_operations;
+static struct inode_operations accessfs_dir_inode_operations;
+
+static inline void accessfs_readdir_aux(struct file *filp,
+					struct accessfs_direntry *dir,
+					int start, void *dirent,
+					filldir_t filldir)
+{
+	struct list_head *list;
+	int i = 2;
+	list_for_each(list, &dir->children) {
+		struct accessfs_entry *de;
+		if (i++ < start)
+			continue;
+
+		de = list_entry(list, struct accessfs_entry, siblings);
+		if (filldir(dirent, de->name, strlen(de->name), filp->f_pos,
+			    de->ino, DT_UNKNOWN) < 0)
+			break;
+
+		++filp->f_pos;
+	}
+}
+
+static int accessfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	int i;
+	struct dentry *dentry = filp->f_dentry;
+	struct accessfs_direntry *dir;
+
+	i = filp->f_pos;
+	switch (i) {
+	case 0:
+		if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino,
+			    DT_DIR) < 0)
+			break;
+
+		++i;
+		++filp->f_pos;
+		/* NO break; */
+	case 1:
+		if (filldir(dirent, "..", 2, i,
+			    dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
+			break;
+
+		++i;
+		++filp->f_pos;
+		/* NO break; */
+	default:
+		mutex_lock(&accessfs_sem);
+		dir = dentry->d_inode->i_private;
+		accessfs_readdir_aux(filp, dir, i, dirent, filldir);
+		mutex_unlock(&accessfs_sem);
+		break;
+	}
+
+	return 0;
+}
+
+static struct accessfs_entry *accessfs_lookup_entry(struct accessfs_entry *pe,
+						    const char *name, int len)
+{
+	struct list_head *list;
+	struct accessfs_direntry *dir;
+	if (!S_ISDIR(pe->attr->mode))
+		return NULL;
+
+	dir = (struct accessfs_direntry *) pe;
+	list_for_each(list, &dir->children) {
+		struct accessfs_entry *de = list_entry(list, struct accessfs_entry, siblings);
+		if (strncmp(de->name, name, len) == 0 && de->name[len] == 0)
+			return de;
+	}
+
+	return NULL;
+}
+
+static struct accessfs_direntry	accessfs_rootdir = {
+	{ "/",
+	  LIST_HEAD_INIT(accessfs_rootdir.node.hash),
+	  LIST_HEAD_INIT(accessfs_rootdir.node.siblings),
+	  1, &accessfs_rootdir.attr },
+	NULL, LIST_HEAD_INIT(accessfs_rootdir.children),
+	{ 0, 0, S_IFDIR | 0755 }
+};
+
+static void accessfs_init_inode(struct inode *inode, struct accessfs_entry *pe)
+{
+	static const struct timespec epoch = {0, 0};
+	inode->i_private = pe;
+	inode->i_uid = pe->attr->uid;
+	inode->i_gid = pe->attr->gid;
+	inode->i_mode = pe->attr->mode;
+/*
+	inode->i_blksize = PAGE_CACHE_SIZE;
+	inode->i_blocks = 0;
+	inode->i_rdev = NODEV;
+*/
+	inode->i_atime = inode->i_mtime = inode->i_ctime = epoch;
+	switch (inode->i_mode & S_IFMT) {
+	case S_IFREG:
+		inode->i_op = &accessfs_inode_operations;
+		break;
+	case S_IFDIR:
+		inode->i_op = &accessfs_dir_inode_operations;
+		inode->i_fop = &accessfs_dir_file_operations;
+		break;
+	default:
+		BUG();
+		break;
+	}
+}
+
+static struct inode *accessfs_get_root_inode(struct super_block *sb)
+{
+	struct inode *inode = new_inode(sb);
+	if (inode) {
+		mutex_lock(&accessfs_sem);
+/* 		inode->i_ino = accessfs_rootdir.node.ino; */
+		accessfs_init_inode(inode, &accessfs_rootdir.node);
+		accessfs_rootdir.node.ino = inode->i_ino;
+		mutex_unlock(&accessfs_sem);
+	}
+
+	return inode;
+}
+
+static LIST_HEAD(hash);
+
+static int accessfs_node_init(struct accessfs_direntry *parent,
+			      struct accessfs_entry *de, const char *name,
+			      size_t len, struct access_attr *attr, mode_t mode)
+{
+	static unsigned long ino = 1;
+	de->name = kmalloc(len + 1, GFP_KERNEL);
+	if (de->name == NULL)
+		return -ENOMEM;
+
+	strncpy(de->name, name, len);
+	de->name[len] = 0;
+	de->ino = ++ino;
+	de->attr = attr;
+	de->attr->uid = 0;
+	de->attr->gid = 0;
+	de->attr->mode = mode;
+
+	list_add_tail(&de->hash, &hash);
+	list_add_tail(&de->siblings, &parent->children);
+	return 0;
+}
+
+static int accessfs_mknod(struct accessfs_direntry *dir, const char *name,
+			  struct access_attr *attr)
+{
+	struct accessfs_entry *pe;
+	pe = kmalloc(sizeof(struct accessfs_entry), GFP_KERNEL);
+	if (pe == NULL)
+		return -ENOMEM;
+
+	accessfs_node_init(dir, pe, name, strlen(name), attr,
+			   S_IFREG | attr->mode);
+	return 0;
+}
+
+static struct accessfs_direntry	*accessfs_mkdir(struct accessfs_direntry *parent,
+						const char *name, size_t len)
+{
+	int err;
+	struct accessfs_direntry *dir;
+	dir = kmalloc(sizeof(struct accessfs_direntry), GFP_KERNEL);
+	if (dir == NULL)
+		return NULL;
+
+	dir->parent = parent;
+	INIT_LIST_HEAD(&dir->children);
+	err = accessfs_node_init(parent, &dir->node, name, len, &dir->attr,
+				 S_IFDIR | 0755);
+	if (err) {
+		kfree(dir);
+		dir = 0;
+	}
+
+	return dir;
+}
+
+struct accessfs_direntry *accessfs_make_dirpath(const char *name)
+{
+	struct accessfs_direntry *dir = &accessfs_rootdir;
+	const char *slash;
+	mutex_lock(&accessfs_sem);
+	do {
+		struct accessfs_entry *de;
+		size_t len;
+		while (*name == '/')
+			++name;
+
+		slash = strchr(name, '/');
+		len = slash ? slash - name : strlen(name);
+		de = accessfs_lookup_entry(&dir->node, name, len);
+		if (de == NULL) {
+			dir = accessfs_mkdir(dir, name, len);
+		} else if (S_ISDIR(de->attr->mode)) {
+			dir = (struct accessfs_direntry *) de;
+		} else {
+			dir = NULL;
+		}
+
+		if (dir == NULL)
+			break;
+
+		name = slash  + 1;
+	} while (slash != NULL);
+
+	mutex_unlock(&accessfs_sem);
+	return dir;
+}
+
+static void accessfs_unlink(struct accessfs_entry *pe)
+{
+	list_del_init(&pe->hash);
+	list_del_init(&pe->siblings);
+	kfree(pe->name);
+	kfree(pe);
+}
+
+static int accessfs_notify_change(struct dentry *dentry, struct iattr *iattr)
+{
+	struct accessfs_entry *pe;
+	struct inode *i = dentry->d_inode;
+	int err;
+	err = inode_change_ok(i, iattr);
+	if (err)
+		return err;
+
+	setattr_copy(i, iattr);
+
+	pe = (struct accessfs_entry *) i->i_private;
+	pe->attr->uid = i->i_uid;
+	pe->attr->gid = i->i_gid;
+	pe->attr->mode = i->i_mode;
+	return 0;
+}
+
+static struct inode *accessfs_iget(struct super_block *sb, unsigned long ino)
+{
+	struct list_head *list;
+	struct inode *inode = iget_locked(sb, ino);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	if (!(inode->i_state & I_NEW))
+		return inode;
+
+	mutex_lock(&accessfs_sem);
+	list_for_each(list, &hash) {
+		struct accessfs_entry *pe;
+		pe = list_entry(list, struct accessfs_entry, hash);
+		if (pe->ino == ino) {
+			accessfs_init_inode(inode, pe);
+			break;
+		}
+	}
+
+	mutex_unlock(&accessfs_sem);
+	return inode;
+}
+
+static struct dentry *accessfs_lookup(struct inode *dir, struct dentry *dentry,
+				      struct nameidata *nd)
+{
+	struct inode *inode = NULL;
+	struct accessfs_entry *pe;
+	mutex_lock(&accessfs_sem);
+	pe = accessfs_lookup_entry(dir->i_private, dentry->d_name.name,
+				   dentry->d_name.len);
+	mutex_unlock(&accessfs_sem);
+	if (pe)
+		inode = accessfs_iget(dir->i_sb, pe->ino);
+
+	d_add(dentry, inode);
+	return NULL;
+}
+
+static struct inode_operations accessfs_inode_operations = {
+	.setattr =	accessfs_notify_change,
+};
+
+static struct inode_operations accessfs_dir_inode_operations = {
+	.lookup =	accessfs_lookup,
+	.setattr =	accessfs_notify_change,
+};
+
+static struct file_operations accessfs_dir_file_operations = {
+	.readdir =	accessfs_readdir,
+};
+
+static struct super_operations accessfs_ops = {
+	.statfs =	simple_statfs,
+};
+
+static int accessfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct inode *inode;
+	struct dentry *root;
+
+	sb->s_blocksize = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+	sb->s_magic = ACCESSFS_MAGIC;
+	sb->s_op = &accessfs_ops;
+	inode = accessfs_get_root_inode(sb);
+	if (!inode)
+		return -ENOMEM;
+
+	root = d_alloc_root(inode);
+	if (!root) {
+		iput(inode);
+		return -ENOMEM;
+	}
+
+	sb->s_root = root;
+	return 0;
+}
+
+static int accessfs_get_sb(struct file_system_type *fs_type,
+			   int flags, const char *dev_name,
+			   void *data, struct vfsmount *mnt)
+{
+	return get_sb_single(fs_type, flags, data, accessfs_fill_super, mnt);
+}
+
+int accessfs_permitted(struct access_attr *p, int mask)
+{
+	mode_t mode = p->mode;
+	if (current_fsuid() == p->uid)
+		mode >>= 6;
+	else if (in_group_p(p->gid))
+		mode >>= 3;
+
+	return (mode & mask) == mask;
+}
+
+int accessfs_register(struct accessfs_direntry *dir, const char *name,
+		      struct access_attr *attr)
+{
+	int err;
+	if (dir == 0)
+		return -EINVAL;
+
+	mutex_lock(&accessfs_sem);
+	err = accessfs_mknod(dir, name, attr);
+	mutex_unlock(&accessfs_sem);
+	return err;
+}
+
+void accessfs_unregister(struct accessfs_direntry *dir, const char *name)
+{
+	struct accessfs_entry *pe;
+	mutex_lock(&accessfs_sem);
+	pe = accessfs_lookup_entry(&dir->node, name, strlen(name));
+	if (pe)
+		accessfs_unlink(pe);
+
+	mutex_unlock(&accessfs_sem);
+}
+
+static struct file_system_type accessfs_fs_type = {
+	.owner =	THIS_MODULE,
+	.name =		"accessfs",
+	.get_sb =	accessfs_get_sb,
+	.kill_sb =	kill_anon_super,
+};
+
+static int __init init_accessfs_fs(void)
+{
+
+	/* create mount point for accessfs */
+	mountdir = proc_mkdir("access", NULL);
+	return register_filesystem(&accessfs_fs_type);
+}
+
+static void __exit exit_accessfs_fs(void)
+{
+	unregister_filesystem(&accessfs_fs_type);
+	remove_proc_entry("access", NULL);
+}
+
+module_init(init_accessfs_fs)
+module_exit(exit_accessfs_fs)
+
+MODULE_AUTHOR("Olaf Dietsche");
+MODULE_DESCRIPTION("Access Filesystem");
+MODULE_LICENSE("GPL v2");
+
+EXPORT_SYMBOL(accessfs_permitted);
+EXPORT_SYMBOL(accessfs_make_dirpath);
+EXPORT_SYMBOL(accessfs_register);
+EXPORT_SYMBOL(accessfs_unregister);
diff --git a/fs/accessfs/ip.c b/fs/accessfs/ip.c
new file mode 100644
index 0000000..bddd2f0
--- /dev/null
+++ b/fs/accessfs/ip.c
@@ -0,0 +1,101 @@
+/* Copyright (c) 2002-2006 Olaf Dietsche
+ *
+ * User permission based port access for Linux.
+ */
+
+#include <linux/accessfs_fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <net/sock.h>
+
+static int max_prot_sock = CONFIG_ACCESSFS_PROT_SOCK;
+#ifndef CONFIG_ACCESSFS_IGNORE_NET_BIND_SERVICE
+#define CONFIG_ACCESSFS_IGNORE_NET_BIND_SERVICE 0
+#endif
+static int ignore_net_bind_service = CONFIG_ACCESSFS_IGNORE_NET_BIND_SERVICE;
+static struct access_attr *bind_to_port;
+
+static int accessfs_ip_prot_sock(struct socket *sock,
+				 struct sockaddr *uaddr, int addr_len)
+{
+	struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
+	unsigned short snum = ntohs(addr->sin_port);
+	if (snum && snum < max_prot_sock
+	    && !accessfs_permitted(&bind_to_port[snum], MAY_EXEC)
+	    && (ignore_net_bind_service || !capable(CAP_NET_BIND_SERVICE)))
+		return -EACCES;
+
+	return 0;
+}
+
+static int accessfs_ip6_prot_sock(struct socket *sock,
+				  struct sockaddr *uaddr, int addr_len)
+{
+	struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
+	unsigned short snum = ntohs(addr->sin6_port);
+	if (snum && snum < max_prot_sock
+	    && !accessfs_permitted(&bind_to_port[snum], MAY_EXEC)
+	    && !capable(CAP_NET_BIND_SERVICE))
+		return -EACCES;
+
+	return 0;
+}
+
+static struct net_hook_operations ip_net_ops = {
+	.ip_prot_sock =	accessfs_ip_prot_sock,
+	.ip6_prot_sock =	accessfs_ip6_prot_sock,
+};
+
+static int __init init_ip(void)
+{
+	struct accessfs_direntry *dir = accessfs_make_dirpath("net/ip/bind");
+	int i;
+
+	if (max_prot_sock < PROT_SOCK)
+		max_prot_sock = PROT_SOCK;
+	else if (max_prot_sock > 65536)
+		max_prot_sock = 65536;
+
+	bind_to_port = kmalloc(max_prot_sock * sizeof(*bind_to_port),
+			       GFP_KERNEL);
+	if (bind_to_port == 0)
+		return -ENOMEM;
+
+	for (i = 1; i < max_prot_sock; ++i) {
+		char	buf[sizeof("65536")];
+		bind_to_port[i].uid = 0;
+		bind_to_port[i].gid = 0;
+		bind_to_port[i].mode = i < PROT_SOCK ? S_IXUSR : S_IXUGO;
+		sprintf(buf, "%d", i);
+		accessfs_register(dir, buf, &bind_to_port[i]);
+	}
+
+	net_hooks_register(&ip_net_ops);
+	return 0;
+}
+
+static void __exit exit_ip(void)
+{
+	struct accessfs_direntry *dir = accessfs_make_dirpath("net/ip/bind");
+	int i;
+	net_hooks_unregister(&ip_net_ops);
+	for (i = 1; i < max_prot_sock; ++i) {
+		char	buf[sizeof("65536")];
+		sprintf(buf, "%d", i);
+		accessfs_unregister(dir, buf);
+	}
+
+	if (bind_to_port != NULL)
+		kfree(bind_to_port);
+}
+
+module_init(init_ip)
+module_exit(exit_ip)
+
+MODULE_AUTHOR("Olaf Dietsche");
+MODULE_DESCRIPTION("User based IP ports permission");
+MODULE_LICENSE("GPL v2");
+module_param(max_prot_sock, int, 0444);
+MODULE_PARM_DESC(max_prot_sock, "Number of protected ports");
+module_param(ignore_net_bind_service, bool, 0644);
+MODULE_PARM_DESC(ignore_net_bind_service, "Ignore CAP_NET_BIND_SERVICE capability");
diff --git a/include/linux/accessfs_fs.h b/include/linux/accessfs_fs.h
new file mode 100644
index 0000000..ecd914e
--- /dev/null
+++ b/include/linux/accessfs_fs.h
@@ -0,0 +1,42 @@
+/* -*- mode: c -*- */
+#ifndef __accessfs_fs_h_included__
+#define __accessfs_fs_h_included__	1
+
+/* Copyright (c) 2001 Olaf Dietsche
+ *
+ * Access permission filesystem for Linux.
+ */
+
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <net/sock.h>
+
+struct access_attr {
+	uid_t	uid;
+	gid_t	gid;
+	mode_t	mode;
+};
+
+struct accessfs_entry {
+	char	*name;
+	struct list_head	hash;
+	struct list_head	siblings;
+	ino_t	ino;
+	struct access_attr	*attr;
+};
+
+struct accessfs_direntry {
+	struct accessfs_entry	node;
+	struct accessfs_direntry	*parent;
+	struct list_head	children;
+	struct access_attr	attr;
+};
+
+extern int accessfs_permitted(struct access_attr *p, int mask);
+extern struct accessfs_direntry *accessfs_make_dirpath(const char *name);
+extern int accessfs_register(struct accessfs_direntry *dir, const char *name, struct access_attr *attr);
+extern void accessfs_unregister(struct accessfs_direntry *dir, const char *name);
+
+#endif
diff --git a/include/net/sock.h b/include/net/sock.h
index bc1cf7d8..a92c2c7 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1794,4 +1794,47 @@ extern int sysctl_optmem_max;
 extern __u32 sysctl_wmem_default;
 extern __u32 sysctl_rmem_default;
 
+/* Networking hooks */
+extern int default_ip_prot_sock(struct socket *sock, struct sockaddr *uaddr,
+				int addr_len);
+extern int default_ip6_prot_sock(struct socket *sock, struct sockaddr *uaddr,
+				 int addr_len);
+#ifdef	CONFIG_NET_HOOKS
+struct net_hook_operations {
+	int	(*ip_prot_sock)(struct socket *sock,
+				struct sockaddr *uaddr, int addr_len);
+	int	(*ip6_prot_sock)(struct socket *sock,
+				 struct sockaddr *uaddr, int addr_len);
+};
+
+extern struct net_hook_operations	*net_ops;
+
+extern void net_hooks_register(struct net_hook_operations *ops);
+extern void net_hooks_unregister(struct net_hook_operations *ops);
+
+static inline int ip_prot_sock(struct socket *sock, struct sockaddr *uaddr,
+			       int addr_len)
+{
+	return net_ops->ip_prot_sock(sock, uaddr, addr_len);
+}
+
+static inline int ip6_prot_sock(struct socket *sock, struct sockaddr *uaddr,
+				int addr_len)
+{
+	return net_ops->ip6_prot_sock(sock, uaddr, addr_len);
+}
+#else
+static inline int ip_prot_sock(struct socket *sock, struct sockaddr *uaddr,
+			       int addr_len)
+{
+	return default_ip_prot_sock(sock, uaddr, addr_len);
+}
+
+static inline int ip6_prot_sock(struct socket *sock, struct sockaddr *uaddr,
+				int addr_len)
+{
+	return default_ip6_prot_sock(sock, uaddr, addr_len);
+}
+#endif
+
 #endif	/* _SOCK_H */
diff --git a/net/Kconfig b/net/Kconfig
index 7284062..4136811 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -75,6 +75,18 @@ config INET
 if INET
 source "net/ipv4/Kconfig"
 source "net/ipv6/Kconfig"
+
+config NET_HOOKS
+	bool "IP: Networking hooks (Experimental)"
+	depends on INET && EXPERIMENTAL
+	default n
+	help
+	  This option enables other kernel parts or modules to hook into the
+	  networking area and provide fine grained control over the access to
+	  IP ports.
+
+	  If you're unsure, say N.
+
 source "net/netlabel/Kconfig"
 
 endif # if INET
diff --git a/net/Makefile b/net/Makefile
index a51d946..4199f6b 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -60,6 +60,7 @@ ifneq ($(CONFIG_DCB),)
 obj-y				+= dcb/
 endif
 obj-$(CONFIG_IEEE802154)	+= ieee802154/
+obj-$(CONFIG_NET_HOOKS)		+= hooks.o
 
 ifeq ($(CONFIG_NET),y)
 obj-$(CONFIG_SYSCTL)		+= sysctl_net.o
diff --git a/net/hooks.c b/net/hooks.c
new file mode 100644
index 0000000..33100e6
--- /dev/null
+++ b/net/hooks.c
@@ -0,0 +1,55 @@
+/* Copyright (c) 2002 Olaf Dietsche
+ *
+ * Networking hooks. Currently for IPv4 and IPv6 only.
+ */
+
+#include <linux/module.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <net/sock.h>
+
+int default_ip_prot_sock(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
+	unsigned short snum = ntohs(addr->sin_port);
+	if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+		return -EACCES;
+
+	return 0;
+}
+
+int default_ip6_prot_sock(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
+	unsigned short snum = ntohs(addr->sin6_port);
+	if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+		return -EACCES;
+
+	return 0;
+}
+
+EXPORT_SYMBOL(default_ip_prot_sock);
+EXPORT_SYMBOL(default_ip6_prot_sock);
+
+#ifdef CONFIG_NET_HOOKS
+static struct net_hook_operations default_net_ops = {
+	.ip_prot_sock =	default_ip_prot_sock,
+	.ip6_prot_sock =	default_ip6_prot_sock,
+};
+
+struct net_hook_operations *net_ops = &default_net_ops;
+
+void net_hooks_register(struct net_hook_operations *ops)
+{
+	net_ops = ops;
+}
+
+void net_hooks_unregister(struct net_hook_operations *ops)
+{
+	net_ops = &default_net_ops;
+}
+
+EXPORT_SYMBOL(net_ops);
+EXPORT_SYMBOL(net_hooks_register);
+EXPORT_SYMBOL(net_hooks_unregister);
+#endif
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 45b89d7..bdda2a9 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -484,7 +484,7 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 
 	snum = ntohs(addr->sin_port);
 	err = -EACCES;
-	if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+	if (ip_prot_sock(sock, uaddr, addr_len))
 		goto out;
 
 	/*      We keep a pair of addresses. rcv_saddr is the one
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 978e80e..2465070 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -277,7 +277,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 		return -EINVAL;
 
 	snum = ntohs(addr->sin6_port);
-	if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+	if (ip6_prot_sock(sock, uaddr, addr_len))
 		return -EACCES;
 
 	lock_sock(sk);

       reply	other threads:[~2011-04-10 22:21 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <Pine.LNX.4.64.1103291321440.32194@fatboy.ethernet.org>
2011-04-10 21:50 ` Olaf Dietsche [this message]
2011-04-11  2:03   ` [PATCH] 2.6.38: access permission filesystem 0.24 Randy Dunlap
2011-04-11 20:47     ` [PATCH] 2.6.38: access permission filesystem 0.25 Olaf Dietsche
2011-04-24 19:09       ` Randy Dunlap
2011-04-26 11:22         ` Olaf Dietsche

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=87sjtpai9d.fsf@rat.lan \
    --to=olaf+list.linux-kernel@olafdietsche.de \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rcordova@ethernet.org \
    /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.