]> git.proxmox.com Git - pve-access-control.git/blobdiff - PVE/AccessControl.pm
user.cfg: sort group and pool members, role privs
[pve-access-control.git] / PVE / AccessControl.pm
index 512fcd2dddcd8844a5edf18cedb5d56411512739..1ba1596668e383ef6dac9ac4a38d63225978b1cb 100644 (file)
@@ -47,9 +47,8 @@ my $pve_auth_key_files = {
 
 my $pve_auth_key_cache = {};
 
-my $ticket_lifetime = 3600*2; # 2 hours
-# TODO: set to 24h for PVE 6.0
-my $authkey_lifetime = 3600*0; # rotation disabled
+my $ticket_lifetime = 3600 * 2; # 2 hours
+my $authkey_lifetime = 3600 * 24; # rotate every 24 hours
 
 Crypt::OpenSSL::RSA->import_random_seed();
 
@@ -167,18 +166,21 @@ sub rotate_authkey {
        return if check_authkey();
 
        my $old = get_pubkey();
+       my $new = Crypt::OpenSSL::RSA->generate_key(2048);
 
        if ($old) {
            eval {
                my $pem = $old->get_public_key_x509_string();
+               # mtime is used for caching and ticket age range calculation
                PVE::Tools::file_set_contents($pve_auth_key_files->{pubold}, $pem);
            };
            die "Failed to store old auth key: $@\n" if $@;
        }
 
-       my $new = Crypt::OpenSSL::RSA->generate_key(2048);
        eval {
            my $pem = $new->get_public_key_x509_string();
+           # mtime is used for caching and ticket age range calculation,
+           # should be close to that of pubold above
            PVE::Tools::file_set_contents($pve_auth_key_files->{pub}, $pem);
        };
        if ($@) {
@@ -210,10 +212,12 @@ sub rotate_authkey {
 }
 
 my $csrf_prevention_secret;
+my $csrf_prevention_secret_legacy;
 my $get_csrfr_secret = sub {
     if (!$csrf_prevention_secret) {
        my $input = PVE::Tools::file_get_contents($pve_www_key_fn);
-       $csrf_prevention_secret = Digest::SHA::sha1_base64($input);
+       $csrf_prevention_secret = Digest::SHA::hmac_sha256_base64($input);
+       $csrf_prevention_secret_legacy = Digest::SHA::sha1_base64($input);
     }
     return $csrf_prevention_secret;
 };
@@ -229,7 +233,16 @@ sub assemble_csrf_prevention_token {
 sub verify_csrf_prevention_token {
     my ($username, $token, $noerr) = @_;
 
-    my $secret =  &$get_csrfr_secret();
+    my $secret = $get_csrfr_secret->();
+
+    # FIXME: remove with PVE 7 and/or refactor all into PVE::Ticket ?
+    if ($token =~ m/^([A-Z0-9]{8}):(\S+)$/) {
+       my $sig = $2;
+       if (length($sig) == 27) {
+           # the legacy secret got populated by above get_csrfr_secret call
+           $secret = $csrf_prevention_secret_legacy;
+       }
+    }
 
     return PVE::Ticket::verify_csrf_prevention_token(
        $secret, $username, $token, -300, $ticket_lifetime, $noerr);
@@ -284,7 +297,7 @@ sub verify_ticket {
        return undef if !$rsa_pub;
 
        my ($min, $max) = $get_ticket_age_range->($now, $rsa_mtime, $old);
-       return undef if !$min;
+       return undef if !defined($min);
 
        return PVE::Ticket::verify_rsa_ticket(
            $rsa_pub, 'PVE', $ticket, undef, $min, $max, 1);
@@ -952,6 +965,8 @@ sub parse_user_config {
        } elsif ($et eq 'acl') {
            my ($propagate, $pathtxt, $uglist, $rolelist) = @data;
 
+           $propagate = $propagate ? 1 : 0;
+
            if (my $path = normalize_path($pathtxt)) {
                foreach my $role (split_list($rolelist)) {
 
@@ -961,8 +976,9 @@ sub parse_user_config {
                    }
 
                    foreach my $ug (split_list($uglist)) {
-                       if ($ug =~ m/^@(\S+)$/) {
-                           my $group = $1;
+                       my ($group) = $ug =~ m/^@(\S+)$/;
+
+                       if ($group && verify_groupname($group, 1)) {
                            if ($cfg->{groups}->{$group}) { # group exists
                                $cfg->{acl}->{$path}->{groups}->{$group}->{$role} = $propagate;
                            } else {
@@ -1035,7 +1051,7 @@ sub write_user_config {
 
     my $data = '';
 
-    foreach my $user (keys %{$cfg->{users}}) {
+    foreach my $user (sort keys %{$cfg->{users}}) {
        my $d = $cfg->{users}->{$user};
        my $firstname = $d->{firstname} ? PVE::Tools::encode_text($d->{firstname}) : '';
        my $lastname = $d->{lastname} ? PVE::Tools::encode_text($d->{lastname}) : '';
@@ -1049,30 +1065,30 @@ sub write_user_config {
 
     $data .= "\n";
 
-    foreach my $group (keys %{$cfg->{groups}}) {
+    foreach my $group (sort keys %{$cfg->{groups}}) {
        my $d = $cfg->{groups}->{$group};
-       my $list = join (',', keys %{$d->{users}});
+       my $list = join (',', sort keys %{$d->{users}});
        my $comment = $d->{comment} ? PVE::Tools::encode_text($d->{comment}) : '';
        $data .= "group:$group:$list:$comment:\n";
     }
 
     $data .= "\n";
 
-    foreach my $pool (keys %{$cfg->{pools}}) {
+    foreach my $pool (sort keys %{$cfg->{pools}}) {
        my $d = $cfg->{pools}->{$pool};
-       my $vmlist = join (',', keys %{$d->{vms}});
-       my $storelist = join (',', keys %{$d->{storage}});
+       my $vmlist = join (',', sort keys %{$d->{vms}});
+       my $storelist = join (',', sort keys %{$d->{storage}});
        my $comment = $d->{comment} ? PVE::Tools::encode_text($d->{comment}) : '';
        $data .= "pool:$pool:$comment:$vmlist:$storelist:\n";
     }
 
     $data .= "\n";
 
-    foreach my $role (keys %{$cfg->{roles}}) {
+    foreach my $role (sort keys %{$cfg->{roles}}) {
        next if $special_roles->{$role};
 
        my $d = $cfg->{roles}->{$role};
-       my $list = join (',', keys %$d);
+       my $list = join (',', sort keys %$d);
        $data .= "role:$role:$list:\n";
     }
 
@@ -1121,11 +1137,11 @@ sub write_user_config {
        }
 
        foreach my $rolelist (sort keys %{$ra->{0}}) {
-           my $uglist = join (',', keys %{$ra->{0}->{$rolelist}});
+           my $uglist = join (',', sort keys %{$ra->{0}->{$rolelist}});
            $data .= "acl:0:$path:$uglist:$rolelist:\n";
        }
        foreach my $rolelist (sort keys %{$ra->{1}}) {
-           my $uglist = join (',', keys %{$ra->{1}->{$rolelist}});
+           my $uglist = join (',', sort keys %{$ra->{1}->{$rolelist}});
            $data .= "acl:1:$path:$uglist:$rolelist:\n";
        }
     }