Skip to contentSkip to conversation

2.5.44: Filesystem capabilities

RFC PATCH New Olaf Dietsche 1 participant 1 message Oct 30, 2002

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

RFC PATCH 1/1 2.5.44: Filesystem capabilities

This patch resolves the last issue, access to the capabilities file. Write access is granted for processes, which have the CAP_SETFCAP capability. This is done by wrapping the inode_operations for the root dir and the capability file with appropriate routines.

Although the fs capability patch is complete now, I'm not entirely sure, wether I got everything right. Maybe the security and file system experts may take a look at the code.

As always, I'm running this code myself right now.

Thanks for your time.

Regards, Olaf.

fs/Config.help +47 -0
diff -urN a/fs/Config.help b/fs/Config.help
@@ -1152,3 +1152,50 @@
 
   If unsure, say N.
 
+CONFIG_FS_CAPABILITIES
+  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 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.
+
+  For user space tools see:
+  <http://home.t-online.de/home/olaf.dietsche/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
+  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.
+
+  GNU libc doesn't know about filesystem capabilities yet and doesn'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 this is already
+  SUID and/or SGID). However, this may break some programs.
+
+  If you're unsure, say Y.
fs/Config.in +3 -0
diff -urN a/fs/Config.in b/fs/Config.in
@@ -108,6 +108,9 @@
    define_bool CONFIG_QUOTACTL y
 fi
 
+dep_bool 'Filesystem capabilities (Experimental)' CONFIG_FS_CAPABILITIES $CONFIG_EXPERIMENTAL
+dep_bool '  Disable LD_PRELOAD on privileged executables' CONFIG_LIBC_ENABLE_SECURE_HACK $CONFIG_FS_CAPABILITIES
+
 if [ "$CONFIG_NET" = "y" ]; then
 
    mainmenu_option next_comment
fs/Makefile +3 -2
diff -urN a/fs/Makefile b/fs/Makefile
@@ -6,7 +6,7 @@
 # 
 
 export-objs :=	open.o dcache.o buffer.o bio.o inode.o dquot.o mpage.o aio.o \
-                fcntl.o read_write.o dcookies.o
+                fcntl.o read_write.o dcookies.o fscaps.o
 
 obj-y :=	open.o read_write.o devices.o file_table.o buffer.o \
 		bio.o super.o block_dev.o char_dev.o stat.o exec.o pipe.o \
@@ -41,7 +41,8 @@
 obj-y				+= devpts/
 
 obj-$(CONFIG_PROFILING)		+= dcookies.o
- 
+obj-$(CONFIG_FS_CAPABILITIES)	+= fscaps.o
+
 # Do not add any filesystems before this line
 obj-$(CONFIG_EXT3_FS)		+= ext3/ # Before ext2 so root fs can be ext3
 obj-$(CONFIG_JBD)		+= jbd/
fs/attr.c +4 -0
diff -urN a/fs/attr.c b/fs/attr.c
@@ -13,6 +13,7 @@
 #include <linux/fcntl.h>
 #include <linux/quotaops.h>
 #include <linux/security.h>
+#include <linux/fscaps.h>
 
 /* Taken over from the old code... */
 
@@ -170,6 +171,9 @@
 	}
 	if (!error) {
 		unsigned long dn_mask = setattr_mask(ia_valid);
+		if (ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
+			fscap_drop(inode);
+
 		if (dn_mask)
 			dnotify_parent(dentry, dn_mask);
 	}
fs/fscaps.c +297 -0

297 lines not on this page · raw message

fs/namespace.c +4 -0
diff -urN a/fs/namespace.c b/fs/namespace.c
@@ -19,6 +19,7 @@
 #include <linux/seq_file.h>
 #include <linux/namespace.h>
 #include <linux/namei.h>
+#include <linux/fscaps.h>
 
 #include <asm/uaccess.h>
 
@@ -340,6 +341,7 @@
 		lock_kernel();
 		DQUOT_OFF(sb);
 		acct_auto_close(sb);
+		fscap_umount(sb);
 		unlock_kernel();
 		security_ops->sb_umount_close(mnt);
 		spin_lock(&dcache_lock);
@@ -655,6 +657,8 @@
 
 	mnt->mnt_flags = mnt_flags;
 	err = graft_tree(mnt, nd);
+	if (!err)
+		fscap_mount(mnt);
 unlock:
 	up_write(&current->namespace->sem);
 	mntput(mnt);
fs/open.c +4 -0
diff -urN a/fs/open.c b/fs/open.c
@@ -17,6 +17,7 @@
 #include <linux/namei.h>
 #include <linux/backing-dev.h>
 #include <linux/security.h>
+#include <linux/fscaps.h>
 
 #include <asm/uaccess.h>
 
@@ -665,6 +666,9 @@
 				goto cleanup_all;
 		}
 	}
+
+	if (flags & O_CREAT)
+		fscap_drop(inode);
 
 	return f;
 
fs/super.c +1 -0
diff -urN a/fs/super.c b/fs/super.c
@@ -72,6 +72,7 @@
 		s->s_maxbytes = MAX_NON_LFS;
 		s->dq_op = sb_dquot_ops;
 		s->s_qcop = sb_quotactl_ops;
+		s->s_fscaps = NULL;
 	}
 out:
 	return s;
include/linux/capability.h +4 -0
diff -urN a/include/linux/capability.h b/include/linux/capability.h
@@ -283,6 +283,10 @@
 
 #define CAP_LEASE            28
 
+/* Allow setting capabilities on files */
+
+#define CAP_SETFCAP          29
+
 #ifdef __KERNEL__
 /* 
  * Bounding set
include/linux/fs.h +1 -0
diff -urN a/include/linux/fs.h b/include/linux/fs.h
@@ -665,6 +665,7 @@
 	struct block_device	*s_bdev;
 	struct list_head	s_instances;
 	struct quota_info	s_dquot;	/* Diskquota specific options */
+	struct fscap_info	*s_fscaps;	/* Filesystem capability stuff */
 
 	char s_id[32];				/* Informational name */
 
include/linux/fscaps.h +30 -0
diff -urN a/include/linux/fscaps.h b/include/linux/fscaps.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2002 Olaf Dietsche
+ *
+ * Filesystem capabilities for linux.
+ */
+
+#ifndef _LINUX_FS_CAPS_H
+#define _LINUX_FS_CAPS_H
+
+#include <linux/config.h>
+
+struct vfsmount;
+struct super_block;
+struct linux_binprm;
+struct inode;
+
+#if defined(CONFIG_FS_CAPABILITIES) || defined(CONFIG_FS_CAPABILITIES_MODULE)
+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/capability.c +7 -1
diff -urN a/security/capability.c b/security/capability.c
@@ -18,6 +18,7 @@
 #include <linux/smp_lock.h>
 #include <linux/skbuff.h>
 #include <linux/netlink.h>
+#include <linux/fscaps.h>
 
 /* flag to keep track of how we were registered */
 static int secondary;
@@ -119,11 +120,12 @@
 {
 	/* Copied from fs/exec.c:prepare_binprm. */
 
-	/* We don't have VFS support for capabilities yet */
 	cap_clear (bprm->cap_inheritable);
 	cap_clear (bprm->cap_permitted);
 	cap_clear (bprm->cap_effective);
 
+	fscap_read(bprm);
+
 	/*  To support inheritance of root-permissions and suid-root
 	 *  executables under compatibility mode, we raise all three
 	 *  capability sets for the file.
@@ -184,6 +186,10 @@
 							       cap_permitted);
 			}
 		}
+#ifdef CONFIG_LIBC_ENABLE_SECURE_HACK
+		if (current->euid == current->uid && current->egid == current->gid)
+			current->gid = -1;
+#endif
 		do_unlock = 1;
 	}