android_kernel_oneplus_msm8998/fs/gfs2/acl.c
Jan Kara 49b60d4aa9 BACKPORT: posix_acl: Clear SGID bit when setting file permissions
(cherry pick from commit 073931017b49d9458aa351605b43a7e34598caef)

When file permissions are modified via chmod(2) and the user is not in
the owning group or capable of CAP_FSETID, the setgid bit is cleared in
inode_change_ok().  Setting a POSIX ACL via setxattr(2) sets the file
permissions as well as the new ACL, but doesn't clear the setgid bit in
a similar way; this allows to bypass the check in chmod(2).  Fix that.

NB: We did not resolve the ACL leak in this CL, require additional
    upstream fix.

References: CVE-2016-7097
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Bug: 32458736
Change-Id: I19591ad452cc825ac282b3cfd2daaa72aa9a1ac1
2017-02-07 15:21:07 +00:00

111 lines
2.3 KiB
C

/*
* Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2.
*/
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/xattr.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
#include <linux/gfs2_ondisk.h>
#include "gfs2.h"
#include "incore.h"
#include "acl.h"
#include "xattr.h"
#include "glock.h"
#include "inode.h"
#include "meta_io.h"
#include "trans.h"
#include "util.h"
static const char *gfs2_acl_name(int type)
{
switch (type) {
case ACL_TYPE_ACCESS:
return GFS2_POSIX_ACL_ACCESS;
case ACL_TYPE_DEFAULT:
return GFS2_POSIX_ACL_DEFAULT;
}
return NULL;
}
struct posix_acl *gfs2_get_acl(struct inode *inode, int type)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct posix_acl *acl;
const char *name;
char *data;
int len;
if (!ip->i_eattr)
return NULL;
name = gfs2_acl_name(type);
if (name == NULL)
return ERR_PTR(-EINVAL);
len = gfs2_xattr_acl_get(ip, name, &data);
if (len < 0)
return ERR_PTR(len);
if (len == 0)
return NULL;
acl = posix_acl_from_xattr(&init_user_ns, data, len);
kfree(data);
return acl;
}
int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
int error;
int len;
char *data;
const char *name = gfs2_acl_name(type);
BUG_ON(name == NULL);
if (acl && acl->a_count > GFS2_ACL_MAX_ENTRIES(GFS2_SB(inode)))
return -E2BIG;
if (type == ACL_TYPE_ACCESS) {
umode_t mode = inode->i_mode;
error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
if (error)
return error;
if (mode != inode->i_mode)
mark_inode_dirty(inode);
}
if (acl) {
len = posix_acl_to_xattr(&init_user_ns, acl, NULL, 0);
if (len == 0)
return 0;
data = kmalloc(len, GFP_NOFS);
if (data == NULL)
return -ENOMEM;
error = posix_acl_to_xattr(&init_user_ns, acl, data, len);
if (error < 0)
goto out;
} else {
data = NULL;
len = 0;
}
error = __gfs2_xattr_set(inode, name, data, len, 0, GFS2_EATYPE_SYS);
if (error)
goto out;
set_cached_acl(inode, type, acl);
out:
kfree(data);
return error;
}