]> git.proxmox.com Git - pve-access-control.git/commitdiff
authenticate_2nd_new: only lock tfa config for recovery keys
authorDominik Csapak <d.csapak@proxmox.com>
Fri, 21 Oct 2022 08:31:15 +0000 (10:31 +0200)
committerWolfgang Bumiller <w.bumiller@proxmox.com>
Fri, 21 Oct 2022 08:44:03 +0000 (10:44 +0200)
we currently only need to lock the tfa config when we got a recovery key
as a tfa challenge response, since that's the only thing that can
actually change the tfa config (every other method only reads from
there).

so to do that, factor out the code that was inside the lock, and call it
with/without lock depending on the tfa challenge response

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
src/PVE/AccessControl.pm

index c32dcc303fabc2498ae0cdfc16c4759962213690..f4ea35847b9d3c910c8c9e641c43df37858f5607 100644 (file)
@@ -786,79 +786,90 @@ sub authenticate_2nd_old : prototype($$$) {
     return wantarray ? ($username, $tfa_data) : $username;
 }
 
-# Returns a tfa challenge or undef.
-sub authenticate_2nd_new : prototype($$$$) {
+sub authenticate_2nd_new_do : prototype($$$$) {
     my ($username, $realm, $otp, $tfa_challenge) = @_;
+    my ($tfa_cfg, $realm_tfa) = user_get_tfa($username, $realm, 1);
 
-    my $result = lock_tfa_config(sub {
-       my ($tfa_cfg, $realm_tfa) = user_get_tfa($username, $realm, 1);
-
-       if (!defined($tfa_cfg)) {
-           return undef;
-       }
-
-       my $realm_type = $realm_tfa && $realm_tfa->{type};
-       # verify realm type unless using recovery keys:
-       if (defined($realm_type)) {
-           $realm_type = 'totp' if $realm_type eq 'oath'; # we used to call it that
-           if ($realm_type eq 'yubico') {
-               # Yubico auth will not be supported in rust for now...
-               if (!defined($tfa_challenge)) {
-                   my $challenge = { yubico => JSON::true };
-                   # Even with yubico auth we do allow recovery keys to be used:
-                   if (my $recovery = $tfa_cfg->recovery_state($username)) {
-                       $challenge->{recovery} = $recovery;
-                   }
-                   return to_json($challenge);
-               }
+    if (!defined($tfa_cfg)) {
+       return undef;
+    }
 
-               if ($otp =~ /^yubico:(.*)$/) {
-                   $otp = $1;
-                   # Defer to after unlocking the TFA config:
-                   return sub {
-                       authenticate_yubico_new(
-                           $tfa_cfg, $username, $realm_tfa, $tfa_challenge, $otp,
-                       );
-                   };
+    my $realm_type = $realm_tfa && $realm_tfa->{type};
+    # verify realm type unless using recovery keys:
+    if (defined($realm_type)) {
+       $realm_type = 'totp' if $realm_type eq 'oath'; # we used to call it that
+       if ($realm_type eq 'yubico') {
+           # Yubico auth will not be supported in rust for now...
+           if (!defined($tfa_challenge)) {
+               my $challenge = { yubico => JSON::true };
+               # Even with yubico auth we do allow recovery keys to be used:
+               if (my $recovery = $tfa_cfg->recovery_state($username)) {
+                   $challenge->{recovery} = $recovery;
                }
+               return to_json($challenge);
            }
 
-           my $response_type;
-           if (defined($otp)) {
-               if ($otp !~ /^([^:]+):/) {
-                   die "bad otp response\n";
-               }
-               $response_type = $1;
+           if ($otp =~ /^yubico:(.*)$/) {
+               $otp = $1;
+               # Defer to after unlocking the TFA config:
+               return sub {
+                   authenticate_yubico_new(
+                       $tfa_cfg, $username, $realm_tfa, $tfa_challenge, $otp,
+                   );
+               };
            }
+       }
 
-           die "realm requires $realm_type authentication\n"
-               if $response_type && $response_type ne 'recovery' && $response_type ne $realm_type;
+       my $response_type;
+       if (defined($otp)) {
+           if ($otp !~ /^([^:]+):/) {
+               die "bad otp response\n";
+           }
+           $response_type = $1;
        }
 
-       configure_u2f_and_wa($tfa_cfg);
+       die "realm requires $realm_type authentication\n"
+           if $response_type && $response_type ne 'recovery' && $response_type ne $realm_type;
+    }
 
-       my $must_save = 0;
-       if (defined($tfa_challenge)) {
-           $tfa_challenge = verify_ticket($tfa_challenge, 0, $username);
-           $must_save = $tfa_cfg->authentication_verify($username, $tfa_challenge, $otp);
-           $tfa_challenge = undef;
-       } else {
-           $tfa_challenge = $tfa_cfg->authentication_challenge($username);
-           if (defined($otp)) {
-               if (defined($tfa_challenge)) {
-                   $must_save = $tfa_cfg->authentication_verify($username, $tfa_challenge, $otp);
-               } else {
-                   die "no such challenge\n";
-               }
+    configure_u2f_and_wa($tfa_cfg);
+
+    my $must_save = 0;
+    if (defined($tfa_challenge)) {
+       $tfa_challenge = verify_ticket($tfa_challenge, 0, $username);
+       $must_save = $tfa_cfg->authentication_verify($username, $tfa_challenge, $otp);
+       $tfa_challenge = undef;
+    } else {
+       $tfa_challenge = $tfa_cfg->authentication_challenge($username);
+       if (defined($otp)) {
+           if (defined($tfa_challenge)) {
+               $must_save = $tfa_cfg->authentication_verify($username, $tfa_challenge, $otp);
+           } else {
+               die "no such challenge\n";
            }
        }
+    }
 
-       if ($must_save) {
-           cfs_write_file('priv/tfa.cfg', $tfa_cfg);
-       }
+    if ($must_save) {
+       cfs_write_file('priv/tfa.cfg', $tfa_cfg);
+    }
 
-       return $tfa_challenge;
-    });
+    return $tfa_challenge;
+}
+
+# Returns a tfa challenge or undef.
+sub authenticate_2nd_new : prototype($$$$) {
+    my ($username, $realm, $otp, $tfa_challenge) = @_;
+
+    my $result;
+
+    if (defined($otp) && $otp =~ m/^recovery:/) {
+       $result = lock_tfa_config(sub {
+           authenticate_2nd_new_do($username, $realm, $otp, $tfa_challenge);
+       });
+    } else {
+       $result = authenticate_2nd_new_do($username, $realm, $otp, $tfa_challenge);
+    }
 
     # Yubico auth returns the authentication sub:
     if (ref($result) eq 'CODE') {