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