Skip to contentSkip to conversation

2.6.25: Filesystem capabilities 0.18

PATCH New Olaf Dietsche 1 participant 1 message May 12, 2008

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

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
index c509123..51f9a95 100644
@@ -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
index 1e7a11b..225c90e 100644
@@ -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
index 966b73e..8897bf8 100644
@@ -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
index 94f026e..ab37237 100644
@@ -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
index 3fa4e4f..1ba2251 100644
@@ -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
index 09008db..9b50a36 100644
@@ -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
index b7fc55e..757600e 100644
@@ -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
index b84b848..dac9a2b 100644
@@ -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
new file mode 100644
index 0000000..9046c15
@@ -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
index 06d5c94..cd39d94 100644
@@ -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;