From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id BCD80707BB for ; Fri, 3 Jun 2022 13:51:41 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id B312B70B0 for ; Fri, 3 Jun 2022 13:51:11 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id EB40C70A5 for ; Fri, 3 Jun 2022 13:51:10 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id C11A543A09 for ; Fri, 3 Jun 2022 13:51:10 +0200 (CEST) From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= To: pve-devel@lists.proxmox.com Date: Fri, 3 Jun 2022 13:50:49 +0200 Message-Id: <20220603115049.1908792-4-f.gruenbichler@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220603115049.1908792-1-f.gruenbichler@proxmox.com> References: <20220603115049.1908792-1-f.gruenbichler@proxmox.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.171 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record T_SCC_BODY_TEXT_LINE -0.01 - URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [rpcenvironment.pm] Subject: [pve-devel] [PATCH access-control 3/3] permissions: add some more comments X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 03 Jun 2022 11:51:41 -0000 for future reference/changes. Signed-off-by: Fabian Grünbichler --- src/PVE/RPCEnvironment.pm | 40 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/PVE/RPCEnvironment.pm b/src/PVE/RPCEnvironment.pm index a0c7555..08e39ff 100644 --- a/src/PVE/RPCEnvironment.pm +++ b/src/PVE/RPCEnvironment.pm @@ -23,15 +23,18 @@ my $compile_acl_path = sub { return undef if !$cfg->{roles}; + # permissions() has an early return for this case die "internal error" if $user eq 'root@pam'; my $cache = $self->{aclcache}; $cache->{$user} = {} if !$cache->{$user}; my $data = $cache->{$user}; + # permissions() will always prime the cache for the owning user my ($username, undef) = PVE::AccessControl::split_tokenid($user, 1); die "internal error" if $username && $username ne 'root@pam' && !defined($cache->{$username}); + # resolve and cache roles of the current user/token for all pool ACL paths if (!$data->{poolroles}) { $data->{poolroles} = {}; @@ -52,37 +55,54 @@ my $compile_acl_path = sub { } } + # get roles of current user/token on checked path - this already handles + # propagation and NoAccess along the path + # + # hash mapping role name to propagation flag value, a key being defined + # means the role is set my $roles = PVE::AccessControl::roles($cfg, $user, $path); # apply roles inherited from pools - # Note: assume we do not want to propagate those privs if ($data->{poolroles}->{$path}) { + # NoAccess must not be trumped by pool ACLs if (!defined($roles->{NoAccess})) { if ($data->{poolroles}->{$path}->{NoAccess}) { + # but pool ACL NoAccess trumps regular ACL $roles = { 'NoAccess' => 0 }; } else { foreach my $role (keys %{$data->{poolroles}->{$path}}) { + # only use role from pool ACL if regular ACL didn't already + # set it, and never set propagation for pool-derived ACLs $roles->{$role} = 0 if !defined($roles->{$role}); } } } } + # cache roles $data->{roles}->{$path} = $roles; + # derive privs from set roles - hash mapping privilege name to propagation + # flag value, a key being defined means the priv is set my $privs = {}; foreach my $role (keys %$roles) { if (my $privset = $cfg->{roles}->{$role}) { foreach my $p (keys %$privset) { + # set priv '$p' to propagated iff any of the set roles + # containing it have the propagated flag set $privs->{$p} ||= $roles->{$role}; } } } + # intersect user and token permissions if ($username && $username ne 'root@pam') { - # intersect user and token permissions + # map of set privs to their propagation flag value, for the owning user my $user_privs = $cache->{$username}->{privs}->{$path}; + # list of privs set both for token and owning user my $filtered_privs = [ grep { defined($user_privs->{$_}) } keys %$privs ]; + # intersection of privs using filtered list, combining both propagation + # flags $privs = { map { $_ => $user_privs->{$_} && $privs->{$_} } @$filtered_privs }; } @@ -91,11 +111,27 @@ my $compile_acl_path = sub { delete $privs->{$priv} if !defined($privs->{$priv}); } + # cache privs $data->{privs}->{$path} = $privs; return $privs; }; +# this is the method used by permission check helpers below +# +# returned value is a hash mapping all set privileges on $path to their +# respective propagation flag. the propagation flag is informational only - +# actual propagation is handled in PVE::AccessControl::roles(). to determine +# whether a privilege is set, check for definedness in the returned hash. +# +# compiled ACLs are cached, so repeated checks for the same path and user are +# almost free. +# +# if $user is a tokenid, permissions are calculated depending on the +# privilege-separation flag value: +# - non-priv-separated: permissions for owning user are returned +# - priv-separated: permissions for owning user are calculated and intersected +# with those of token sub permissions { my ($self, $user, $path) = @_; -- 2.30.2