From patchwork Wed Jun 24 21:57:07 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Andreas_Gr=C3=BCnbacher?= X-Patchwork-Id: 6670561 Return-Path: X-Original-To: patchwork-linux-nfs@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 953EA9F39B for ; Wed, 24 Jun 2015 22:02:21 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 05F7A20547 for ; Wed, 24 Jun 2015 22:02:20 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 11B832056E for ; Wed, 24 Jun 2015 22:02:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753458AbbFXWCO (ORCPT ); Wed, 24 Jun 2015 18:02:14 -0400 Received: from mail-wi0-f178.google.com ([209.85.212.178]:35959 "EHLO mail-wi0-f178.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752097AbbFXV6A (ORCPT ); Wed, 24 Jun 2015 17:58:00 -0400 Received: by wicnd19 with SMTP id nd19so147383068wic.1; Wed, 24 Jun 2015 14:57:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=JoVYGF+nzL8o3KH+UxVewmXsiWyC2+Zd5i1Wq9x6BKM=; b=fmWGG+Ktc/izuDEfvvx0J7kxS/XLSVN0DiDTFwdn5zJmTIdlqY8i/Ib/HZqvCWPDv5 KyQJAcx2pm7DdFu1NHbP2yZ/YleLUfo6kKdh679/pW+KqfxwcwIrnLmXKdPxbTL/JL72 bF0dinixSvbwacnzHwo8Ptk1On113/cKUh0NWS72rtzjn5orooVUW+GvbnHdMPwAJ0Hf 9KiqUk5Va8TzjOZ1W/gQ5WMNb8Km9dLF26hfy1UaX44F1xFgeFTZ4SH4pUhNIKPrC01s bjQ2HrpbkACz/p7i0t67Y81zpGxMKR5FC7z5vHGF8MovQtP2207Nmz5XdytVv1HRMkwZ ztsA== X-Received: by 10.194.89.225 with SMTP id br1mr61099631wjb.97.1435183078060; Wed, 24 Jun 2015 14:57:58 -0700 (PDT) Received: from nuc.home.com (80-110-112-232.cgn.dynamic.surfer.at. [80.110.112.232]) by mx.google.com with ESMTPSA id lu5sm42559880wjb.9.2015.06.24.14.57.56 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 24 Jun 2015 14:57:57 -0700 (PDT) From: Andreas Gruenbacher X-Google-Original-From: Andreas Gruenbacher To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-nfs@vger.kernel.org, linux-api@vger.kernel.org, samba-technical@lists.samba.org, linux-security-module@vger.kernel.org Cc: "Aneesh Kumar K.V" Subject: [RFC v4 18/31] ext4: Add richacl support Date: Wed, 24 Jun 2015 23:57:07 +0200 Message-Id: <1435183040-22726-19-git-send-email-agruenba@redhat.com> X-Mailer: git-send-email 2.4.2 In-Reply-To: <1435183040-22726-1-git-send-email-agruenba@redhat.com> References: <1435183040-22726-1-git-send-email-agruenba@redhat.com> Sender: linux-nfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org X-Spam-Status: No, score=-8.2 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: "Aneesh Kumar K.V" Support the richacl permission model in ext4. The richacls are stored in "system.richacl" xattrs. Richacls need to be enabled by tune2fs or at file system create time. Signed-off-by: Andreas Gruenbacher --- fs/ext4/Kconfig | 15 ++++ fs/ext4/Makefile | 1 + fs/ext4/acl.c | 6 +- fs/ext4/acl.h | 12 ++-- fs/ext4/file.c | 6 +- fs/ext4/ialloc.c | 7 +- fs/ext4/inode.c | 10 ++- fs/ext4/namei.c | 11 +-- fs/ext4/richacl.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ext4/richacl.h | 47 ++++++++++++ fs/ext4/xattr.c | 6 ++ fs/ext4/xattr.h | 1 + 12 files changed, 314 insertions(+), 19 deletions(-) create mode 100644 fs/ext4/richacl.c create mode 100644 fs/ext4/richacl.h diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index 024f228..644aeef 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -95,3 +95,18 @@ config EXT4_DEBUG If you select Y here, then you will be able to turn on debugging with a command such as: echo 1 > /sys/module/ext4/parameters/mballoc_debug + +config EXT4_FS_RICHACL + bool "Ext4 Rich Access Control Lists (EXPERIMENTAL)" + depends on EXT4_FS + select FS_RICHACL + help + Rich ACLs are an implementation of NFSv4 ACLs, extended by file masks + to fit into the standard POSIX file permission model. They are + designed to work seamlessly locally as well as across the NFSv4 and + CIFS/SMB2 network file system protocols. + + To learn more about Rich ACL, visit + http://acl.bestbits.at/richacl/ + + If you don't know what Rich ACLs are, say N diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile index 75285ea..ea0d539 100644 --- a/fs/ext4/Makefile +++ b/fs/ext4/Makefile @@ -14,3 +14,4 @@ ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o ext4-$(CONFIG_EXT4_FS_ENCRYPTION) += crypto_policy.o crypto.o \ crypto_key.o crypto_fname.o +ext4-$(CONFIG_EXT4_FS_RICHACL) += richacl.o diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index 69b1e73..d965fa6 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -140,7 +140,7 @@ fail: * inode->i_mutex: don't care */ struct posix_acl * -ext4_get_acl(struct inode *inode, int type) +ext4_get_posix_acl(struct inode *inode, int type) { int name_index; char *value = NULL; @@ -234,7 +234,7 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type, } int -ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type) +ext4_set_posix_acl(struct inode *inode, struct posix_acl *acl, int type) { handle_t *handle; int error, retries = 0; @@ -259,7 +259,7 @@ retry: * inode->i_mutex: up (access to inode is still exclusive) */ int -ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) +ext4_init_posix_acl(handle_t *handle, struct inode *inode, struct inode *dir) { struct posix_acl *default_acl, *acl; int error; diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h index da2c795..450b4d1 100644 --- a/fs/ext4/acl.h +++ b/fs/ext4/acl.h @@ -54,17 +54,17 @@ static inline int ext4_acl_count(size_t size) #ifdef CONFIG_EXT4_FS_POSIX_ACL /* acl.c */ -struct posix_acl *ext4_get_acl(struct inode *inode, int type); -int ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type); -extern int ext4_init_acl(handle_t *, struct inode *, struct inode *); +struct posix_acl *ext4_get_posix_acl(struct inode *inode, int type); +int ext4_set_posix_acl(struct inode *inode, struct posix_acl *acl, int type); +extern int ext4_init_posix_acl(handle_t *, struct inode *, struct inode *); #else /* CONFIG_EXT4_FS_POSIX_ACL */ #include -#define ext4_get_acl NULL -#define ext4_set_acl NULL +#define ext4_get_posix_acl NULL +#define ext4_set_posix_acl NULL static inline int -ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) +ext4_init_posix_acl(handle_t *handle, struct inode *inode, struct inode *dir) { return 0; } diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 0613c25..958c2bf 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -29,6 +29,7 @@ #include "ext4_jbd2.h" #include "xattr.h" #include "acl.h" +#include "richacl.h" /* * Called when an inode is released. Note that this is different @@ -644,8 +645,9 @@ const struct inode_operations ext4_file_inode_operations = { .getxattr = generic_getxattr, .listxattr = ext4_listxattr, .removexattr = generic_removexattr, - .get_acl = ext4_get_acl, - .set_acl = ext4_set_acl, + .get_acl = ext4_get_posix_acl, + .set_acl = ext4_set_posix_acl, + .get_richacl = ext4_get_richacl, .fiemap = ext4_fiemap, }; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 1eaa6cb..154b0d1 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -27,6 +27,7 @@ #include "ext4_jbd2.h" #include "xattr.h" #include "acl.h" +#include "richacl.h" #include @@ -1061,7 +1062,11 @@ got: if (err) goto fail_drop; - err = ext4_init_acl(handle, inode, dir); + if (EXT4_IS_RICHACL(dir)) + err = ext4_init_richacl(handle, inode, dir); + else + err = ext4_init_posix_acl(handle, inode, dir); + if (err) goto fail_free_drop; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 0554b0b..84337b3 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -41,6 +41,7 @@ #include "xattr.h" #include "acl.h" #include "truncate.h" +#include "richacl.h" #include @@ -4771,9 +4772,12 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) if (orphan && inode->i_nlink) ext4_orphan_del(NULL, inode); - if (!rc && (ia_valid & ATTR_MODE)) - rc = posix_acl_chmod(inode, inode->i_mode); - + if (!rc && (ia_valid & ATTR_MODE)) { + if (EXT4_IS_RICHACL(inode)) + rc = ext4_richacl_chmod(inode); + else + rc = posix_acl_chmod(inode, inode->i_mode); + } err_out: ext4_std_error(inode->i_sb, error); if (!error) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 814f3be..0ab7077 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -38,6 +38,7 @@ #include "xattr.h" #include "acl.h" +#include "richacl.h" #include /* @@ -3902,8 +3903,9 @@ const struct inode_operations ext4_dir_inode_operations = { .getxattr = generic_getxattr, .listxattr = ext4_listxattr, .removexattr = generic_removexattr, - .get_acl = ext4_get_acl, - .set_acl = ext4_set_acl, + .get_acl = ext4_get_posix_acl, + .set_acl = ext4_set_posix_acl, + .get_richacl = ext4_get_richacl, .fiemap = ext4_fiemap, }; @@ -3913,6 +3915,7 @@ const struct inode_operations ext4_special_inode_operations = { .getxattr = generic_getxattr, .listxattr = ext4_listxattr, .removexattr = generic_removexattr, - .get_acl = ext4_get_acl, - .set_acl = ext4_set_acl, + .get_acl = ext4_get_posix_acl, + .set_acl = ext4_set_posix_acl, + .get_richacl = ext4_get_richacl, }; diff --git a/fs/ext4/richacl.c b/fs/ext4/richacl.c new file mode 100644 index 0000000..6ffb2ae --- /dev/null +++ b/fs/ext4/richacl.c @@ -0,0 +1,211 @@ +/* + * Copyright IBM Corporation, 2010 + * Copyright (C) 2015 Red Hat, Inc. + * Author Aneesh Kumar K.V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#include +#include +#include + +#include "ext4.h" +#include "ext4_jbd2.h" +#include "xattr.h" +#include "acl.h" +#include "richacl.h" + +struct richacl * +ext4_get_richacl(struct inode *inode) +{ + const int name_index = EXT4_XATTR_INDEX_RICHACL; + void *value = NULL; + struct richacl *acl; + int retval; + + if (!IS_RICHACL(inode)) + return ERR_PTR(-EOPNOTSUPP); + acl = get_cached_richacl(inode); + if (acl != ACL_NOT_CACHED) + return acl; + retval = ext4_xattr_get(inode, name_index, "", NULL, 0); + if (retval > 0) { + value = kmalloc(retval, GFP_NOFS); + if (!value) + return ERR_PTR(-ENOMEM); + retval = ext4_xattr_get(inode, name_index, "", value, retval); + } + if (retval > 0) { + acl = richacl_from_xattr(&init_user_ns, value, retval); + if (acl == ERR_PTR(-EINVAL)) + acl = ERR_PTR(-EIO); + } else if (retval == -ENODATA || retval == -ENOSYS) + acl = NULL; + else + acl = ERR_PTR(retval); + kfree(value); + + if (!IS_ERR(acl)) + set_cached_richacl(inode, acl); + + return acl; +} + +static int +ext4_set_richacl(handle_t *handle, struct inode *inode, struct richacl *acl) +{ + const int name_index = EXT4_XATTR_INDEX_RICHACL; + size_t size = 0; + void *value = NULL; + int retval; + + if (acl) { + mode_t mode = inode->i_mode; + if (richacl_equiv_mode(acl, &mode) == 0) { + inode->i_mode = mode; + ext4_mark_inode_dirty(handle, inode); + acl = NULL; + } + } + if (acl) { + size = richacl_xattr_size(acl); + value = kmalloc(size, GFP_NOFS); + if (!value) + return -ENOMEM; + richacl_to_xattr(&init_user_ns, acl, value, size); + } + if (handle) + retval = ext4_xattr_set_handle(handle, inode, name_index, "", + value, size, 0); + else + retval = ext4_xattr_set(inode, name_index, "", value, size, 0); + kfree(value); + if (!retval) + set_cached_richacl(inode, acl); + + return retval; +} + +int +ext4_init_richacl(handle_t *handle, struct inode *inode, struct inode *dir) +{ + struct richacl *acl = richacl_create(inode, dir); + int error; + + error = PTR_ERR(acl); + if (IS_ERR(acl)) + return error; + if (acl) { + error = ext4_set_richacl(handle, inode, acl); + richacl_put(acl); + } + return error; +} + +int +ext4_richacl_chmod(struct inode *inode) +{ + struct richacl *acl; + int retval; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + acl = ext4_get_richacl(inode); + if (IS_ERR_OR_NULL(acl)) + return PTR_ERR(acl); + acl = richacl_chmod(acl, inode->i_mode); + if (IS_ERR(acl)) + return PTR_ERR(acl); + retval = ext4_set_richacl(NULL, inode, acl); + richacl_put(acl); + + return retval; +} + +static size_t +ext4_xattr_list_richacl(struct dentry *dentry, char *list, size_t list_len, + const char *name, size_t name_len, int type) +{ + const size_t size = sizeof(RICHACL_XATTR); + if (!IS_RICHACL(d_inode(dentry))) + return 0; + if (list && size <= list_len) + memcpy(list, RICHACL_XATTR, size); + return size; +} + +static int +ext4_xattr_get_richacl(struct dentry *dentry, const char *name, void *buffer, + size_t buffer_size, int type) +{ + struct richacl *acl; + int error; + + if (strcmp(name, "") != 0) + return -EINVAL; + acl = ext4_get_richacl(d_inode(dentry)); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl == NULL) + return -ENODATA; + + error = richacl_to_xattr(&init_user_ns, acl, buffer, buffer_size); + richacl_put(acl); + return error; +} + +static int +ext4_xattr_set_richacl(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags, int type) +{ + handle_t *handle; + struct richacl *acl = NULL; + int retval, retries = 0; + struct inode *inode = d_inode(dentry); + + if (!IS_RICHACL(d_inode(dentry))) + return -EOPNOTSUPP; + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + if (strcmp(name, "") != 0) + return -EINVAL; + if (!uid_eq(current_fsuid(), inode->i_uid) && + inode_permission(inode, MAY_CHMOD) && + !capable(CAP_FOWNER)) + return -EPERM; + if (value) { + acl = richacl_from_xattr(&init_user_ns, value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + + inode->i_mode &= ~S_IRWXUGO; + inode->i_mode |= richacl_masks_to_mode(acl); + } + +retry: + handle = ext4_journal_start(inode, EXT4_HT_XATTR, + EXT4_DATA_TRANS_BLOCKS(inode->i_sb)); + if (IS_ERR(handle)) + return PTR_ERR(handle); + retval = ext4_set_richacl(handle, inode, acl); + ext4_journal_stop(handle); + if (retval == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) + goto retry; + richacl_put(acl); + return retval; +} + +const struct xattr_handler ext4_richacl_xattr_handler = { + .prefix = RICHACL_XATTR, + .list = ext4_xattr_list_richacl, + .get = ext4_xattr_get_richacl, + .set = ext4_xattr_set_richacl, +}; diff --git a/fs/ext4/richacl.h b/fs/ext4/richacl.h new file mode 100644 index 0000000..09a5cad --- /dev/null +++ b/fs/ext4/richacl.h @@ -0,0 +1,47 @@ +/* + * Copyright IBM Corporation, 2010 + * Copyright (C) 2015 Red Hat, Inc. + * Author Aneesh Kumar K.V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef __FS_EXT4_RICHACL_H +#define __FS_EXT4_RICHACL_H + +#include + +#ifdef CONFIG_EXT4_FS_RICHACL + +#define EXT4_IS_RICHACL(inode) IS_RICHACL(inode) + +extern struct richacl *ext4_get_richacl(struct inode *); +extern int ext4_init_richacl(handle_t *, struct inode *, struct inode *); +extern int ext4_richacl_chmod(struct inode *); + +#else /* CONFIG_FS_EXT4_RICHACL */ + +#define EXT4_IS_RICHACL(inode) (0) +#define ext4_get_richacl NULL + +static inline int +ext4_init_richacl(handle_t *handle, struct inode *inode, struct inode *dir) +{ + return 0; +} + +static inline int +ext4_richacl_chmod(struct inode *inode) +{ + return 0; +} + +#endif /* CONFIG_FS_EXT4_RICHACL */ +#endif /* __FS_EXT4_RICHACL_H */ diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 16e28c0..c1dee9b 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -99,6 +99,9 @@ static const struct xattr_handler *ext4_xattr_handler_map[] = { #ifdef CONFIG_EXT4_FS_SECURITY [EXT4_XATTR_INDEX_SECURITY] = &ext4_xattr_security_handler, #endif +#ifdef CONFIG_EXT4_FS_RICHACL + [EXT4_XATTR_INDEX_RICHACL] = &ext4_richacl_xattr_handler, +#endif }; const struct xattr_handler *ext4_xattr_handlers[] = { @@ -111,6 +114,9 @@ const struct xattr_handler *ext4_xattr_handlers[] = { #ifdef CONFIG_EXT4_FS_SECURITY &ext4_xattr_security_handler, #endif +#ifdef CONFIG_EXT4_FS_RICHACL + &ext4_richacl_xattr_handler, +#endif NULL }; diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index ddc0957..f315493 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h @@ -98,6 +98,7 @@ struct ext4_xattr_ibody_find { extern const struct xattr_handler ext4_xattr_user_handler; extern const struct xattr_handler ext4_xattr_trusted_handler; extern const struct xattr_handler ext4_xattr_security_handler; +extern const struct xattr_handler ext4_richacl_xattr_handler; #define EXT4_XATTR_NAME_ENCRYPTION_CONTEXT "c"