From 9208cb2349631b1f189d7c337e7a539d21fd0925 Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Mon, 9 Dec 2019 21:35:37 +0100 Subject: [PATCH] support skipping of 2FA --- lam/lib/2factor.inc | 23 ++++++++++++++++++++++- lam/lib/config.inc | 2 +- lam/lib/webauthn.inc | 13 +++++++++++++ lam/templates/config/confmain.php | 4 ++-- lam/templates/lib/500_lam.js | 6 ++++++ 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/lam/lib/2factor.inc b/lam/lib/2factor.inc index 3640ffed..b3176386 100644 --- a/lam/lib/2factor.inc +++ b/lam/lib/2factor.inc @@ -11,6 +11,7 @@ use \htmlStatusMessage; use \htmlDiv; use \LAMException; use Webauthn\PublicKeyCredentialCreationOptions; +use function LAM\LOGIN\WEBAUTHN\hasTokensRegistered; use function LAM\LOGIN\WEBAUTHN\storeNewRegistration; /* @@ -547,6 +548,15 @@ class WebauthnProvider extends BaseProvider { $row->add($loginButton, 12); $errorMessage = new htmlStatusMessage('ERROR', '', _('This service requires a browser with "WebAuthn" support.')); $row->add(new htmlDiv(null, $errorMessage, array('hidden webauthn-error')), 12); + if ($this->config->twoFactorAuthenticationOptional === true) { + include_once __DIR__ . '/webauthn.inc'; + $hasTokens = hasTokensRegistered($userDn); + if (!$hasTokens) { + $skipButton = new htmlButton('skip_webauthn', _('Skip')); + $skipButton->setCSSClasses(array('fullwidth')); + $row->add($skipButton, 12); + } + } $row->add(new htmlJavaScript('window.lam.webauthn.start(\'' . $pathPrefix . '\');'), 0); } @@ -556,8 +566,12 @@ class WebauthnProvider extends BaseProvider { */ public function verify2ndFactor($user, $password, $serial, $twoFactorInput) { logNewMessage(LOG_DEBUG, 'WebauthnProvider: Checking 2nd factor for ' . $user); - $response = base64_decode($_POST['sig_response']); include_once __DIR__ . '/webauthn.inc'; + logNewMessage(LOG_ERR, $user); + if ($this->config->twoFactorAuthenticationOptional && !hasTokensRegistered($user) && ($_POST['sig_response'] === 'skip')) { + return true; + } + $response = base64_decode($_POST['sig_response']); $registrationObject = PublicKeyCredentialCreationOptions::createFromString($_SESSION['webauthn_registration']); if (storeNewRegistration($registrationObject, $response)) { return true; @@ -634,6 +648,7 @@ class TwoFactorProviderService { $tfConfig->isSelfService = true; $tfConfig->twoFactorAuthentication = $profile->twoFactorAuthentication; $tfConfig->twoFactorAuthenticationInsecure = $profile->twoFactorAuthenticationInsecure; + $tfConfig->twoFactorAuthenticationOptional = $profile->twoFactorAuthenticationOptional; if ($tfConfig->twoFactorAuthentication == TwoFactorProviderService::TWO_FACTOR_YUBICO) { $tfConfig->twoFactorAuthenticationURL = explode("\r\n", $profile->twoFactorAuthenticationURL); } @@ -673,6 +688,7 @@ class TwoFactorProviderService { $tfConfig->isSelfService = false; $tfConfig->twoFactorAuthentication = $conf->getTwoFactorAuthentication(); $tfConfig->twoFactorAuthenticationInsecure = $conf->getTwoFactorAuthenticationInsecure(); + $tfConfig->twoFactorAuthenticationOptional = $conf->getTwoFactorAuthenticationOptional(); if ($tfConfig->twoFactorAuthentication == TwoFactorProviderService::TWO_FACTOR_YUBICO) { $tfConfig->twoFactorAuthenticationURL = explode("\r\n", $conf->getTwoFactorAuthenticationURL()); } @@ -741,4 +757,9 @@ class TwoFactorConfiguration { */ public $twoFactorAuthenticationSerialAttributeName = null; + /** + * @var bool 2FA is optional + */ + public $twoFactorAuthenticationOptional = false; + } diff --git a/lam/lib/config.inc b/lam/lib/config.inc index ec339107..53324f34 100644 --- a/lam/lib/config.inc +++ b/lam/lib/config.inc @@ -2480,7 +2480,7 @@ class LAMConfig { * @return bool 2nd factor is optional */ public function getTwoFactorAuthenticationOptional() { - return $this->twoFactorAuthenticationOptional; + return boolval($this->twoFactorAuthenticationOptional); } /** diff --git a/lam/lib/webauthn.inc b/lam/lib/webauthn.inc index b0847854..cf2cb955 100644 --- a/lam/lib/webauthn.inc +++ b/lam/lib/webauthn.inc @@ -274,6 +274,19 @@ function getExtensionOutputChecker() { return new ExtensionOutputCheckerHandler(); } +/** + * Returns if there are any tokens registered for the given DN. + * + * @param string $dn user DN + * @return bool at least one token is registered + */ +function hasTokensRegistered($dn) { + $repository = new PublicKeyCredentialSourceRepositorySQLite(); + $userEntity = getUserEntity($dn); + $tokens = $repository->findAllForUserEntity($userEntity); + return !empty($tokens); +} + /** * Stores the public key credentials in the SQLite database. * diff --git a/lam/templates/config/confmain.php b/lam/templates/config/confmain.php index a99f2d5e..b80d5296 100644 --- a/lam/templates/config/confmain.php +++ b/lam/templates/config/confmain.php @@ -479,7 +479,7 @@ if (extension_loaded('curl')) { TwoFactorProviderService::TWO_FACTOR_YUBICO => array('twoFactorURL', 'twoFactorAttribute', 'twoFactorDomain'), TwoFactorProviderService::TWO_FACTOR_DUO => array('twoFactorURLs', 'twoFactorOptional', 'twoFactorInsecure', 'twoFactorLabel', 'twoFactorDomain'), TwoFactorProviderService::TWO_FACTOR_WEBAUTHN => array('twoFactorURL', 'twoFactorURLs', 'twoFactorInsecure', 'twoFactorLabel', - 'twoFactorOptional', 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorAttribute'), + 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorAttribute'), )); $twoFactorSelect->setTableRowsToShow(array( TwoFactorProviderService::TWO_FACTOR_PRIVACYIDEA => array('twoFactorURL', 'twoFactorInsecure', 'twoFactorLabel', @@ -488,7 +488,7 @@ if (extension_loaded('curl')) { 'twoFactorOptional', 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey'), TwoFactorProviderService::TWO_FACTOR_DUO => array('twoFactorURL', 'twoFactorLabel', 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorAttribute'), - TwoFactorProviderService::TWO_FACTOR_WEBAUTHN => array('twoFactorDomain') + TwoFactorProviderService::TWO_FACTOR_WEBAUTHN => array('twoFactorDomain', 'twoFactorOptional') )); $row->add($twoFactorSelect, 12); $twoFactorAttribute = new htmlResponsiveInputField(_("User name attribute"), 'twoFactorAttribute', $conf->getTwoFactorAuthenticationAttribute(), '528'); diff --git a/lam/templates/lib/500_lam.js b/lam/templates/lib/500_lam.js index b51c55ed..954b1e11 100644 --- a/lam/templates/lib/500_lam.js +++ b/lam/templates/lib/500_lam.js @@ -1377,6 +1377,12 @@ window.lam.webauthn.start = function(prefix) { * @param prefix path prefix for Ajax endpoint */ window.lam.webauthn.run = function(prefix) { + jQuery('#btn_skip_webauthn').click(function () { + let form = jQuery("#2faform"); + form.append(''); + form.submit(); + return; + }); var token = jQuery('#sec_token').val(); // check for webauthn support if (!navigator.credentials || (typeof(PublicKeyCredential) === "undefined")) {