EC-312 Fix crash on entering invalid credentials five times on Autofill (#1988)

This commit is contained in:
Federico Maccaroni 2022-07-14 19:17:04 -03:00 committed by GitHub
parent 2d2a883b96
commit d2fbf5bdea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 236 additions and 170 deletions

View file

@ -8,5 +8,6 @@ namespace Bit.App.Abstractions
{ {
void Init(Func<AppOptions> getOptionsFunc, IAccountsManagerHost accountsManagerHost); void Init(Func<AppOptions> getOptionsFunc, IAccountsManagerHost accountsManagerHost);
Task NavigateOnAccountChangeAsync(bool? isAuthed = null); Task NavigateOnAccountChangeAsync(bool? isAuthed = null);
Task LogOutAsync(string userId, bool userInitiated, bool expired);
} }
} }

View file

@ -301,7 +301,7 @@ namespace Bit.App
UpdateThemeAsync(); UpdateThemeAsync();
}; };
Current.MainPage = new NavigationPage(new HomePage(Options)); Current.MainPage = new NavigationPage(new HomePage(Options));
var mainPageTask = _accountsManager.NavigateOnAccountChangeAsync(); _accountsManager.NavigateOnAccountChangeAsync().FireAndForget();
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init(); ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
} }

View file

@ -123,7 +123,11 @@ namespace Bit.App.Utilities.AccountManagement
await _vaultTimeoutService.LockAsync(true); await _vaultTimeoutService.LockAsync(true);
break; break;
case AccountsManagerMessageCommands.LOGOUT: case AccountsManagerMessageCommands.LOGOUT:
await Device.InvokeOnMainThreadAsync(() => LogOutAsync(message.Data as Tuple<string, bool, bool>)); var extras = message.Data as Tuple<string, bool, bool>;
var userId = extras?.Item1;
var userInitiated = extras?.Item2 ?? true;
var expired = extras?.Item3 ?? false;
await Device.InvokeOnMainThreadAsync(() => LogOutAsync(userId, userInitiated, expired));
break; break;
case AccountsManagerMessageCommands.LOGGED_OUT: case AccountsManagerMessageCommands.LOGGED_OUT:
// Clean up old migrated key if they ever log out. // Clean up old migrated key if they ever log out.
@ -181,12 +185,8 @@ namespace Bit.App.Utilities.AccountManagement
}); });
} }
private async Task LogOutAsync(Tuple<string, bool, bool> extras) public async Task LogOutAsync(string userId, bool userInitiated, bool expired)
{ {
var userId = extras?.Item1;
var userInitiated = extras?.Item2 ?? true;
var expired = extras?.Item3 ?? false;
await AppHelpers.LogOutAsync(userId, userInitiated); await AppHelpers.LogOutAsync(userId, userInitiated);
await NavigateOnAccountChangeAsync(); await NavigateOnAccountChangeAsync();
_authService.LogOut(() => _authService.LogOut(() =>

View file

@ -6,6 +6,7 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Bit.Core.Models.Request; using Bit.Core.Models.Request;
using Bit.Core.Utilities;
namespace Bit.Core.Services namespace Bit.Core.Services
{ {
@ -173,7 +174,7 @@ namespace Bit.Core.Services
public void LogOut(Action callback) public void LogOut(Action callback)
{ {
callback.Invoke(); callback.Invoke();
_messagingService.Send("loggedOut"); _messagingService.Send(AccountsManagerMessageCommands.LOGGED_OUT);
} }
public List<TwoFactorProvider> GetSupportedTwoFactorProviders() public List<TwoFactorProvider> GetSupportedTwoFactorProviders()

View file

@ -8,10 +8,12 @@ using Bit.App.Utilities;
using Bit.App.Utilities.AccountManagement; using Bit.App.Utilities.AccountManagement;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Bit.iOS.Autofill.Models; using Bit.iOS.Autofill.Models;
using Bit.iOS.Core.Utilities; using Bit.iOS.Core.Utilities;
using Bit.iOS.Core.Views; using Bit.iOS.Core.Views;
using CoreFoundation;
using CoreNFC; using CoreNFC;
using Foundation; using Foundation;
using UIKit; using UIKit;
@ -35,6 +37,8 @@ namespace Bit.iOS.Autofill
} }
public override void ViewDidLoad() public override void ViewDidLoad()
{
try
{ {
InitApp(); InitApp();
base.ViewDidLoad(); base.ViewDidLoad();
@ -45,8 +49,16 @@ namespace Bit.iOS.Autofill
ExtContext = ExtensionContext ExtContext = ExtensionContext
}; };
} }
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
public override async void PrepareCredentialList(ASCredentialServiceIdentifier[] serviceIdentifiers) public override async void PrepareCredentialList(ASCredentialServiceIdentifier[] serviceIdentifiers)
{
try
{ {
InitAppIfNeeded(); InitAppIfNeeded();
_context.ServiceIdentifiers = serviceIdentifiers; _context.ServiceIdentifiers = serviceIdentifiers;
@ -79,8 +91,16 @@ namespace Bit.iOS.Autofill
} }
} }
} }
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
public override async void ProvideCredentialWithoutUserInteraction(ASPasswordCredentialIdentity credentialIdentity) public override async void ProvideCredentialWithoutUserInteraction(ASPasswordCredentialIdentity credentialIdentity)
{
try
{ {
InitAppIfNeeded(); InitAppIfNeeded();
await _stateService.Value.SetPasswordRepromptAutofillAsync(false); await _stateService.Value.SetPasswordRepromptAutofillAsync(false);
@ -95,8 +115,16 @@ namespace Bit.iOS.Autofill
_context.CredentialIdentity = credentialIdentity; _context.CredentialIdentity = credentialIdentity;
await ProvideCredentialAsync(false); await ProvideCredentialAsync(false);
} }
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
public override async void PrepareInterfaceToProvideCredential(ASPasswordCredentialIdentity credentialIdentity) public override async void PrepareInterfaceToProvideCredential(ASPasswordCredentialIdentity credentialIdentity)
{
try
{ {
InitAppIfNeeded(); InitAppIfNeeded();
if (!await IsAuthed()) if (!await IsAuthed())
@ -105,10 +133,18 @@ namespace Bit.iOS.Autofill
return; return;
} }
_context.CredentialIdentity = credentialIdentity; _context.CredentialIdentity = credentialIdentity;
CheckLock(async () => await ProvideCredentialAsync()); await CheckLockAsync(async () => await ProvideCredentialAsync());
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
} }
public override async void PrepareInterfaceForExtensionConfiguration() public override async void PrepareInterfaceForExtensionConfiguration()
{
try
{ {
InitAppIfNeeded(); InitAppIfNeeded();
_context.Configuring = true; _context.Configuring = true;
@ -117,7 +153,13 @@ namespace Bit.iOS.Autofill
await _accountsManager.NavigateOnAccountChangeAsync(false); await _accountsManager.NavigateOnAccountChangeAsync(false);
return; return;
} }
CheckLock(() => PerformSegue("setupSegue", this)); await CheckLockAsync(() => PerformSegue("setupSegue", this));
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
} }
public void CompleteRequest(string id = null, string username = null, public void CompleteRequest(string id = null, string username = null,
@ -158,6 +200,8 @@ namespace Bit.iOS.Autofill
} }
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender) public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
try
{ {
if (segue.DestinationViewController is UINavigationController navController) if (segue.DestinationViewController is UINavigationController navController)
{ {
@ -188,11 +232,20 @@ namespace Bit.iOS.Autofill
new CustomPresentationControllerDelegate(setupViewController.DismissModalAction); new CustomPresentationControllerDelegate(setupViewController.DismissModalAction);
} }
} }
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
} }
public void DismissLockAndContinue() public void DismissLockAndContinue()
{ {
DismissViewController(false, async () => DismissViewController(false, async () =>
{
try
{ {
if (_context.CredentialIdentity != null) if (_context.CredentialIdentity != null)
{ {
@ -212,10 +265,18 @@ namespace Bit.iOS.Autofill
{ {
PerformSegue("loginListSegue", this); PerformSegue("loginListSegue", this);
} }
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}); });
} }
private async Task ProvideCredentialAsync(bool userInteraction = true) private async Task ProvideCredentialAsync(bool userInteraction = true)
{
try
{ {
var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService", true); var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService", true);
Bit.Core.Models.Domain.Cipher cipher = null; Bit.Core.Models.Domain.Cipher cipher = null;
@ -275,12 +336,18 @@ namespace Bit.iOS.Autofill
CompleteRequest(decCipher.Id, decCipher.Login.Username, decCipher.Login.Password, totpCode); CompleteRequest(decCipher.Id, decCipher.Login.Username, decCipher.Login.Password, totpCode);
} }
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
private async void CheckLock(Action notLockedAction) private async Task CheckLockAsync(Action notLockedAction)
{ {
if (await IsLocked() || await _stateService.Value.GetPasswordRepromptAutofillAsync()) if (await IsLocked() || await _stateService.Value.GetPasswordRepromptAutofillAsync())
{ {
PerformSegue("lockPasswordSegue", this); DispatchQueue.MainQueue.DispatchAsync(() => PerformSegue("lockPasswordSegue", this));
} }
else else
{ {
@ -302,16 +369,22 @@ namespace Bit.iOS.Autofill
private void LogoutIfAuthed() private void LogoutIfAuthed()
{ {
NSRunLoop.Main.BeginInvokeOnMainThread(async () => NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
{
try
{ {
if (await IsAuthed()) if (await IsAuthed())
{ {
await AppHelpers.LogOutAsync(await _stateService.Value.GetActiveUserIdAsync()); await AppHelpers.LogOutAsync(await _stateService.Value.GetActiveUserIdAsync());
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
if (deviceActionService.SystemMajorVersion() >= 12)
{ {
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync(); await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
} }
} }
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
}); });
} }

View file

@ -28,6 +28,7 @@ namespace Bit.iOS.Core.Controllers
private IPlatformUtilsService _platformUtilsService; private IPlatformUtilsService _platformUtilsService;
private IBiometricService _biometricService; private IBiometricService _biometricService;
private IKeyConnectorService _keyConnectorService; private IKeyConnectorService _keyConnectorService;
private IAccountsManager _accountManager;
private bool _isPinProtected; private bool _isPinProtected;
private bool _isPinProtectedWithKey; private bool _isPinProtectedWithKey;
private bool _pinLock; private bool _pinLock;
@ -95,6 +96,7 @@ namespace Bit.iOS.Core.Controllers
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService"); _biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"); _keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
_accountManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
// We re-use the lock screen for autofill extension to verify master password // We re-use the lock screen for autofill extension to verify master password
// when trying to access protected items. // when trying to access protected items.
@ -265,13 +267,7 @@ namespace Bit.iOS.Core.Controllers
} }
if (failed) if (failed)
{ {
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync(); await HandleFailedCredentialsAsync();
if (invalidUnlockAttempts >= 5)
{
await LogOutAsync();
return;
}
InvalidValue();
} }
} }
else else
@ -305,17 +301,22 @@ namespace Bit.iOS.Core.Controllers
await SetKeyAndContinueAsync(key2, true); await SetKeyAndContinueAsync(key2, true);
} }
else else
{
await HandleFailedCredentialsAsync();
}
}
}
private async Task HandleFailedCredentialsAsync()
{ {
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync(); var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
if (invalidUnlockAttempts >= 5) if (invalidUnlockAttempts >= 5)
{ {
await LogOutAsync(); await _accountManager.LogOutAsync(await _stateService.GetActiveUserIdAsync(), false, false);
return; return;
} }
InvalidValue(); InvalidValue();
} }
}
}
public async Task PromptBiometricAsync() public async Task PromptBiometricAsync()
{ {
@ -395,16 +396,6 @@ namespace Bit.iOS.Core.Controllers
PresentViewController(alert, true, null); PresentViewController(alert, true, null);
} }
private async Task LogOutAsync()
{
await AppHelpers.LogOutAsync(await _stateService.GetActiveUserIdAsync());
var authService = ServiceContainer.Resolve<IAuthService>("authService");
authService.LogOut(() =>
{
Cancel?.Invoke();
});
}
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
base.Dispose(disposing); base.Dispose(disposing);