From patchwork Wed Jul 22 13:03:04 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: 6843531 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 D67819F457 for ; Wed, 22 Jul 2015 13:18:57 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id E5564206DB for ; Wed, 22 Jul 2015 13:18:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 53886206D7 for ; Wed, 22 Jul 2015 13:18:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756786AbbGVNE1 (ORCPT ); Wed, 22 Jul 2015 09:04:27 -0400 Received: from mail-wi0-f177.google.com ([209.85.212.177]:33891 "EHLO mail-wi0-f177.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756730AbbGVNEW (ORCPT ); Wed, 22 Jul 2015 09:04:22 -0400 Received: by wibud3 with SMTP id ud3so153261477wib.1; Wed, 22 Jul 2015 06:04:20 -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=CgyzhyRbA1jm+jNNHPPNEWOjrWKIvy2YyElSIF5Abfg=; b=OvNIDa2AxY+GgHVFxnx/TZNMCocmZJo3Xb5nHfjbtFIA9l8KgG6N8KMwhr/SPEgLlZ RmXbPsBUaOezTZmhX/qHjQxe9NoWPJw+xm14r94IgtdzGARVTv7JU5y5BGaOZHqUliDd 1Z865dsUlRV51ozevQF9xANlXUZWl+FlHAiYcFtmp5C8uWPw0fu17KvzulbSp/OqXXmo BF6elQwv7iqEAy62iQFAyNtbl2s69/EA6IT9omNOJjqhEvZnFBhcl7cKRrt2PQ6wmfgB CE5HWIRjBwuJ4iYNSr5fYrkgQP64FlU9Zo0xhyrtRUsMt320GRqZmJ8H80gBQeWlLU6P +vmg== X-Received: by 10.194.58.167 with SMTP id s7mr5025568wjq.38.1437570260743; Wed, 22 Jul 2015 06:04:20 -0700 (PDT) Received: from schleppi.home.com ([149.14.88.26]) by smtp.gmail.com with ESMTPSA id u7sm21992628wif.3.2015.07.22.06.04.19 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 22 Jul 2015 06:04:19 -0700 (PDT) From: Andreas Gruenbacher To: linux-kernel@vger.kernel.org Cc: 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, Andreas Gruenbacher Subject: [PATCH v5 14/39] richacl: Create-time inheritance Date: Wed, 22 Jul 2015 15:03:04 +0200 Message-Id: <1437570209-29832-15-git-send-email-andreas.gruenbacher@gmail.com> X-Mailer: git-send-email 2.4.3 In-Reply-To: <1437570209-29832-1-git-send-email-andreas.gruenbacher@gmail.com> References: <1437570209-29832-1-git-send-email-andreas.gruenbacher@gmail.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.0 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=unavailable 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: Andreas Gruenbacher When a new file is created, it can inherit an acl from its parent directory; this is similar to how default acls work in POSIX (draft) ACLs. As with POSIX ACLs, if a file inherits an acl from its parent directory, the intersection between the create mode and the permissions granted by the inherited acl determines the file masks and file permission bits, and the umask is ignored. Signed-off-by: Andreas Gruenbacher --- fs/richacl_base.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/richacl_inode.c | 62 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 2 ++ 3 files changed, 133 insertions(+) diff --git a/fs/richacl_base.c b/fs/richacl_base.c index 333faa9..ae224b1 100644 --- a/fs/richacl_base.c +++ b/fs/richacl_base.c @@ -469,3 +469,72 @@ richacl_equiv_mode(const struct richacl *acl, mode_t *mode_p) return 0; } EXPORT_SYMBOL_GPL(richacl_equiv_mode); + +/** + * richacl_inherit - compute the inherited acl of a new file + * @dir_acl: acl of the containing directory + * @isdir: inherit by a directory or non-directory? + * + * A directory can have acl entries which files and/or directories created + * inside the directory will inherit. This function computes the acl for such + * a new file. If there is no inheritable acl, it will return %NULL. + */ +struct richacl * +richacl_inherit(const struct richacl *dir_acl, int isdir) +{ + const struct richace *dir_ace; + struct richacl *acl = NULL; + struct richace *ace; + int count = 0; + + if (isdir) { + richacl_for_each_entry(dir_ace, dir_acl) { + if (!richace_is_inheritable(dir_ace)) + continue; + count++; + } + if (!count) + return NULL; + acl = richacl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + ace = acl->a_entries; + richacl_for_each_entry(dir_ace, dir_acl) { + if (!richace_is_inheritable(dir_ace)) + continue; + richace_copy(ace, dir_ace); + if (dir_ace->e_flags & RICHACE_NO_PROPAGATE_INHERIT_ACE) + richace_clear_inheritance_flags(ace); + if ((dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE) && + !(dir_ace->e_flags & RICHACE_DIRECTORY_INHERIT_ACE)) + ace->e_flags |= RICHACE_INHERIT_ONLY_ACE; + ace++; + } + } else { + richacl_for_each_entry(dir_ace, dir_acl) { + if (!(dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE)) + continue; + count++; + } + if (!count) + return NULL; + acl = richacl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + ace = acl->a_entries; + richacl_for_each_entry(dir_ace, dir_acl) { + if (!(dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE)) + continue; + richace_copy(ace, dir_ace); + richace_clear_inheritance_flags(ace); + /* + * RICHACE_DELETE_CHILD is meaningless for + * non-directories, so clear it. + */ + ace->e_mask &= ~RICHACE_DELETE_CHILD; + ace++; + } + } + + return acl; +} diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c index 72c461d..fd6f710 100644 --- a/fs/richacl_inode.c +++ b/fs/richacl_inode.c @@ -191,3 +191,65 @@ out: return denied ? -EACCES : 0; } EXPORT_SYMBOL_GPL(richacl_permission); + +/** + * richacl_inherit_inode - compute inherited acl and file mode + * @dir_acl: acl of the containing directory + * @inode: inode of the new file (create mode in i_mode) + * + * The file permission bits in inode->i_mode must be set to the create mode by + * the caller. + * + * If there is an inheritable acl, the maximum permissions that the acl grants + * will be computed and permissions not granted by the acl will be removed from + * inode->i_mode. If there is no inheritable acl, the umask will be applied + * instead. + */ +static struct richacl * +richacl_inherit_inode(const struct richacl *dir_acl, struct inode *inode) +{ + struct richacl *acl; + mode_t mask; + + acl = richacl_inherit(dir_acl, S_ISDIR(inode->i_mode)); + if (acl) { + mask = inode->i_mode; + if (richacl_equiv_mode(acl, &mask) == 0) { + richacl_put(acl); + acl = NULL; + } else { + richacl_compute_max_masks(acl, inode->i_uid); + /* + * Ensure that the acl will not grant any permissions + * beyond the create mode. + */ + acl->a_flags |= RICHACL_MASKED; + acl->a_owner_mask &= richacl_mode_to_mask(inode->i_mode >> 6); + acl->a_group_mask &= richacl_mode_to_mask(inode->i_mode >> 3); + acl->a_other_mask &= richacl_mode_to_mask(inode->i_mode); + mask = ~S_IRWXUGO | richacl_masks_to_mode(acl); + } + } else + mask = ~current_umask(); + + inode->i_mode &= mask; + return acl; +} + +struct richacl *richacl_create(struct inode *inode, struct inode *dir) +{ + struct richacl *dir_acl, *acl = NULL; + + if (S_ISLNK(inode->i_mode)) + return NULL; + dir_acl = get_richacl(dir); + if (dir_acl) { + if (IS_ERR(dir_acl)) + return dir_acl; + acl = richacl_inherit_inode(dir_acl, inode); + richacl_put(dir_acl); + } else + inode->i_mode &= ~current_umask(); + return acl; +} +EXPORT_SYMBOL_GPL(richacl_create); diff --git a/include/linux/richacl.h b/include/linux/richacl.h index 03876cd..e00bcb4 100644 --- a/include/linux/richacl.h +++ b/include/linux/richacl.h @@ -309,8 +309,10 @@ extern unsigned int richacl_want_to_mask(unsigned int); extern void richacl_compute_max_masks(struct richacl *, kuid_t); extern struct richacl *richacl_chmod(struct richacl *, mode_t); extern int richacl_equiv_mode(const struct richacl *, mode_t *); +extern struct richacl *richacl_inherit(const struct richacl *, int); /* richacl_inode.c */ extern int richacl_permission(struct inode *, const struct richacl *, int); +extern struct richacl *richacl_create(struct inode *, struct inode *); #endif /* __RICHACL_H */