When you
setfcaps -c cap_net_admin=p -e /bin/ping
cp /bin/sh /bin/ping
then /bin/ping should lose its file capabilities. This patch probably
will need to be cleaned up, but seems to work as it should.
thanks,
-serge
>From f546dc74fac4bfd6374924a6bf21815d1d857ba8 Mon Sep 17 00:00:00 2001
From: Serge E. Hallyn <[EMAIL PROTECTED]>
Date: Fri, 27 Jul 2007 10:56:10 -0400
Subject: [PATCH RFC] file capabilities: clear fcaps on inode change
When a file with posix capabilities is overwritten, the
file capabilities, like a setuid bit, should be removed.
Signed-off-by: Serge E. Hallyn <[EMAIL PROTECTED]>
---
fs/attr.c | 12 ++++++++++++
fs/nfsd/vfs.c | 4 ++--
fs/open.c | 3 ++-
fs/splice.c | 10 ++++++++++
fs/xattr.c | 27 +++++++++++++++++++++++++++
include/linux/fs.h | 1 +
mm/filemap.c | 24 ++++++++++++++++++++++--
7 files changed, 76 insertions(+), 5 deletions(-)
diff --git a/fs/attr.c b/fs/attr.c
index f8dfc22..e4ca694 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -100,6 +100,9 @@ int inode_setattr(struct inode * inode, struct iattr * attr)
}
EXPORT_SYMBOL(inode_setattr);
+int file_has_capabilities(struct dentry *dentry);
+int remove_file_capabilities(struct dentry *dentry);
+
int notify_change(struct dentry * dentry, struct iattr * attr)
{
struct inode *inode = dentry->d_inode;
@@ -116,6 +119,15 @@ int notify_change(struct dentry * dentry, struct iattr *
attr)
attr->ia_atime = now;
if (!(ia_valid & ATTR_MTIME_SET))
attr->ia_mtime = now;
+ if (ia_valid & ATTR_KILL_CAP) {
+ attr->ia_valid &= ~ATTR_KILL_CAP;
+ ia_valid &= ~ATTR_KILL_CAP;
+ if (file_has_capabilities(dentry)) {
+ error = remove_file_capabilities(dentry);
+ if (error)
+ return error;
+ }
+ }
if (ia_valid & ATTR_KILL_SUID) {
attr->ia_valid &= ~ATTR_KILL_SUID;
if (mode & S_ISUID) {
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index ee96a89..3b3c5b5 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -364,7 +364,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct iattr *iap,
/* Revoke setuid/setgid bit on chown/chgrp */
if ((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid)
- iap->ia_valid |= ATTR_KILL_SUID;
+ iap->ia_valid |= ATTR_KILL_SUID | ATTR_KILL_CAP;
if ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid)
iap->ia_valid |= ATTR_KILL_SGID;
@@ -921,7 +921,7 @@ out:
static void kill_suid(struct dentry *dentry)
{
struct iattr ia;
- ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID;
+ ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_CAP;
mutex_lock(&dentry->d_inode->i_mutex);
notify_change(dentry, &ia);
diff --git a/fs/open.c b/fs/open.c
index 0e2e7b1..1524854 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -658,7 +658,8 @@ static int chown_common(struct dentry * dentry, uid_t user,
gid_t group)
newattrs.ia_gid = group;
}
if (!S_ISDIR(inode->i_mode))
- newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID;
+ newattrs.ia_valid |=
+ ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_CAP;
mutex_lock(&inode->i_mutex);
error = notify_change(dentry, &newattrs);
mutex_unlock(&inode->i_mutex);
diff --git a/fs/splice.c b/fs/splice.c
index e36c003..77a7da5 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -805,6 +805,8 @@ generic_file_splice_write_nolock(struct pipe_inode_info
*pipe, struct file *out,
EXPORT_SYMBOL(generic_file_splice_write_nolock);
+int remove_file_capabilities(struct dentry *dentry);
+int should_remove_cap(struct dentry *);
/**
* generic_file_splice_write - splice data from a pipe to a file
* @pipe: pipe info
@@ -835,6 +837,14 @@ generic_file_splice_write(struct pipe_inode_info *pipe,
struct file *out,
if (err)
return err;
}
+ err = should_remove_cap(out->f_path.dentry);
+ if (unlikely(err)) {
+ mutex_lock(&inode->i_mutex);
+ err = remove_file_capabilities(out->f_path.dentry);
+ mutex_unlock(&inode->i_mutex);
+ if (err)
+ return err;
+ }
ret = splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_file);
if (ret > 0) {
diff --git a/fs/xattr.c b/fs/xattr.c
index 6645b73..0ec2266 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -618,3 +618,30 @@ EXPORT_SYMBOL(generic_getxattr);
EXPORT_SYMBOL(generic_listxattr);
EXPORT_SYMBOL(generic_setxattr);
EXPORT_SYMBOL(generic_removexattr);
+
+int file_has_capabilities(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+
+ if (!inode->i_op || !inode->i_op->getxattr)
+ return 0;
+
+ error = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, NULL, 0);
+ if (error > 0)
+ return 1;
+ return 0;
+}
+
+int remove_file_capabilities(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+
+ if (!inode->i_op || !inode->i_op->removexattr)
+ return 0;
+
+ error = inode->i_op->removexattr(dentry, XATTR_NAME_CAPS);
+
+ return error;
+}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3d99c04..3f4165b 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -335,6 +335,7 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t
offset,
#define ATTR_KILL_SUID 2048
#define ATTR_KILL_SGID 4096
#define ATTR_FILE 8192
+#define ATTR_KILL_CAP 16384
/*
* This is the Inode Attributes structure, used for notify_change(). It
diff --git a/mm/filemap.c b/mm/filemap.c
index c0774b5..041eba6 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1637,6 +1637,19 @@ int should_remove_suid(struct dentry *dentry)
}
EXPORT_SYMBOL(should_remove_suid);
+/*
+ * we always remove file capabilities if the file is overwritten,
+ * regardless which capabilities the writer may have.
+ */
+extern int file_has_capabilities(struct dentry *dentry);
+int should_remove_cap(struct dentry *dentry)
+{
+ if (file_has_capabilities(dentry))
+ return ATTR_KILL_CAP;
+
+ return 0;
+}
+
int __remove_suid(struct dentry *dentry, int kill)
{
struct iattr newattrs;
@@ -1645,14 +1658,21 @@ int __remove_suid(struct dentry *dentry, int kill)
return notify_change(dentry, &newattrs);
}
+int remove_file_capabilities(struct dentry *dentry);
int remove_suid(struct dentry *dentry)
{
int kill = should_remove_suid(dentry);
+ int ret = 0;
if (unlikely(kill))
- return __remove_suid(dentry, kill);
+ ret = __remove_suid(dentry, kill);
+ if (ret)
+ return ret;
- return 0;
+ if (should_remove_cap(dentry))
+ ret = remove_file_capabilities(dentry);
+
+ return ret;
}
EXPORT_SYMBOL(remove_suid);
--
1.5.1.1.GIT
-
To unsubscribe from this list: send the line "unsubscribe
linux-security-module" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html