[pve-devel] [RFC 14/23] API token: implement permission checks

Fabian Grünbichler f.gruenbichler at proxmox.com
Thu Oct 17 15:14:08 CEST 2019


non-privsep tokens will always return the roles/permissions of their
associated users. privsep tokens will return unfiltered roles, but
filtered permissions.

Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
---
 PVE/AccessControl.pm  | 30 ++++++++++++++++++++++++++++++
 PVE/RPCEnvironment.pm | 27 +++++++++++++++++++++++++--
 2 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/PVE/AccessControl.pm b/PVE/AccessControl.pm
index b5dfe4b..0e7f1f7 100644
--- a/PVE/AccessControl.pm
+++ b/PVE/AccessControl.pm
@@ -1353,10 +1353,25 @@ sub roles {
     my ($cfg, $user, $path) = @_;
 
     # NOTE: we do not consider pools here.
+    # NOTE: for privsep tokens, this does not filter roles by those that the
+    # corresponding user has.
     # Use $rpcenv->permission() for any actual permission checks!
 
     return 'Administrator' if $user eq 'root at pam'; # root can do anything
 
+    if (pve_verify_tokenid($user, 1)) {
+	my $tokenid = $user;
+	my ($username, $token) = split_tokenid($tokenid);
+
+	my $token_info = $cfg->{users}->{$username}->{tokens}->{$token};
+	return () if !$token_info;
+
+	my @user_roles = roles($cfg, $username, $path);
+
+	# return full user privileges
+	return @user_roles if !$token_info->{privsep};
+    }
+
     my $perm = {};
 
     foreach my $p (sort keys %{$cfg->{acl}}) {
@@ -1368,6 +1383,21 @@ sub roles {
 
 	#print "CHECKACL $path $p\n";
 	#print "ACL $path = " . Dumper ($acl);
+	if (my $ri = $acl->{tokens}->{$user}) {
+	    my $new;
+	    foreach my $role (keys %$ri) {
+		my $propagate = $ri->{$role};
+		if ($final || $propagate) {
+		    #print "APPLY ROLE $p $user $role\n";
+		    $new = {} if !$new;
+		    $new->{$role} = 1;
+		}
+	    }
+	    if ($new) {
+		$perm = $new; # overwrite previous settings
+		next;
+	    }
+	}
 
 	if (my $ri = $acl->{users}->{$user}) {
 	    my $new;
diff --git a/PVE/RPCEnvironment.pm b/PVE/RPCEnvironment.pm
index 7e0af70..f378522 100644
--- a/PVE/RPCEnvironment.pm
+++ b/PVE/RPCEnvironment.pm
@@ -30,6 +30,9 @@ my $compile_acl_path = sub {
     $cache->{$user} = {} if !$cache->{$user};
     my $data = $cache->{$user};
 
+    my ($username, undef) = PVE::AccessControl::split_tokenid($user, 1);
+    die "internal error" if $username && !defined($cache->{$username});
+
     if (!$data->{poolroles}) {
 	$data->{poolroles} = {};
 
@@ -76,6 +79,13 @@ my $compile_acl_path = sub {
 	    }
 	}
     }
+
+    if ($username) {
+	# intersect user and token permissions
+	my $user_privs = $cache->{$username}->{privs}->{$path};
+	$privs = { map { $_ => 1 } grep { $user_privs->{$_} } keys %$privs };
+    }
+
     $data->{privs}->{$path} = $privs;
 
     return $privs;
@@ -89,8 +99,21 @@ sub permissions {
 	return $cfg->{roles}->{'Administrator'};
     }
 
-    $user = PVE::AccessControl::verify_username($user, 1);
-    return {} if !$user;
+    if (PVE::AccessControl::pve_verify_tokenid($user, 1)) {
+	my ($username, $token) = PVE::AccessControl::split_tokenid($user);
+	my $cfg = $self->{user_cfg};
+	my $token_info = $cfg->{users}->{$username}->{tokens}->{$token};
+	return {} if !$token_info;
+
+	# ensure cache for user is populated
+	my $user_perms = $self->permissions($username, $path);
+
+	# return user privs for non-privsep tokens
+	return $user_perms if !$token_info->{privsep};
+    } else {
+	$user = PVE::AccessControl::verify_username($user, 1);
+	return {} if !$user;
+    }
 
     my $cache = $self->{aclcache};
     $cache->{$user} = {} if !$cache->{$user};
-- 
2.20.1





More information about the pve-devel mailing list