2.6.25: Filesystem capabilities 0.18
Subsystems (4)
PATCH 1/1 2.6.25: Filesystem capabilities 0.18
This patch implements filesystem capabilities. It allows to run privileged executables without the need for suid root.
Changes:
- updated to 2.6.25
This patch is available at:
<http://www.olafdietsche.de/linux/capability/>
Regards, Olaf.
fs/Kconfig +65 -0
@@ -1445,6 +1445,71 @@ config QNX4FS_RW It's currently broken, so for now: answer N. +config FS_CAPABILITIES + bool "Filesystem capabilities (Experimental)" + depends on EXPERIMENTAL + default n + help + This implementation is likely _not_ POSIX compatible. + + If you say Y here, you will be able to grant selective privileges to + executables on a needed basis. This means for some executables, there + is no need anymore to run as root or as a suid root binary. + + For example, you may drop the SUID bit from ping and grant the + CAP_NET_RAW capability: + # chmod u-s /bin/ping + # chcap cap_net_raw=ep /bin/ping + + Another use would be to run system daemons with their own uid: + # chcap cap_net_bind_service=ei /usr/sbin/named + This sets the effective and inheritable capabilities of named. + + In your startup script: + inhcaps cap_net_bind_service=i bind:bind /usr/sbin/named + + This sets the inheritable set to CAP_NET_BIND_SERVICE, which is + needed in order to bind to port 53, and runs named as user bind + with group bind. + + This allows running named with needed restricted privileges, if the + parent process (root) owns them already. When started by regular + users, named runs without any privileges. + + WARNING: + resize2fs(8) might relocate inodes and thus break fs capabilities. + For this to work you must dump the capability db before you resize + and restore the db afterwards. + + For user space tools see: + <http://www.olafdietsche.de/linux/capability/> + + For libcap and an alternative implementation, based on extended + attributes, see: + <http://www.kernel.org/pub/linux/libs/security/linux-privs/> + + If you're unsure, say N. + +config LIBC_ENABLE_SECURE_HACK + bool "Disable LD_PRELOAD on privileged executables" + depends on FS_CAPABILITIES + default n + help + LD_PRELOAD is a glibc feature, which allows to override system + library functions. But this means also a security hole, through + which an attacker might gain unauthorized privileges. This is + already prevented for SUID and SGID binaries. + + Older GNU libc (pre 2.3.6) didn't know about filesystem + capabilities and didn't disable LD_PRELOAD for privileged + executables, which are not SUID or SGID. This hack sets the + group id to an invalid value and tricks GNU libc into thinking, + this is a SGID binary (unless it is already SUID and/or SGID). + + However, this may break some programs. + + If your libc is older than 2.3.6, say Y. + config ROMFS_FS tristate "ROM file system support" depends on BLOCK
fs/Makefile +2 -1
@@ -64,7 +64,8 @@ obj-y += devpts/ obj-$(CONFIG_PROFILING) += dcookies.o obj-$(CONFIG_DLM) += dlm/ - +obj-$(CONFIG_FS_CAPABILITIES) += fscaps.o + # Do not add any filesystems before this line obj-$(CONFIG_REISERFS_FS) += reiserfs/ obj-$(CONFIG_EXT3_FS) += ext3/ # Before ext2 so root fs can be ext3
fs/attr.c +6 -1
@@ -14,6 +14,7 @@#include <linux/fcntl.h> #include <linux/quotaops.h> #include <linux/security.h> +#include <linux/fscaps.h> /* Taken over from the old code... */
@@ -177,8 +178,12 @@ int notify_change(struct dentry * dentry, struct iattr * attr)if (ia_valid &ATTR_SIZE )up_write (&dentry->d_inode->i_alloc_sem); -if (!error) +if (!error) { +if (ia_valid & (ATTR_KILL_SUID |ATTR_KILL_SGID )) +fscap_drop (inode); +fsnotify_change (dentry, ia_valid); + }return error; }
fs/fscaps.c new file +314 -0
314 lines not on this page · raw message
fs/namespace.c +3 -0
@@ -26,6 +26,7 @@#include <linux/mount.h> #include <linux/ramfs.h> #include <linux/log2.h> +#include <linux/fscaps.h> #include <asm/uaccess.h> #include <asm/unistd.h> #include "pnode.h"
@@ -1194,6 +1195,8 @@ int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd,if ((err =graft_tree (newmnt, nd))) goto unlock; +fscap_mount (newmnt); +if (fslist)/* add to the specified expiration list */ list_add_tail (&newmnt->mnt_expire, fslist);
fs/open.c +4 -0
@@ -27,6 +27,7 @@#include <linux/rcupdate.h> #include <linux/audit.h> #include <linux/falloc.h> +#include <linux/fscaps.h> int vfs_statfs (struct dentry *dentry,struct kstatfs *buf) {
@@ -780,6 +781,9 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, } } +if (flags &O_CREAT ) +fscap_drop (inode); +return f; cleanup_all:
fs/super.c +3 -0
@@ -37,6 +37,7 @@#include <linux/idr.h> #include <linux/kobject.h> #include <linux/mutex.h> +#include <linux/fscaps.h> #include <asm/uaccess.h>
@@ -90,6 +91,7 @@ static struct super_block *alloc_super(struct file_system_type *type) s->s_qcop = sb_quotactl_ops; s->s_op = &default_op; s->s_time_gran =1000000000 ; + s->s_fscaps =NULL ; } out:return s;
@@ -178,6 +180,7 @@ void deactivate_super(struct super_block *s) s->s_count -=S_BIAS -1 ;spin_unlock (&sb_lock);DQUOT_OFF (s); +fscap_umount (s);down_write (&s->s_umount); fs->kill_sb (s);put_filesystem (fs);
include/linux/binfmts.h +4 -0
@@ -37,8 +37,12 @@ struct linux_binprm{int sh_bang;struct file * file;int e_uid, e_gid; +#ifdef CONFIG_FS_CAPABILITIES +kernel_cap_t cap_inheritable, cap_permitted, cap_effective; +#else kernel_cap_t cap_inheritable, cap_permitted;bool cap_effective; +#endif void *security;int argc, envc;char * filename;/* Name of binary as seen by procps */
include/linux/fs.h +1 -0
@@ -1015,6 +1015,7 @@ struct super_block {struct mtd_info *s_mtd;struct list_head s_instances;struct quota_info s_dquot;/* Diskquota specific options */ +struct fscap_info *s_fscaps;/* Filesystem capability stuff */ int s_frozen;wait_queue_head_t s_wait_unfrozen;
include/linux/fscaps.h new file +28 -0
@@ -0,0 +1,28 @@ +/* +* Copyright (c) 2002 Olaf Dietsche +* +* Filesystem capabilities for linux. +*/ + +#ifndef _LINUX_FS_CAPS_H +#define _LINUX_FS_CAPS_H + +struct vfsmount ; +struct super_block ; +struct linux_binprm ; +struct inode ; + +#if defined(CONFIG_FS_CAPABILITIES ) +extern void fscap_mount (struct vfsmount *mnt); +extern void fscap_umount (struct super_block *sb); +extern void fscap_read (struct linux_binprm *bprm); +extern void fscap_drop (struct inode *inode); +#else +/* !CONFIG_FS_CAPABILITIES */ +static inline void fscap_mount (struct vfsmount *mnt) {} +static inline void fscap_umount (struct super_block *sb) {} +static inline void fscap_read (struct linux_binprm *bprm) {} +static inline void fscap_drop (struct inode *inode) {} +#endif + +#endif
security/commoncap.c +23 -0
@@ -24,6 +24,7 @@#include <linux/hugetlb.h> #include <linux/mount.h> #include <linux/sched.h> +#include <linux/fscaps.h> /* Global security state */
@@ -160,7 +161,11 @@ static inline void bprm_clear_caps(struct linux_binprm *bprm) {cap_clear (bprm->cap_inheritable);cap_clear (bprm->cap_permitted); +#ifdef CONFIG_FS_CAPABILITIES +cap_clear (bprm->cap_effective); +#else bprm->cap_effective = false; +#endif }#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
@@ -291,6 +296,7 @@ int cap_inode_killpriv(struct dentry *dentry)static inline int get_file_caps (struct linux_binprm *bprm) {bprm_clear_caps (bprm); +fscap_read (bprm);return 0 ; }#endif
@@ -318,7 +324,11 @@ int cap_bprm_set_security (struct linux_binprm *bprm)cap_set_full (bprm->cap_permitted); } if (bprm->e_uid == 0) +#ifdef CONFIG_FS_CAPABILITIES +cap_set_full (bprm->cap_effective); +#else bprm->cap_effective = true; +#endif }return ret;
@@ -350,6 +360,10 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) current->cap_permitted); } } +#ifdef CONFIG_LIBC_ENABLE_SECURE_HACK +if (bprm->e_uid == current->uid && bprm->e_gid == current->gid) + current->gid =-1 ; +#endif } current->suid = current->euid = current->fsuid = bprm->e_uid;
@@ -360,10 +374,15 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)* capability rules */ if (!is_global_init (current)) { current->cap_permitted = new_permitted; +#ifdef CONFIG_FS_CAPABILITIES + current->cap_effective = +cap_intersect (new_permitted, bprm->cap_effective); +#else if (bprm->cap_effective) current->cap_effective = new_permitted;else cap_clear (current->cap_effective); +#endif }/* AUD: Audit candidate if current->cap_effective is set */
@@ -374,7 +393,11 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)int cap_bprm_secureexec (struct linux_binprm *bprm) {if (current->uid !=0 ) { +#ifdef CONFIG_FS_CAPABILITIES +if (!cap_isclear (current->cap_permitted)) +#else if (bprm->cap_effective) +#endif return 1 ;if (!cap_isclear (bprm->cap_permitted))return 1 ;