GUI: recover hidden protection without replacing outer credentials

Handle protection failures before outer-password retries, retain cached and
explicit outer credentials without adding default keyfiles, and focus the
hidden-protection controls. Initialize hidden PIM and KDF independently,
and retain an accepted backup header during protection recovery.

Retry embedded backup headers after repeated hidden-protection failures
without advancing the outer-password retry count. Restore the accepted
primary-header selection if backup authentication fails, and report
automatic backup use only when the selected header remains a backup.

Keep primary-header recovery available after an automatic backup attempt
fails with EIO, while reporting the error. Preserve cancellation and
other error handling.

Pass known protection failures from favorite mounts directly into GUI
recovery, including keyfile-only credentials with a null password. Skip
redundant authentication attempts and preserve the text interface's
existing retry path.

Initialize the backup-header checkbox with the other validated controls,
so an initial backup-header request survives the first password prompt.

Conceal the accepted outer password and PIM before disabling their
controls during protection recovery.
This commit is contained in:
Mounir IDRASSI committed 2026-09-25 16:43:25 +02:00
1 parent bc9f7d2899
commit bee7d4f1c8
8 files changed
+159 -40

No files matched your search

+91 -9
View File
@@ -15,6 +15,7 @@
#ifdef TC_UNIX
#include <wx/mimetype.h>
#include <wx/sckipc.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
@@ -905,6 +906,17 @@ namespace VeraCrypt
}
shared_ptr <VolumeInfo> GraphicUserInterface::MountVolume (MountOptions &options, bool tryCachedPasswords) const
{
return MountVolumeInternal (options, tryCachedPasswords, false);
}
shared_ptr <VolumeInfo> GraphicUserInterface::MountVolumeWithProtectionRecovery (MountOptions &options, const PasswordException &protectionError) const
{
ShowWarning (protectionError);
return MountVolumeInternal (options, false, true);
}
shared_ptr <VolumeInfo> GraphicUserInterface::MountVolumeInternal (MountOptions &options, bool tryCachedPasswords, bool protectionRecovery) const
{
CheckRequirementsForMountingVolume();
@@ -937,7 +949,32 @@ namespace VeraCrypt
try
{
if (tryCachedPasswords
bool protectionError = protectionRecovery;
auto mountWithProtectionRecovery = [&] () -> shared_ptr <VolumeInfo>
{
protectionError = false;
try
{
return UserInterface::MountVolume (options);
}
catch (ProtectionPasswordIncorrect &e)
{
ShowWarning (e);
}
catch (ProtectionPasswordKeyfilesIncorrect &e)
{
ShowWarning (e);
}
protectionError = true;
// Keep the accepted outer credential source, including the password
// cache. An explicit empty list prevents default keyfiles being added.
if (!options.Keyfiles)
options.Keyfiles = make_shared <KeyfileList>();
return shared_ptr <VolumeInfo>();
};
if (!protectionError && tryCachedPasswords
&& (!options.Password || options.Password->IsEmpty())
&& (!options.Keyfiles || options.Keyfiles->empty())
&& !Core->IsPasswordCacheEmpty())
@@ -946,21 +983,25 @@ namespace VeraCrypt
try
{
wxBusyCursor busy;
return UserInterface::MountVolume (options);
volume = mountWithProtectionRecovery();
if (volume)
return volume;
}
catch (PasswordException&) { }
}
if (!options.Keyfiles && GetPreferences().UseKeyfiles && !GetPreferences().DefaultKeyfiles.empty())
if (!protectionError && !options.Keyfiles && GetPreferences().UseKeyfiles && !GetPreferences().DefaultKeyfiles.empty())
options.Keyfiles = make_shared <KeyfileList> (GetPreferences().DefaultKeyfiles);
if ((options.Password && !options.Password->IsEmpty())
|| (options.Keyfiles && !options.Keyfiles->empty() && options.Password))
if (!protectionError && ((options.Password && !options.Password->IsEmpty())
|| (options.Keyfiles && !options.Keyfiles->empty() && options.Password)))
{
try
{
wxBusyCursor busy;
return UserInterface::MountVolume (options);
volume = mountWithProtectionRecovery();
if (volume)
return volume;
}
catch (PasswordException&) { }
}
@@ -970,17 +1011,54 @@ namespace VeraCrypt
MountOptionsDialog dialog (GetTopWindow(), options);
int incorrectPasswordCount = 0;
int incorrectProtectionPasswordCount = 0;
bool autoBackupHeaderUsed = false;
while (!volume)
{
dialog.Hide();
dialog.SetProtectionRecovery (protectionError);
if (dialog.ShowModal() != wxID_OK)
return volume;
try
{
wxBusyCursor busy;
volume = UserInterface::MountVolume (options);
volume = mountWithProtectionRecovery();
if (!volume && protectionError && !options.UseBackupHeaders
&& ++incorrectProtectionPasswordCount > 2)
{
// The outer primary header was accepted, but the hidden primary
// header may be damaged. Try the backups once for this attempt.
options.UseBackupHeaders = true;
try
{
volume = UserInterface::MountVolume (options);
autoBackupHeaderUsed = true;
}
catch (PasswordException&)
{
// Keep recovering against the accepted primary outer header.
options.UseBackupHeaders = false;
}
#ifdef TC_UNIX
catch (SystemException &e)
{
options.UseBackupHeaders = false;
if (e.GetErrorCode() != EIO)
throw;
// An unreadable backup must not prevent another primary-header attempt.
ShowWarning (e);
}
#endif
catch (...)
{
options.UseBackupHeaders = false;
throw;
}
}
}
catch (PasswordIncorrect &e)
{
@@ -988,15 +1066,16 @@ namespace VeraCrypt
{
// Try to mount the volume using the backup header
options.UseBackupHeaders = true;
autoBackupHeaderUsed = true;
try
{
volume = UserInterface::MountVolume (options);
ShowWarning ("HEADER_DAMAGED_AUTO_USED_HEADER_BAK");
volume = mountWithProtectionRecovery();
}
catch (...)
{
options.UseBackupHeaders = false;
autoBackupHeaderUsed = false;
ShowWarning (e);
}
}
@@ -1008,6 +1087,9 @@ namespace VeraCrypt
ShowWarning (e);
}
}
if (autoBackupHeaderUsed && options.UseBackupHeaders)
ShowWarning ("HEADER_DAMAGED_AUTO_USED_HEADER_BAK");
}
catch (exception &e)
{