From patchwork Wed Jun 24 21:57:11 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: 6670321 Return-Path: X-Original-To: patchwork-linux-fsdevel@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 DFC459F39B for ; Wed, 24 Jun 2015 21:58:59 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C5E9720547 for ; Wed, 24 Jun 2015 21:58:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 490D520574 for ; Wed, 24 Jun 2015 21:58:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752835AbbFXV6x (ORCPT ); Wed, 24 Jun 2015 17:58:53 -0400 Received: from mail-wg0-f54.google.com ([74.125.82.54]:34245 "EHLO mail-wg0-f54.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752450AbbFXV6H (ORCPT ); Wed, 24 Jun 2015 17:58:07 -0400 Received: by wgqq4 with SMTP id q4so47256387wgq.1; Wed, 24 Jun 2015 14:58:03 -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=yuf1Fa0ECOkPjdTUj/IEla+0BOU6XOJ7/Y4m61EIlNY=; b=WiBttddzQltWcrX/xj6JZReIaQlpRKwBF7Wl6PwSrEO5DUN58Xnhrp9vm7qWidhdwa aJ0ITRmWYbAfgrOSBusXxgOuoTyxMJWu2vadDD9Kyj3V/F0yPrlHJva8n/K3G7tLOuzx EAT7cvBgEPUhm2BdmAk96MGycQj6c2I4mHVPj1QvT8zRDAf3C5SXXnDwkwGI6mS8gCa5 7Q3QukfDShs7FXlOKKrt8S/tJnHxOuax62Tzk1eCxNgsXRyHbixPrZOnQPlvlvIHk+Yl Pvi786xj5BvTzeqF8aQoAcPki2YPg3l9DjBB51O70XuzqPawOZzVNpDoQFi6JITnQjSE rd7A== X-Received: by 10.194.63.16 with SMTP id c16mr75012068wjs.105.1435183083741; Wed, 24 Jun 2015 14:58:03 -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.58.02 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 24 Jun 2015 14:58:03 -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: Andreas Gruenbacher Subject: [RFC v4 22/31] richacl: Propagate everyone@ permissions to other aces Date: Wed, 24 Jun 2015 23:57:11 +0200 Message-Id: <1435183040-22726-23-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-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@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=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 The trailing everyone@ allow ace can grant permissions to all file classes including the owner and group class. Before we can apply the other mask to this entry to turn it into an "other class" entry, we need to ensure that members of the owner or group class will not lose any permissions from that ace. Conceptually, we do this by inserting additional :::allow entries before the trailing everyone@ allow ace with the same permissions as the trailing everyone@ allow ace for owner@, group@, and all explicitly mentioned users and groups. (In practice, we will rarely need to insert any additional aces in this step.) Signed-off-by: Andreas Gruenbacher --- fs/richacl_compat.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c index d813789..c6db72a 100644 --- a/fs/richacl_compat.c +++ b/fs/richacl_compat.c @@ -224,3 +224,184 @@ richacl_move_everyone_aces_down(struct richacl_alloc *alloc) } return 0; } + +/** + * __richacl_propagate_everyone - propagate everyone@ permissions up for @who + * @alloc: acl and number of allocated entries + * @who: identifier to propagate permissions for + * @allow: permissions to propagate up + * + * Propagate the permissions in @allow up from the end of the acl to the start + * for the specified principal @who. + * + * The simplest possible approach to achieve this would be to insert a + * ":::allow" ace before the final everyone@ allow ace. Since this + * would often result in aces which are not needed or which could be merged + * with an existing ace, we make the following optimizations: + * + * - We go through the acl and determine which permissions are already + * allowed or denied to @who, and we remove those permissions from + * @allow. + * + * - If the acl contains an allow ace for @who and no aces after this entry + * deny permissions in @allow, we add the permissions in @allow to this + * ace. (Propagating permissions across a deny ace which can match the + * process can elevate permissions.) + * + * This transformation does not alter the permissions that the acl grants. + */ +static int +__richacl_propagate_everyone(struct richacl_alloc *alloc, struct richace *who, + unsigned int allow) +{ + struct richace *allow_last = NULL, *ace; + + /* + * Remove the permissions from allow that are already determined for + * this who value, and figure out if there is an allow entry for + * this who value that is "reachable" from the trailing everyone@ + * allow ace. + */ + richacl_for_each_entry(ace, alloc->acl) { + if (richace_is_inherit_only(ace)) + continue; + if (richace_is_allow(ace)) { + if (richace_is_same_identifier(ace, who)) { + allow &= ~ace->e_mask; + allow_last = ace; + } + } else if (richace_is_deny(ace)) { + if (richace_is_same_identifier(ace, who)) + allow &= ~ace->e_mask; + else if (allow & ace->e_mask) + allow_last = NULL; + } + } + ace--; + + /* + * If for group class entries, all the remaining permissions will + * remain granted by the trailing everyone@ ace, no additional entry is + * needed. + */ + if (!richace_is_owner(who) && + richace_is_everyone(ace) && richace_is_allow(ace) && + !(allow & ~(ace->e_mask & alloc->acl->a_other_mask))) + allow = 0; + + if (allow) { + if (allow_last) + return richace_change_mask(alloc, &allow_last, + allow_last->e_mask | allow); + else { + struct richace who_copy; + + ace = alloc->acl->a_entries + alloc->acl->a_count - 1; + memcpy(&who_copy, who, sizeof(struct richace)); + if (richacl_insert_entry(alloc, &ace)) + return -1; + memcpy(ace, &who_copy, sizeof(struct richace)); + ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE; + richace_clear_inheritance_flags(ace); + ace->e_mask = allow; + } + } + return 0; +} + +/** + * richacl_propagate_everyone - propagate everyone@ permissions up the acl + * @alloc: acl and number of allocated entries + * + * Make sure that group@ and all other users and groups mentioned in the acl + * will not lose any permissions when finally applying the other mask to the + * everyone@ allow ace at the end of the acl. We modify the permissions of + * existing entries or add new entries before the final everyone@ allow ace to + * achieve that. + * + * Entries for owner@ are ignored here; see richacl_set_owner_permissions(). + * + * For example, the following acl implicitly grants everyone rwpx access: + * + * joe:r::allow + * everyone@:rwpx::allow + * + * When applying mode 0660 to this acl, group@ would lose rwp access, and joe + * would lose wp access even though the mode does not exclude those + * permissions. After propagating the everyone@ permissions, the result for + * applying mode 0660 becomes: + * + * owner@:rwp::allow + * joe:rwp::allow + * group@:rwp::allow + * + * Deny aces complicate the matter. For example, the following acl grants + * everyone but joe write access: + * + * joe:wp::deny + * everyone@:rwpx::allow + * + * When applying mode 0660 to this acl, group@ would lose rwp access, and joe + * would lose r access. After propagating the everyone@ permissions, the + * result for applying mode 0660 becomes: + * + * owner@:rwp::allow + * joe:w::deny + * group@:rwp::allow + * joe:r::allow + */ +static int +richacl_propagate_everyone(struct richacl_alloc *alloc) +{ + struct richace who = { .e_flags = RICHACE_SPECIAL_WHO }; + struct richacl *acl = alloc->acl; + struct richace *ace; + unsigned int group_allow; + + /* + * If the group mask contains permissions which are not in the other + * mask, we may need to propagate permissions up from the everyone@ + * allow ace. + */ + if (!(acl->a_group_mask & ~acl->a_other_mask)) + return 0; + if (!acl->a_count) + return 0; + ace = acl->a_entries + acl->a_count - 1; + if (richace_is_inherit_only(ace) || !richace_is_everyone(ace)) + return 0; + + group_allow = ace->e_mask & acl->a_group_mask; + if (group_allow & ~acl->a_other_mask) { + int n; + + /* Propagate everyone@ permissions through to group@. */ + who.e_id.special = RICHACE_GROUP_SPECIAL_ID; + if (__richacl_propagate_everyone(alloc, &who, group_allow)) + return -1; + acl = alloc->acl; + + /* + * Start from the entry before the trailing everyone@ allow + * entry. We will not hit everyone@ entries in the loop. + */ + for (n = acl->a_count - 2; n != -1; n--) { + ace = acl->a_entries + n; + + if (richace_is_inherit_only(ace) || + richace_is_owner(ace) || + richace_is_group(ace)) + continue; + if (richace_is_allow(ace) || richace_is_deny(ace)) { + /* + * Any inserted entry will end up below the + * current entry + */ + if (__richacl_propagate_everyone(alloc, ace, + group_allow)) + return -1; + } + } + } + return 0; +}