* [PATCH] 2.6.15: access permission filesystem 0.17 @ 2006-01-14 21:06 Olaf Dietsche 2006-01-14 22:45 ` Ingo Oeser 2006-01-23 11:57 ` Rolf Eike Beer 0 siblings, 2 replies; 19+ messages in thread From: Olaf Dietsche @ 2006-01-14 21:06 UTC (permalink / raw) To: linux-kernel [-- Attachment #1: Type: text/plain, Size: 404 bytes --] This 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.15 This patch is available at: <http://www.olafdietsche.de/linux/accessfs/> Regards, Olaf. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: Access permission filesystem --] [-- Type: text/x-patch, Size: 25292 bytes --] diff -urN a/fs/Kconfig b/fs/Kconfig --- a/fs/Kconfig Wed Jan 4 22:01:06 2006 +++ b/fs/Kconfig Sun Jan 8 15:04:18 2006 @@ -1209,6 +1209,7 @@ It's currently broken, so for now: answer N. +source "fs/accessfs/Kconfig" config SYSV_FS diff -urN a/fs/Makefile b/fs/Makefile --- a/fs/Makefile Wed Jan 4 22:01:06 2006 +++ b/fs/Makefile Sun Jan 8 15:04:18 2006 @@ -101,3 +101,4 @@ obj-$(CONFIG_HOSTFS) += hostfs/ obj-$(CONFIG_HPPFS) += hppfs/ obj-$(CONFIG_DEBUG_FS) += debugfs/ +obj-$(CONFIG_ACCESS_FS) += accessfs/ diff -urN a/fs/accessfs/Kconfig b/fs/accessfs/Kconfig --- a/fs/accessfs/Kconfig Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/Kconfig Sun Jan 8 15:04:18 2006 @@ -0,0 +1,82 @@ +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. + + The recommended mount point for this file-system is /proc/access, + which will appear automatically in the /proc filesystem. + + 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. + + With this option, 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 + + 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_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. + + 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 + + If you're unsure, say N. + diff -urN a/fs/accessfs/Makefile b/fs/accessfs/Makefile --- a/fs/accessfs/Makefile Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/Makefile Sun Jan 8 15:04:18 2006 @@ -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 -urN a/fs/accessfs/capabilities.c b/fs/accessfs/capabilities.c --- a/fs/accessfs/capabilities.c Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/capabilities.c Sun Jan 8 15:04:18 2006 @@ -0,0 +1,107 @@ +/* Copyright (c) 2002 Olaf Dietsche + * + * User based capabilities for Linux. + */ + +#include <linux/accessfs_fs.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/security.h> + +static struct access_attr caps[29]; +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" +}; + +static int accessfs_capable(struct task_struct *tsk, int cap) +{ + if (accessfs_permitted(&caps[cap], MAY_EXEC)) { + /* capability granted */ + tsk->flags |= PF_SUPERPRIV; + return 0; + } + + /* capability denied */ + return -EPERM; +} + +static struct security_operations accessfs_security_ops = { + .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 < sizeof(caps) / sizeof(caps[0]); ++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; + } + } + + err = register_security(&accessfs_security_ops); + if (err != 0) + unregister_capabilities(dir, sizeof(names) / sizeof(names[0])); + + return err; +} + +static void __exit exit_capabilities(void) +{ + struct accessfs_direntry *dir; + dir = accessfs_make_dirpath("capabilities"); + unregister_security(&accessfs_security_ops); + unregister_capabilities(dir, sizeof(names) / sizeof(names[0])); +} + +module_init(init_capabilities) +module_exit(exit_capabilities) + +MODULE_AUTHOR("Olaf Dietsche"); +MODULE_DESCRIPTION("User based capabilities"); +MODULE_LICENSE("GPL"); diff -urN a/fs/accessfs/inode.c b/fs/accessfs/inode.c --- a/fs/accessfs/inode.c Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/inode.c Sun Jan 8 15:04:18 2006 @@ -0,0 +1,418 @@ +/* Copyright (c) 2001 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/slab.h> +#include <linux/string.h> +#include <linux/proc_fs.h> +#include <asm/statfs.h> +#include <asm/semaphore.h> +#include <asm/uaccess.h> + +#define ACCESSFS_MAGIC 0x3c1d36e7 + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *mountdir = NULL; +#endif + +static DECLARE_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; + + list = dir->children.next; + for (i = 2; i < start && list != &dir->children; ++i) + list = list->next; + + while (list != &dir->children) { + struct accessfs_entry *de; + 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; + list = list->next; + } +} + +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: + down(&accessfs_sem); + dir = (struct accessfs_direntry *) dentry->d_inode->u.generic_ip; + accessfs_readdir_aux(filp, dir, i, dirent, filldir); + up(&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; + struct accessfs_entry *de; + if (!S_ISDIR(pe->attr->mode)) + return NULL; + + dir = (struct accessfs_direntry *) pe; + de = NULL; + list_for_each(list, &dir->children) { + de = list_entry(list, struct accessfs_entry, siblings); + if (strncmp(de->name, name, len) == 0 && de->name[len] == 0) + break; + } + + return de; +} + +static struct dentry *accessfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct inode *inode = NULL; + struct accessfs_entry *pe; + down(&accessfs_sem); + pe = accessfs_lookup_entry(dir->u.generic_ip, dentry->d_name.name, dentry->d_name.len); + up(&accessfs_sem); + if (pe) + inode = iget(dir->i_sb, pe->ino); + + d_add(dentry, inode); + 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->u.generic_ip = 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) { + down(&accessfs_sem); +/* inode->i_ino = accessfs_rootdir.node.ino; */ + accessfs_init_inode(inode, &accessfs_rootdir.node); + accessfs_rootdir.node.ino = inode->i_ino; + up(&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; + down(&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); + + up(&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 inode *i = dentry->d_inode; + int err = inode_setattr(i, iattr); + if (!err) { + struct accessfs_entry *pe; + pe = (struct accessfs_entry *) i->u.generic_ip; + pe->attr->uid = i->i_uid; + pe->attr->gid = i->i_gid; + pe->attr->mode = i->i_mode; + } + + return err; +} + +static void accessfs_read_inode(struct inode *inode) +{ + ino_t ino = inode->i_ino; + struct list_head *list; + down(&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; + } + } + + up(&accessfs_sem); +} + +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 = { + .read_inode = accessfs_read_inode, + .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 struct super_block *accessfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) +{ + return get_sb_single(fs_type, flags, data, accessfs_fill_super); +} + +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; + + down(&accessfs_sem); + err = accessfs_mknod(dir, name, attr); + up(&accessfs_sem); + return err; +} + +void accessfs_unregister(struct accessfs_direntry *dir, const char *name) +{ + struct accessfs_entry *pe; + down(&accessfs_sem); + pe = accessfs_lookup_entry(&dir->node, name, strlen(name)); + if (pe) { + accessfs_unlink(pe); + } + + up(&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) +{ + +#ifdef CONFIG_PROC_FS + /* create mount point for accessfs */ + mountdir = proc_mkdir("access",&proc_root); +#endif + return register_filesystem(&accessfs_fs_type); +} + +static void __exit exit_accessfs_fs(void) +{ + unregister_filesystem(&accessfs_fs_type); + +#ifdef CONFIG_PROC_FS + remove_proc_entry("access",&proc_root); +#endif +} + +module_init(init_accessfs_fs) +module_exit(exit_accessfs_fs) + +MODULE_AUTHOR("Olaf Dietsche"); +MODULE_DESCRIPTION("Access Filesystem"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(accessfs_permitted); +EXPORT_SYMBOL(accessfs_make_dirpath); +EXPORT_SYMBOL(accessfs_register); +EXPORT_SYMBOL(accessfs_unregister); diff -urN a/fs/accessfs/ip.c b/fs/accessfs/ip.c --- a/fs/accessfs/ip.c Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/ip.c Sun Jan 8 15:04:18 2006 @@ -0,0 +1,92 @@ +/* Copyright (c) 2002 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; +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) + && !capable(CAP_NET_BIND_SERVICE)) + return -EACCES; + + return 0; +} + +static int accessfs_ip6_prot_sock(struct socket *sock, + struct sockaddr *uaddr, int addr_len) +{ +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + 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; +#else + return -EACCES; +#endif +} + +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; + 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 != 0) + 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"); +module_param(max_prot_sock, int, 0); +MODULE_PARM_DESC(max_prot_sock, "Number of protected ports"); diff -urN a/include/linux/accessfs_fs.h b/include/linux/accessfs_fs.h --- a/include/linux/accessfs_fs.h Thu Jan 1 01:00:00 1970 +++ b/include/linux/accessfs_fs.h Sun Jan 8 15:04:18 2006 @@ -0,0 +1,49 @@ +/* -*- 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/config.h> +#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); + +#if CONFIG_ACCESSFS_PROT_SOCK < PROT_SOCK +#define CONFIG_ACCESSFS_PROT_SOCK PROT_SOCK +#elseif CONFIG_ACCESSFS_PROT_SOCK > 65536 +#define CONFIG_ACCESSFS_PROT_SOCK 65536 +#endif + +#endif diff -urN a/include/net/sock.h b/include/net/sock.h --- a/include/net/sock.h Wed Jan 4 22:01:49 2006 +++ b/include/net/sock.h Sun Jan 8 15:04:18 2006 @@ -1391,4 +1391,21 @@ extern __u32 sysctl_wmem_default; extern __u32 sysctl_rmem_default; +/* Networking hooks */ +#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 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); +extern void net_hooks_register(struct net_hook_operations *ops); +extern void net_hooks_unregister(struct net_hook_operations *ops); +#endif + #endif /* _SOCK_H */ diff -urN a/net/Kconfig b/net/Kconfig --- a/net/Kconfig Wed Jan 4 13:23:55 2006 +++ b/net/Kconfig Sun Jan 8 15:04:18 2006 @@ -56,6 +56,17 @@ 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. + endif # if INET diff -urN a/net/Makefile b/net/Makefile --- a/net/Makefile Wed Jan 4 22:01:52 2006 +++ b/net/Makefile Sun Jan 8 15:05:09 2006 @@ -45,6 +45,7 @@ obj-$(CONFIG_IP_DCCP) += dccp/ obj-$(CONFIG_IP_SCTP) += sctp/ obj-$(CONFIG_IEEE80211) += ieee80211/ +obj-$(CONFIG_NET_HOOKS) += hooks.o ifeq ($(CONFIG_NET),y) obj-$(CONFIG_SYSCTL) += sysctl_net.o diff -urN a/net/hooks.c b/net/hooks.c --- a/net/hooks.c Thu Jan 1 01:00:00 1970 +++ b/net/hooks.c Sun Jan 8 15:04:18 2006 @@ -0,0 +1,56 @@ +/* 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) +{ +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + 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; +#else + return -EACCES; +#endif +} + +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(default_ip_prot_sock); +EXPORT_SYMBOL(default_ip6_prot_sock); +EXPORT_SYMBOL(net_hooks_register); +EXPORT_SYMBOL(net_hooks_unregister); diff -urN a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c --- a/net/ipv4/af_inet.c Wed Jan 4 22:01:53 2006 +++ b/net/ipv4/af_inet.c Sun Jan 8 15:04:18 2006 @@ -428,7 +428,11 @@ snum = ntohs(addr->sin_port); err = -EACCES; +#ifdef CONFIG_NET_HOOKS + if (net_ops->ip_prot_sock(sock, uaddr, addr_len)) +#else if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) +#endif goto out; /* We keep a pair of addresses. rcv_saddr is the one diff -urN a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c --- a/net/ipv6/af_inet6.c Wed Jan 4 22:01:55 2006 +++ b/net/ipv6/af_inet6.c Sun Jan 8 15:04:18 2006 @@ -258,7 +258,11 @@ return -EINVAL; snum = ntohs(addr->sin6_port); +#ifdef CONFIG_NET_HOOKS + if (net_ops->ip6_prot_sock(sock, uaddr, addr_len)) +#else if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) +#endif return -EACCES; lock_sock(sk); ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] 2.6.15: access permission filesystem 0.17 2006-01-14 21:06 [PATCH] 2.6.15: access permission filesystem 0.17 Olaf Dietsche @ 2006-01-14 22:45 ` Ingo Oeser 2006-01-15 21:22 ` Olaf Dietsche 2006-01-23 11:57 ` Rolf Eike Beer 1 sibling, 1 reply; 19+ messages in thread From: Ingo Oeser @ 2006-01-14 22:45 UTC (permalink / raw) To: Olaf Dietsche; +Cc: linux-kernel [-- Attachment #1: Type: text/plain, Size: 465 bytes --] Hi Olaf, On Saturday 14 January 2006 22:06, Olaf Dietsche wrote: [PATCH] accessfs_readdir_aux() in fs/accessfs/inode.c should set DT_REG, since accessfs supports just directories and regular files. The directory is already handled before in the sole caller of accessfs_readdir_aux(). This saves the superflous stat(2) call after readdir(2). All in all I like the concept! It makes the life of admins much easier. Regards Ingo Oeser [-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] 2.6.15: access permission filesystem 0.17 2006-01-14 22:45 ` Ingo Oeser @ 2006-01-15 21:22 ` Olaf Dietsche 0 siblings, 0 replies; 19+ messages in thread From: Olaf Dietsche @ 2006-01-15 21:22 UTC (permalink / raw) To: Ingo Oeser; +Cc: linux-kernel Ingo Oeser <ioe-lkml@rameria.de> writes: > On Saturday 14 January 2006 22:06, Olaf Dietsche wrote: > [PATCH] > > accessfs_readdir_aux() in fs/accessfs/inode.c > > should set DT_REG, since accessfs supports just directories > and regular files. The directory is already handled before > in the sole caller of accessfs_readdir_aux(). > > This saves the superflous stat(2) call after readdir(2). Thanks for this hint, it is much appreciated. Regards, Olaf. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] 2.6.15: access permission filesystem 0.17 2006-01-14 21:06 [PATCH] 2.6.15: access permission filesystem 0.17 Olaf Dietsche 2006-01-14 22:45 ` Ingo Oeser @ 2006-01-23 11:57 ` Rolf Eike Beer 2006-01-28 22:27 ` [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem Olaf Dietsche 1 sibling, 1 reply; 19+ messages in thread From: Rolf Eike Beer @ 2006-01-23 11:57 UTC (permalink / raw) To: Olaf Dietsche; +Cc: linux-kernel [-- Attachment #1: Type: text/plain, Size: 425 bytes --] Olaf Dietsche wrote: >This 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. Any chance you will push this to Andrew and Linus in near future so we'll see it in mainline? Eike [-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-23 11:57 ` Rolf Eike Beer @ 2006-01-28 22:27 ` Olaf Dietsche 2006-01-28 23:01 ` Andrew Morton 0 siblings, 1 reply; 19+ messages in thread From: Olaf Dietsche @ 2006-01-28 22:27 UTC (permalink / raw) To: Andrew Morton; +Cc: Rolf Eike Beer, linux-kernel Hi Andrew, Can you please include this patch in -mm, to give it wider testing? Accessfs is a permission managing filesystem. It allows to control access to system resources, based on file permissions. It also includes two modules. One module allows granting capabilities based on user-/groupid. The second module allows to grant access to lower numbered IP ports based on user-/groupid. Regards, Olaf. Signed-off-by: Olaf Dietsche <olaf+list.linux-kernel@olafdietsche.de> --- fs/Kconfig | 1 fs/Makefile | 1 fs/accessfs/Kconfig | 82 ++++++++ fs/accessfs/Makefile | 11 + fs/accessfs/capabilities.c | 107 +++++++++++ fs/accessfs/inode.c | 418 ++++++++++++++++++++++++++++++++++++++++++++ fs/accessfs/ip.c | 92 +++++++++ include/linux/accessfs_fs.h | 49 +++++ include/net/sock.h | 17 + net/Kconfig | 11 + net/Makefile | 1 net/hooks.c | 56 +++++ net/ipv4/af_inet.c | 4 net/ipv6/af_inet6.c | 4 14 files changed, 854 insertions(+) diff -urN a/fs/Kconfig b/fs/Kconfig --- a/fs/Kconfig Fri Jan 27 23:52:40 2006 +++ b/fs/Kconfig Sat Jan 28 12:47:19 2006 @@ -1253,6 +1253,7 @@ It's currently broken, so for now: answer N. +source "fs/accessfs/Kconfig" config SYSV_FS diff -urN a/fs/Makefile b/fs/Makefile --- a/fs/Makefile Fri Jan 27 23:52:40 2006 +++ b/fs/Makefile Sat Jan 28 12:48:03 2006 @@ -103,3 +103,4 @@ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_CONFIGFS_FS) += configfs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/ +obj-$(CONFIG_ACCESS_FS) += accessfs/ diff -urN a/fs/accessfs/Kconfig b/fs/accessfs/Kconfig --- a/fs/accessfs/Kconfig Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/Kconfig Sat Jan 28 12:47:19 2006 @@ -0,0 +1,82 @@ +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. + + The recommended mount point for this file-system is /proc/access, + which will appear automatically in the /proc filesystem. + + 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. + + With this option, 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 + + 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_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. + + 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 + + If you're unsure, say N. + diff -urN a/fs/accessfs/Makefile b/fs/accessfs/Makefile --- a/fs/accessfs/Makefile Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/Makefile Sat Jan 28 12:47:19 2006 @@ -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 -urN a/fs/accessfs/capabilities.c b/fs/accessfs/capabilities.c --- a/fs/accessfs/capabilities.c Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/capabilities.c Sat Jan 28 12:47:19 2006 @@ -0,0 +1,107 @@ +/* Copyright (c) 2002 Olaf Dietsche + * + * User based capabilities for Linux. + */ + +#include <linux/accessfs_fs.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/security.h> + +static struct access_attr caps[29]; +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" +}; + +static int accessfs_capable(struct task_struct *tsk, int cap) +{ + if (accessfs_permitted(&caps[cap], MAY_EXEC)) { + /* capability granted */ + tsk->flags |= PF_SUPERPRIV; + return 0; + } + + /* capability denied */ + return -EPERM; +} + +static struct security_operations accessfs_security_ops = { + .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 < sizeof(caps) / sizeof(caps[0]); ++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; + } + } + + err = register_security(&accessfs_security_ops); + if (err != 0) + unregister_capabilities(dir, sizeof(names) / sizeof(names[0])); + + return err; +} + +static void __exit exit_capabilities(void) +{ + struct accessfs_direntry *dir; + dir = accessfs_make_dirpath("capabilities"); + unregister_security(&accessfs_security_ops); + unregister_capabilities(dir, sizeof(names) / sizeof(names[0])); +} + +module_init(init_capabilities) +module_exit(exit_capabilities) + +MODULE_AUTHOR("Olaf Dietsche"); +MODULE_DESCRIPTION("User based capabilities"); +MODULE_LICENSE("GPL"); diff -urN a/fs/accessfs/inode.c b/fs/accessfs/inode.c --- a/fs/accessfs/inode.c Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/inode.c Sat Jan 28 12:47:19 2006 @@ -0,0 +1,418 @@ +/* Copyright (c) 2001 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/slab.h> +#include <linux/string.h> +#include <linux/proc_fs.h> +#include <asm/statfs.h> +#include <asm/semaphore.h> +#include <asm/uaccess.h> + +#define ACCESSFS_MAGIC 0x3c1d36e7 + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *mountdir = NULL; +#endif + +static DECLARE_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; + + list = dir->children.next; + for (i = 2; i < start && list != &dir->children; ++i) + list = list->next; + + while (list != &dir->children) { + struct accessfs_entry *de; + 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; + list = list->next; + } +} + +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: + down(&accessfs_sem); + dir = (struct accessfs_direntry *) dentry->d_inode->u.generic_ip; + accessfs_readdir_aux(filp, dir, i, dirent, filldir); + up(&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; + struct accessfs_entry *de; + if (!S_ISDIR(pe->attr->mode)) + return NULL; + + dir = (struct accessfs_direntry *) pe; + de = NULL; + list_for_each(list, &dir->children) { + de = list_entry(list, struct accessfs_entry, siblings); + if (strncmp(de->name, name, len) == 0 && de->name[len] == 0) + break; + } + + return de; +} + +static struct dentry *accessfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct inode *inode = NULL; + struct accessfs_entry *pe; + down(&accessfs_sem); + pe = accessfs_lookup_entry(dir->u.generic_ip, dentry->d_name.name, dentry->d_name.len); + up(&accessfs_sem); + if (pe) + inode = iget(dir->i_sb, pe->ino); + + d_add(dentry, inode); + 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->u.generic_ip = 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) { + down(&accessfs_sem); +/* inode->i_ino = accessfs_rootdir.node.ino; */ + accessfs_init_inode(inode, &accessfs_rootdir.node); + accessfs_rootdir.node.ino = inode->i_ino; + up(&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; + down(&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); + + up(&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 inode *i = dentry->d_inode; + int err = inode_setattr(i, iattr); + if (!err) { + struct accessfs_entry *pe; + pe = (struct accessfs_entry *) i->u.generic_ip; + pe->attr->uid = i->i_uid; + pe->attr->gid = i->i_gid; + pe->attr->mode = i->i_mode; + } + + return err; +} + +static void accessfs_read_inode(struct inode *inode) +{ + ino_t ino = inode->i_ino; + struct list_head *list; + down(&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; + } + } + + up(&accessfs_sem); +} + +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 = { + .read_inode = accessfs_read_inode, + .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 struct super_block *accessfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) +{ + return get_sb_single(fs_type, flags, data, accessfs_fill_super); +} + +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; + + down(&accessfs_sem); + err = accessfs_mknod(dir, name, attr); + up(&accessfs_sem); + return err; +} + +void accessfs_unregister(struct accessfs_direntry *dir, const char *name) +{ + struct accessfs_entry *pe; + down(&accessfs_sem); + pe = accessfs_lookup_entry(&dir->node, name, strlen(name)); + if (pe) { + accessfs_unlink(pe); + } + + up(&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) +{ + +#ifdef CONFIG_PROC_FS + /* create mount point for accessfs */ + mountdir = proc_mkdir("access",&proc_root); +#endif + return register_filesystem(&accessfs_fs_type); +} + +static void __exit exit_accessfs_fs(void) +{ + unregister_filesystem(&accessfs_fs_type); + +#ifdef CONFIG_PROC_FS + remove_proc_entry("access",&proc_root); +#endif +} + +module_init(init_accessfs_fs) +module_exit(exit_accessfs_fs) + +MODULE_AUTHOR("Olaf Dietsche"); +MODULE_DESCRIPTION("Access Filesystem"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(accessfs_permitted); +EXPORT_SYMBOL(accessfs_make_dirpath); +EXPORT_SYMBOL(accessfs_register); +EXPORT_SYMBOL(accessfs_unregister); diff -urN a/fs/accessfs/ip.c b/fs/accessfs/ip.c --- a/fs/accessfs/ip.c Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/ip.c Sat Jan 28 12:47:19 2006 @@ -0,0 +1,92 @@ +/* Copyright (c) 2002 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; +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) + && !capable(CAP_NET_BIND_SERVICE)) + return -EACCES; + + return 0; +} + +static int accessfs_ip6_prot_sock(struct socket *sock, + struct sockaddr *uaddr, int addr_len) +{ +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + 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; +#else + return -EACCES; +#endif +} + +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; + 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 != 0) + 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"); +module_param(max_prot_sock, int, 0); +MODULE_PARM_DESC(max_prot_sock, "Number of protected ports"); diff -urN a/include/linux/accessfs_fs.h b/include/linux/accessfs_fs.h --- a/include/linux/accessfs_fs.h Thu Jan 1 01:00:00 1970 +++ b/include/linux/accessfs_fs.h Sat Jan 28 12:47:19 2006 @@ -0,0 +1,49 @@ +/* -*- 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/config.h> +#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); + +#if CONFIG_ACCESSFS_PROT_SOCK < PROT_SOCK +#define CONFIG_ACCESSFS_PROT_SOCK PROT_SOCK +#elseif CONFIG_ACCESSFS_PROT_SOCK > 65536 +#define CONFIG_ACCESSFS_PROT_SOCK 65536 +#endif + +#endif diff -urN a/include/net/sock.h b/include/net/sock.h --- a/include/net/sock.h Fri Jan 27 23:53:13 2006 +++ b/include/net/sock.h Sat Jan 28 12:47:19 2006 @@ -1417,4 +1417,21 @@ extern __u32 sysctl_wmem_default; extern __u32 sysctl_rmem_default; +/* Networking hooks */ +#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 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); +extern void net_hooks_register(struct net_hook_operations *ops); +extern void net_hooks_unregister(struct net_hook_operations *ops); +#endif + #endif /* _SOCK_H */ diff -urN a/net/Kconfig b/net/Kconfig --- a/net/Kconfig Sat Jan 28 12:46:23 2006 +++ b/net/Kconfig Sat Jan 28 12:47:19 2006 @@ -56,6 +56,17 @@ 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. + endif # if INET diff -urN a/net/Makefile b/net/Makefile --- a/net/Makefile Fri Jan 27 23:53:18 2006 +++ b/net/Makefile Sat Jan 28 12:48:26 2006 @@ -46,6 +46,7 @@ obj-$(CONFIG_IP_SCTP) += sctp/ obj-$(CONFIG_IEEE80211) += ieee80211/ obj-$(CONFIG_TIPC) += tipc/ +obj-$(CONFIG_NET_HOOKS) += hooks.o ifeq ($(CONFIG_NET),y) obj-$(CONFIG_SYSCTL) += sysctl_net.o diff -urN a/net/hooks.c b/net/hooks.c --- a/net/hooks.c Thu Jan 1 01:00:00 1970 +++ b/net/hooks.c Sat Jan 28 12:47:19 2006 @@ -0,0 +1,56 @@ +/* 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) +{ +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + 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; +#else + return -EACCES; +#endif +} + +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(default_ip_prot_sock); +EXPORT_SYMBOL(default_ip6_prot_sock); +EXPORT_SYMBOL(net_hooks_register); +EXPORT_SYMBOL(net_hooks_unregister); diff -urN a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c --- a/net/ipv4/af_inet.c Fri Jan 27 23:53:20 2006 +++ b/net/ipv4/af_inet.c Sat Jan 28 12:47:19 2006 @@ -431,7 +431,11 @@ snum = ntohs(addr->sin_port); err = -EACCES; +#ifdef CONFIG_NET_HOOKS + if (net_ops->ip_prot_sock(sock, uaddr, addr_len)) +#else if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) +#endif goto out; /* We keep a pair of addresses. rcv_saddr is the one diff -urN a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c --- a/net/ipv6/af_inet6.c Fri Jan 27 23:53:23 2006 +++ b/net/ipv6/af_inet6.c Sat Jan 28 12:47:19 2006 @@ -260,7 +260,11 @@ return -EINVAL; snum = ntohs(addr->sin6_port); +#ifdef CONFIG_NET_HOOKS + if (net_ops->ip6_prot_sock(sock, uaddr, addr_len)) +#else if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) +#endif return -EACCES; lock_sock(sk); ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-28 22:27 ` [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem Olaf Dietsche @ 2006-01-28 23:01 ` Andrew Morton 2006-01-29 22:47 ` Olaf Dietsche 2006-01-30 9:07 ` Jan Engelhardt 0 siblings, 2 replies; 19+ messages in thread From: Andrew Morton @ 2006-01-28 23:01 UTC (permalink / raw) To: Olaf Dietsche; +Cc: eike-kernel, linux-kernel Olaf Dietsche <olaf+list.linux-kernel@olafdietsche.de> wrote: > > Hi Andrew, > > Can you please include this patch in -mm, to give it wider testing? I doubt if it'll get a lot of runtime testing. > Accessfs is a permission managing filesystem. It allows to control > access to system resources, based on file permissions. It also > includes two modules. One module allows granting capabilities based > on user-/groupid. The second module allows to grant access to lower > numbered IP ports based on user-/groupid. > It seems to be network-centric? Do these capabilities really need to be implemented via a brand-new security infrastructure, rather then by enhancing the existing one(s)? > + 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 Documenting a feature in Kconfig is a bit odd. I assume proper Documentation is forthcoming? > + */ > + > +#include <linux/accessfs_fs.h> > +#include <linux/init.h> > +#include <linux/module.h> > +#include <linux/security.h> > + > +static struct access_attr caps[29]; caps[ARRAY_SIZE(names)] should work. > +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" > +}; > + > > +static void unregister_capabilities(struct accessfs_direntry *dir, int n) > +{ > + int i; > + for (i = 0; i < n; ++i) { > + accessfs_unregister(dir, names[i]); > + } > +} Unneeded braces. > +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 < sizeof(caps) / sizeof(caps[0]); ++i) { ARRAY_SIZE() (lots of instances) > +static DECLARE_MUTEX(accessfs_sem); Please use a `struct mutex'. > + > +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; > + > + list = dir->children.next; > + for (i = 2; i < start && list != &dir->children; ++i) > + list = list->next; > + > + while (list != &dir->children) { > + struct accessfs_entry *de; > + 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; > + list = list->next; > + } > +} Use standard list accessors? Please fit code into 80 cols. > +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: > + down(&accessfs_sem); > + dir = (struct accessfs_direntry *) dentry->d_inode->u.generic_ip; Unneeded typecast. > + accessfs_readdir_aux(filp, dir, i, dirent, filldir); > + up(&accessfs_sem); > + break; > + } > + > + return 0; > +} > + > +static void accessfs_init_inode(struct inode *inode, struct accessfs_entry *pe) > +{ > + static const struct timespec epoch = {0, 0}; Unneeded initialiser (although it does make things clearer) > + > +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; > +} Again, painful to read in 80-cols. > +struct accessfs_direntry *accessfs_make_dirpath(const char *name) > +{ > + struct accessfs_direntry *dir = &accessfs_rootdir; > + const char *slash; > + down(&accessfs_sem); Shouldn't that lock be per-superblock? > +static void accessfs_read_inode(struct inode *inode) > +{ > + ino_t ino = inode->i_ino; > + struct list_head *list; > + down(&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; > + } > + } That's not a hash! > +{ > + unregister_filesystem(&accessfs_fs_type); > + > +#ifdef CONFIG_PROC_FS > + remove_proc_entry("access",&proc_root); > +#endif > +} The CONFIG_PROC_FS ifdefs shouldn't be needed - we have stubs. > +static int accessfs_ip6_prot_sock(struct socket *sock, > + struct sockaddr *uaddr, int addr_len) > +{ > +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) That's a bit awkward, especially the CONFIG_IPV6_MODULE dependency. Is it possible to just unconditionally compile this in? > +static int __init init_ip(void) > +{ > + struct accessfs_direntry *dir = accessfs_make_dirpath("net/ip/bind"); > + int i; > + bind_to_port = kmalloc(max_prot_sock * sizeof(*bind_to_port), GFP_KERNEL); > + if (bind_to_port == 0) Use NULL to avoid sparse warnings. > + > +#if CONFIG_ACCESSFS_PROT_SOCK < PROT_SOCK > +#define CONFIG_ACCESSFS_PROT_SOCK PROT_SOCK > +#elseif CONFIG_ACCESSFS_PROT_SOCK > 65536 > +#define CONFIG_ACCESSFS_PROT_SOCK 65536 > +#endif Please don't redefine CONFIG_ variables like this. I'd have expected the compiler to have generated a warning about this, too. > snum = ntohs(addr->sin_port); > err = -EACCES; > +#ifdef CONFIG_NET_HOOKS > + if (net_ops->ip_prot_sock(sock, uaddr, addr_len)) > +#else > if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) > +#endif > goto out; Maybe some wrapper which hides the above? > /* We keep a pair of addresses. rcv_saddr is the one > diff -urN a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c > --- a/net/ipv6/af_inet6.c Fri Jan 27 23:53:23 2006 > +++ b/net/ipv6/af_inet6.c Sat Jan 28 12:47:19 2006 > @@ -260,7 +260,11 @@ > return -EINVAL; > > snum = ntohs(addr->sin6_port); > +#ifdef CONFIG_NET_HOOKS > + if (net_ops->ip6_prot_sock(sock, uaddr, addr_len)) > +#else > if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) > +#endif > return -EACCES; > which could be used here as well. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-28 23:01 ` Andrew Morton @ 2006-01-29 22:47 ` Olaf Dietsche 2006-01-30 9:07 ` Jan Engelhardt 1 sibling, 0 replies; 19+ messages in thread From: Olaf Dietsche @ 2006-01-29 22:47 UTC (permalink / raw) To: Andrew Morton; +Cc: eike-kernel, linux-kernel Andrew Morton <akpm@osdl.org> writes: > Olaf Dietsche <olaf+list.linux-kernel@olafdietsche.de> wrote: >> >> Can you please include this patch in -mm, to give it wider testing? > > I doubt if it'll get a lot of runtime testing. Well, this review alone was worth the effort. Thanks. >> Accessfs is a permission managing filesystem. It allows to control >> access to system resources, based on file permissions. It also >> includes two modules. One module allows granting capabilities based >> on user-/groupid. The second module allows to grant access to lower >> numbered IP ports based on user-/groupid. >> > > It seems to be network-centric? It could be used anywhere, where you want access control on a file permission (user, group, other) basis. But yes, currently I use it that way only. > Do these capabilities really need to be implemented via a brand-new > security infrastructure, rather then by enhancing the existing one(s)? This doesn't change the existing security infrastructure. It just permits users and/or groups to get selected privileges. But I won't insist on this one, since I use filesystem capabilities anyway. >> + 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 > > Documenting a feature in Kconfig is a bit odd. I assume proper > Documentation is forthcoming? When I encounter a new item in Kconfig, I expect help, which explains, what it is good for. Anyway, I moved a few lines to Documentation/filesystems/accessfs.txt >> +struct accessfs_direntry *accessfs_make_dirpath(const char *name) >> +{ >> + struct accessfs_direntry *dir = &accessfs_rootdir; >> + const char *slash; >> + down(&accessfs_sem); > > Shouldn't that lock be per-superblock? Like procfs or sysfs there's only one instance, so I don't think this is necessary. >> +static void accessfs_read_inode(struct inode *inode) >> +{ >> + ino_t ino = inode->i_ino; >> + struct list_head *list; >> + down(&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; >> + } >> + } > > That's not a hash! Do you think it should be? accessfs_read_inode() is not a frequently called function. I think, I addressed all your remaining points. If you have further concerns, I won't respond until next weekend. Regards, Olaf. Signed-off-by: Olaf Dietsche <olaf+list.linux-kernel@olafdietsche.de> --- Documentation/filesystems/accessfs.txt | 41 +++ fs/Kconfig | 1 fs/Makefile | 1 fs/accessfs/Kconfig | 50 +++ fs/accessfs/Makefile | 11 fs/accessfs/capabilities.c | 107 ++++++++ fs/accessfs/inode.c | 426 +++++++++++++++++++++++++++++++++ fs/accessfs/ip.c | 95 +++++++ include/linux/accessfs_fs.h | 43 +++ include/net/sock.h | 43 +++ net/Kconfig | 11 net/Makefile | 1 net/hooks.c | 55 ++++ net/ipv4/af_inet.c | 2 net/ipv6/af_inet6.c | 2 15 files changed, 887 insertions(+), 2 deletions(-) diff -urN a/Documentation/filesystems/accessfs.txt b/Documentation/filesystems/accessfs.txt --- a/Documentation/filesystems/accessfs.txt Thu Jan 1 01:00:00 1970 +++ b/Documentation/filesystems/accessfs.txt Sun Jan 29 16:16:31 2006 @@ -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 -urN a/fs/Kconfig b/fs/Kconfig --- a/fs/Kconfig Fri Jan 27 23:52:40 2006 +++ b/fs/Kconfig Sat Jan 28 12:47:19 2006 @@ -1253,6 +1253,7 @@ It's currently broken, so for now: answer N. +source "fs/accessfs/Kconfig" config SYSV_FS diff -urN a/fs/Makefile b/fs/Makefile --- a/fs/Makefile Fri Jan 27 23:52:40 2006 +++ b/fs/Makefile Sat Jan 28 12:48:03 2006 @@ -103,3 +103,4 @@ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_CONFIGFS_FS) += configfs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/ +obj-$(CONFIG_ACCESS_FS) += accessfs/ diff -urN a/fs/accessfs/Kconfig b/fs/accessfs/Kconfig --- a/fs/accessfs/Kconfig Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/Kconfig Sun Jan 29 23:18:33 2006 @@ -0,0 +1,50 @@ +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_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 -urN a/fs/accessfs/Makefile b/fs/accessfs/Makefile --- a/fs/accessfs/Makefile Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/Makefile Sat Jan 28 12:47:19 2006 @@ -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 -urN a/fs/accessfs/capabilities.c b/fs/accessfs/capabilities.c --- a/fs/accessfs/capabilities.c Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/capabilities.c Sun Jan 29 23:20:31 2006 @@ -0,0 +1,107 @@ +/* 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> + +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" +}; + +static struct access_attr caps[ARRAY_SIZE(names)]; + +static int accessfs_capable(struct task_struct *tsk, int cap) +{ + if (accessfs_permitted(&caps[cap], MAY_EXEC)) { + /* capability granted */ + tsk->flags |= PF_SUPERPRIV; + return 0; + } + + /* capability denied */ + return -EPERM; +} + +static struct security_operations accessfs_security_ops = { + .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; + } + } + + err = register_security(&accessfs_security_ops); + if (err != 0) + unregister_capabilities(dir, ARRAY_SIZE(names)); + + return err; +} + +static void __exit exit_capabilities(void) +{ + struct accessfs_direntry *dir; + dir = accessfs_make_dirpath("capabilities"); + unregister_security(&accessfs_security_ops); + unregister_capabilities(dir, ARRAY_SIZE(names)); +} + +module_init(init_capabilities) +module_exit(exit_capabilities) + +MODULE_AUTHOR("Olaf Dietsche"); +MODULE_DESCRIPTION("User based capabilities"); +MODULE_LICENSE("GPL"); diff -urN a/fs/accessfs/inode.c b/fs/accessfs/inode.c --- a/fs/accessfs/inode.c Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/inode.c Sun Jan 29 23:20:20 2006 @@ -0,0 +1,426 @@ +/* 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/slab.h> +#include <linux/string.h> +#include <linux/proc_fs.h> +#include <asm/statfs.h> +#include <asm/semaphore.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->u.generic_ip; + 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; + struct accessfs_entry *de; + if (!S_ISDIR(pe->attr->mode)) + return NULL; + + dir = (struct accessfs_direntry *) pe; + de = NULL; + list_for_each(list, &dir->children) { + de = list_entry(list, struct accessfs_entry, siblings); + if (strncmp(de->name, name, len) == 0 && de->name[len] == 0) + break; + } + + return de; +} + +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->u.generic_ip, dentry->d_name.name, + dentry->d_name.len); + mutex_unlock(&accessfs_sem); + if (pe) + inode = iget(dir->i_sb, pe->ino); + + d_add(dentry, inode); + 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->u.generic_ip = 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 inode *i = dentry->d_inode; + int err = inode_setattr(i, iattr); + if (!err) { + struct accessfs_entry *pe; + pe = (struct accessfs_entry *) i->u.generic_ip; + pe->attr->uid = i->i_uid; + pe->attr->gid = i->i_gid; + pe->attr->mode = i->i_mode; + } + + return err; +} + +static void accessfs_read_inode(struct inode *inode) +{ + ino_t ino = inode->i_ino; + struct list_head *list; + 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); +} + +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 = { + .read_inode = accessfs_read_inode, + .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 struct super_block *accessfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + return get_sb_single(fs_type, flags, data, accessfs_fill_super); +} + +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",&proc_root); + return register_filesystem(&accessfs_fs_type); +} + +static void __exit exit_accessfs_fs(void) +{ + unregister_filesystem(&accessfs_fs_type); + remove_proc_entry("access",&proc_root); +} + +module_init(init_accessfs_fs) +module_exit(exit_accessfs_fs) + +MODULE_AUTHOR("Olaf Dietsche"); +MODULE_DESCRIPTION("Access Filesystem"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(accessfs_permitted); +EXPORT_SYMBOL(accessfs_make_dirpath); +EXPORT_SYMBOL(accessfs_register); +EXPORT_SYMBOL(accessfs_unregister); diff -urN a/fs/accessfs/ip.c b/fs/accessfs/ip.c --- a/fs/accessfs/ip.c Thu Jan 1 01:00:00 1970 +++ b/fs/accessfs/ip.c Sun Jan 29 23:20:39 2006 @@ -0,0 +1,95 @@ +/* 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; +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) + && !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"); +module_param(max_prot_sock, int, 0); +MODULE_PARM_DESC(max_prot_sock, "Number of protected ports"); diff -urN a/include/linux/accessfs_fs.h b/include/linux/accessfs_fs.h --- a/include/linux/accessfs_fs.h Thu Jan 1 01:00:00 1970 +++ b/include/linux/accessfs_fs.h Sun Jan 29 13:58:52 2006 @@ -0,0 +1,43 @@ +/* -*- 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/config.h> +#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 -urN a/include/net/sock.h b/include/net/sock.h --- a/include/net/sock.h Fri Jan 27 23:53:13 2006 +++ b/include/net/sock.h Sun Jan 29 15:29:13 2006 @@ -1417,4 +1417,47 @@ 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 -urN a/net/Kconfig b/net/Kconfig --- a/net/Kconfig Sat Jan 28 12:46:23 2006 +++ b/net/Kconfig Sat Jan 28 12:47:19 2006 @@ -56,6 +56,17 @@ 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. + endif # if INET diff -urN a/net/Makefile b/net/Makefile --- a/net/Makefile Fri Jan 27 23:53:18 2006 +++ b/net/Makefile Sat Jan 28 12:48:26 2006 @@ -46,6 +46,7 @@ obj-$(CONFIG_IP_SCTP) += sctp/ obj-$(CONFIG_IEEE80211) += ieee80211/ obj-$(CONFIG_TIPC) += tipc/ +obj-$(CONFIG_NET_HOOKS) += hooks.o ifeq ($(CONFIG_NET),y) obj-$(CONFIG_SYSCTL) += sysctl_net.o diff -urN a/net/hooks.c b/net/hooks.c --- a/net/hooks.c Thu Jan 1 01:00:00 1970 +++ b/net/hooks.c Sun Jan 29 15:29:00 2006 @@ -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 -urN a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c --- a/net/ipv4/af_inet.c Fri Jan 27 23:53:20 2006 +++ b/net/ipv4/af_inet.c Sun Jan 29 15:29:48 2006 @@ -431,7 +431,7 @@ 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 -urN a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c --- a/net/ipv6/af_inet6.c Fri Jan 27 23:53:23 2006 +++ b/net/ipv6/af_inet6.c Sun Jan 29 15:28:49 2006 @@ -260,7 +260,7 @@ 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); ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-28 23:01 ` Andrew Morton 2006-01-29 22:47 ` Olaf Dietsche @ 2006-01-30 9:07 ` Jan Engelhardt 2006-01-30 9:16 ` Andrew Morton 1 sibling, 1 reply; 19+ messages in thread From: Jan Engelhardt @ 2006-01-30 9:07 UTC (permalink / raw) To: Andrew Morton; +Cc: Olaf Dietsche, eike-kernel, Linux Kernel Mailing List >> +static DECLARE_MUTEX(accessfs_sem); > >Please use a `struct mutex'. You know what's irritating? That DECLARE_MUTEX starts a semaphore and DEFINE_MUTEX a mutex. Jan Engelhardt -- ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-30 9:07 ` Jan Engelhardt @ 2006-01-30 9:16 ` Andrew Morton 2006-01-30 9:24 ` Jan Engelhardt 0 siblings, 1 reply; 19+ messages in thread From: Andrew Morton @ 2006-01-30 9:16 UTC (permalink / raw) To: Jan Engelhardt; +Cc: olaf+list.linux-kernel, eike-kernel, linux-kernel Jan Engelhardt <jengelh@linux01.gwdg.de> wrote: > > > >> +static DECLARE_MUTEX(accessfs_sem); > > > >Please use a `struct mutex'. > > You know what's irritating? That DECLARE_MUTEX starts a semaphore and > DEFINE_MUTEX a mutex. > DECLARE_MUTEX will go away. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-30 9:16 ` Andrew Morton @ 2006-01-30 9:24 ` Jan Engelhardt 2006-01-30 9:32 ` Andrew Morton 2006-01-30 9:46 ` Arjan van de Ven 0 siblings, 2 replies; 19+ messages in thread From: Jan Engelhardt @ 2006-01-30 9:24 UTC (permalink / raw) To: Andrew Morton; +Cc: olaf+list.linux-kernel, eike-kernel, linux-kernel >> >> +static DECLARE_MUTEX(accessfs_sem); >> > >> >Please use a `struct mutex'. >> >> You know what's irritating? That DECLARE_MUTEX starts a semaphore and >> DEFINE_MUTEX a mutex. > >DECLARE_MUTEX will go away. And be replaced by... DEFINE_SEMAPHORE? Jan Engelhardt -- ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-30 9:24 ` Jan Engelhardt @ 2006-01-30 9:32 ` Andrew Morton 2006-01-30 9:46 ` Arjan van de Ven 1 sibling, 0 replies; 19+ messages in thread From: Andrew Morton @ 2006-01-30 9:32 UTC (permalink / raw) To: Jan Engelhardt; +Cc: olaf+list.linux-kernel, eike-kernel, linux-kernel Jan Engelhardt <jengelh@linux01.gwdg.de> wrote: > > >> >> +static DECLARE_MUTEX(accessfs_sem); > >> > > >> >Please use a `struct mutex'. > >> > >> You know what's irritating? That DECLARE_MUTEX starts a semaphore and > >> DEFINE_MUTEX a mutex. > > > >DECLARE_MUTEX will go away. > > And be replaced by... DEFINE_SEMAPHORE? > That would be logical. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-30 9:24 ` Jan Engelhardt 2006-01-30 9:32 ` Andrew Morton @ 2006-01-30 9:46 ` Arjan van de Ven 2006-01-30 9:51 ` Jan Engelhardt 1 sibling, 1 reply; 19+ messages in thread From: Arjan van de Ven @ 2006-01-30 9:46 UTC (permalink / raw) To: Jan Engelhardt Cc: Andrew Morton, olaf+list.linux-kernel, eike-kernel, linux-kernel On Mon, 2006-01-30 at 10:24 +0100, Jan Engelhardt wrote: > >> >> +static DECLARE_MUTEX(accessfs_sem); > >> > > >> >Please use a `struct mutex'. > >> > >> You know what's irritating? That DECLARE_MUTEX starts a semaphore and > >> DEFINE_MUTEX a mutex. > > > >DECLARE_MUTEX will go away. > > And be replaced by... DEFINE_SEMAPHORE? ... or just go away entirely, depending if there are any users of it left after all mutexes have been converted to DEFINE_MUTEX()... ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-30 9:46 ` Arjan van de Ven @ 2006-01-30 9:51 ` Jan Engelhardt 2006-01-30 9:54 ` Arjan van de Ven 0 siblings, 1 reply; 19+ messages in thread From: Jan Engelhardt @ 2006-01-30 9:51 UTC (permalink / raw) To: Arjan van de Ven Cc: Andrew Morton, olaf+list.linux-kernel, eike-kernel, linux-kernel >> >DECLARE_MUTEX will go away. >> >> And be replaced by... DEFINE_SEMAPHORE? > >... or just go away entirely, depending if there are any users of it >left after all mutexes have been converted to DEFINE_MUTEX()... Does that imply that struct semaphore be removed too? You can't do that. Jan Engelhardt -- ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-30 9:51 ` Jan Engelhardt @ 2006-01-30 9:54 ` Arjan van de Ven 2006-01-30 10:25 ` Jan Engelhardt 0 siblings, 1 reply; 19+ messages in thread From: Arjan van de Ven @ 2006-01-30 9:54 UTC (permalink / raw) To: Jan Engelhardt Cc: Andrew Morton, olaf+list.linux-kernel, eike-kernel, linux-kernel On Mon, 2006-01-30 at 10:51 +0100, Jan Engelhardt wrote: > >> >DECLARE_MUTEX will go away. > >> > >> And be replaced by... DEFINE_SEMAPHORE? > > > >... or just go away entirely, depending if there are any users of it > >left after all mutexes have been converted to DEFINE_MUTEX()... > > Does that imply that struct semaphore be removed too? not per se > You can't do that. Exactly why not? Assuming there are zero users left in the kernel..... (which highly obviously is a prerequisite for even thinking about such a step) ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-30 9:54 ` Arjan van de Ven @ 2006-01-30 10:25 ` Jan Engelhardt 2006-01-30 10:31 ` Arjan van de Ven 0 siblings, 1 reply; 19+ messages in thread From: Jan Engelhardt @ 2006-01-30 10:25 UTC (permalink / raw) To: Arjan van de Ven Cc: Andrew Morton, olaf+list.linux-kernel, eike-kernel, linux-kernel >> You can't do that. > >Exactly why not? Assuming there are zero users left in the kernel..... >(which highly obviously is a prerequisite for even thinking about such a >step) Documentation/mutex-design.txt: * - mutexes may not be used in irq contexts I need some locking strategy within irq contexts, and that suggests semaphores do the job. Jan Engelhardt -- | Software Engineer and Linux/Unix Network Administrator | Alphagate Systems, http://alphagate.hopto.org/ | jengelh's site, http://jengelh.hopto.org/ ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-30 10:25 ` Jan Engelhardt @ 2006-01-30 10:31 ` Arjan van de Ven 2006-01-30 10:36 ` Jan Engelhardt 0 siblings, 1 reply; 19+ messages in thread From: Arjan van de Ven @ 2006-01-30 10:31 UTC (permalink / raw) To: Jan Engelhardt Cc: Andrew Morton, olaf+list.linux-kernel, eike-kernel, linux-kernel On Mon, 2006-01-30 at 11:25 +0100, Jan Engelhardt wrote: > >> You can't do that. > > > >Exactly why not? Assuming there are zero users left in the kernel..... > >(which highly obviously is a prerequisite for even thinking about such a > >step) > > Documentation/mutex-design.txt: > * - mutexes may not be used in irq contexts > > I need some locking strategy within irq contexts, and that suggests > semaphores do the job. well... maybe. If you need to mess inside irq context, spinlocks sound more the right thing. Or if you need to do a "I'm done" in the irq and a "sleep until done" thing in process context, then you really should use completions instead. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-30 10:31 ` Arjan van de Ven @ 2006-01-30 10:36 ` Jan Engelhardt 2006-01-30 10:39 ` Arjan van de Ven 0 siblings, 1 reply; 19+ messages in thread From: Jan Engelhardt @ 2006-01-30 10:36 UTC (permalink / raw) To: Arjan van de Ven Cc: Andrew Morton, olaf+list.linux-kernel, eike-kernel, linux-kernel > >well... maybe. >If you need to mess inside irq context, spinlocks sound more the right >thing. Or if you need to do a "I'm done" in the irq and a "sleep until >done" thing in process context, then you really should use completions >instead. Hm, is it allowed to call copy_{from,to}_user() in irq context? Jan Engelhardt -- ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-30 10:36 ` Jan Engelhardt @ 2006-01-30 10:39 ` Arjan van de Ven 2006-01-30 10:46 ` Jan Engelhardt 0 siblings, 1 reply; 19+ messages in thread From: Arjan van de Ven @ 2006-01-30 10:39 UTC (permalink / raw) To: Jan Engelhardt Cc: Andrew Morton, olaf+list.linux-kernel, eike-kernel, linux-kernel On Mon, 2006-01-30 at 11:36 +0100, Jan Engelhardt wrote: > > > >well... maybe. > >If you need to mess inside irq context, spinlocks sound more the right > >thing. Or if you need to do a "I'm done" in the irq and a "sleep until > >done" thing in process context, then you really should use completions > >instead. > > Hm, is it allowed to call copy_{from,to}_user() in irq context? absolutely not. IRQ context has no defined "userspace" context at all, so even if copy_from/to_user wouldn't sleep (they do, and that alone makes it "illegal" to do already) it would be highly undefined what you would be copying to/from. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem 2006-01-30 10:39 ` Arjan van de Ven @ 2006-01-30 10:46 ` Jan Engelhardt 0 siblings, 0 replies; 19+ messages in thread From: Jan Engelhardt @ 2006-01-30 10:46 UTC (permalink / raw) To: Arjan van de Ven Cc: Andrew Morton, olaf+list.linux-kernel, eike-kernel, linux-kernel >> Hm, is it allowed to call copy_{from,to}_user() in irq context? > >absolutely not. Right, everything clarified then. Remove the semaphores ;) Jan Engelhardt -- ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2006-01-30 10:46 UTC | newest] Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2006-01-14 21:06 [PATCH] 2.6.15: access permission filesystem 0.17 Olaf Dietsche 2006-01-14 22:45 ` Ingo Oeser 2006-01-15 21:22 ` Olaf Dietsche 2006-01-23 11:57 ` Rolf Eike Beer 2006-01-28 22:27 ` [PATCH 2.6.16-rc1-git4] accessfs: a permission managing filesystem Olaf Dietsche 2006-01-28 23:01 ` Andrew Morton 2006-01-29 22:47 ` Olaf Dietsche 2006-01-30 9:07 ` Jan Engelhardt 2006-01-30 9:16 ` Andrew Morton 2006-01-30 9:24 ` Jan Engelhardt 2006-01-30 9:32 ` Andrew Morton 2006-01-30 9:46 ` Arjan van de Ven 2006-01-30 9:51 ` Jan Engelhardt 2006-01-30 9:54 ` Arjan van de Ven 2006-01-30 10:25 ` Jan Engelhardt 2006-01-30 10:31 ` Arjan van de Ven 2006-01-30 10:36 ` Jan Engelhardt 2006-01-30 10:39 ` Arjan van de Ven 2006-01-30 10:46 ` Jan Engelhardt
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).