From 1535bf4da645f355a8d41a3fbaa649373e10fd42 Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Mon, 25 Nov 2019 21:07:23 +0100 Subject: [PATCH] webauthn --- lam/help/help.inc | 2 ++ lam/lib/config.inc | 24 ++++++++++++++++++++++- lam/lib/webauthn.inc | 17 +++++++++++----- lam/templates/config/confmain.php | 32 +++++++++++++++++-------------- lam/templates/lib/500_lam.js | 16 ++++++++++++---- lam/templates/misc/ajax.php | 2 +- lam/tests/lib/LAMConfigTest.php | 11 +++++++++++ 7 files changed, 79 insertions(+), 25 deletions(-) diff --git a/lam/help/help.inc b/lam/help/help.inc index 89f8b3ad..b8f44924 100644 --- a/lam/help/help.inc +++ b/lam/help/help.inc @@ -329,6 +329,8 @@ $helpArray = array ( "Text" => _('This text is displayed as footer on the self service main page.')), "528" => array ("Headline" => _('User name attribute'), "Text" => _('The attribute (e.g. "uid") that contains the user name for the 2-factor service.')), + "529" => array ("Headline" => _('Domain'), + "Text" => _('Please enter the webauthn domain. This is the public domain of the webserver (e.g. "example.com"). Do not include protocol or port.')), "550" => array ("Headline" => _("From address"), "Text" => _("This email address will be set as sender address of all password mails. If empty the system default (php.ini) will be used.")), "551" => array ("Headline" => _("Subject"), diff --git a/lam/lib/config.inc b/lam/lib/config.inc index 076b006a..ec339107 100644 --- a/lam/lib/config.inc +++ b/lam/lib/config.inc @@ -607,6 +607,7 @@ class LAMConfig { private $twoFactorAuthenticationURL = 'https://localhost'; private $twoFactorAuthenticationClientId = null; private $twoFactorAuthenticationSecretKey = null; + private $twoFactorAuthenticationDomain = null; private $twoFactorAuthenticationInsecure = false; private $twoFactorAuthenticationLabel = null; private $twoFactorAuthenticationOptional = false; @@ -625,7 +626,7 @@ class LAMConfig { 'scriptUserName', 'scriptSSHKey', 'scriptSSHKeyPassword', 'twoFactorAuthentication', 'twoFactorAuthenticationURL', 'twoFactorAuthenticationInsecure', 'twoFactorAuthenticationLabel', 'twoFactorAuthenticationOptional', 'twoFactorAuthenticationCaption', 'twoFactorAuthenticationClientId', 'twoFactorAuthenticationSecretKey', - 'twoFactorAuthenticationAttribute', 'referentialIntegrityOverlay' + 'twoFactorAuthenticationDomain', 'twoFactorAuthenticationAttribute', 'referentialIntegrityOverlay' ); @@ -991,6 +992,9 @@ class LAMConfig { if (!in_array("twoFactorAuthenticationSecretKey", $saved)) { array_push($file_array, "\n" . "twoFactorAuthenticationSecretKey: " . $this->twoFactorAuthenticationSecretKey . "\n"); } + if (!in_array("twoFactorAuthenticationDomain", $saved)) { + array_push($file_array, "\n" . "twoFactorAuthenticationDomain: " . $this->twoFactorAuthenticationDomain . "\n"); + } if (!in_array("twoFactorAuthenticationInsecure", $saved)) { array_push($file_array, "\n" . "twoFactorAuthenticationInsecure: " . $this->twoFactorAuthenticationInsecure . "\n"); } @@ -2416,6 +2420,24 @@ class LAMConfig { return $this->twoFactorAuthenticationSecretKey; } + /** + * Sets the domain. + * + * @param string $domain domain + */ + public function setTwoFactorAuthenticationDomain($domain) { + $this->twoFactorAuthenticationDomain = $domain; + } + + /** + * Returns the domain. + * + * @return string domain + */ + public function getTwoFactorAuthenticationDomain() { + return $this->twoFactorAuthenticationDomain; + } + /** * Returns if SSL certificate verification is turned off. * diff --git a/lam/lib/webauthn.inc b/lam/lib/webauthn.inc index 9828b431..283eb0f6 100644 --- a/lam/lib/webauthn.inc +++ b/lam/lib/webauthn.inc @@ -51,10 +51,11 @@ function isRegistered($dn) { * Returns a challenge for a new token. * * @param string $dn DN + * @param bool $isSelfService is executed in self service * @return PublicKeyCredentialCreationOptions challenge */ -function getRegistrationObject($dn) { - $rpEntity = createRpEntry(); +function getRegistrationObject($dn, $isSelfService) { + $rpEntity = createRpEntry($isSelfService); $userEntity = getUserEntity($dn); $challenge = generateRandomPassword(32); $credentialParameters = getCredentialParameters(); @@ -76,13 +77,19 @@ function getRegistrationObject($dn) { /** * Returns the part that identifies the server and application. * + * @param bool $isSelfService is executed in self service * @return PublicKeyCredentialRpEntity relying party entry */ -function createRpEntry() { +function createRpEntry($isSelfService) { + $pathPrefix = $isSelfService ? '../' : ''; + $icon = $pathPrefix . '../graphics/logo136.png'; + if (!$isSelfService) { + $domain = $_SESSION['config']->getTwoFactorAuthenticationDomain(); + } return new PublicKeyCredentialRpEntity( 'LDAP Account Manager', //Name - null, // TODO check if domain is required - null // TODO icon + $domain, + $icon ); } diff --git a/lam/templates/config/confmain.php b/lam/templates/config/confmain.php index 68729b58..a99f2d5e 100644 --- a/lam/templates/config/confmain.php +++ b/lam/templates/config/confmain.php @@ -473,21 +473,22 @@ if (extension_loaded('curl')) { $twoFactorSelect = new htmlResponsiveSelect('twoFactor', $twoFactorOptions, array($conf->getTwoFactorAuthentication()), _('Provider'), '514'); $twoFactorSelect->setHasDescriptiveElements(true); $twoFactorSelect->setTableRowsToHide(array( - TwoFactorProviderService::TWO_FACTOR_NONE => array('twoFactorURL', 'twoFactorURLs', 'twoFactorInsecure', 'twoFactorLabel', - 'twoFactorOptional', 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorAttribute'), - TwoFactorProviderService::TWO_FACTOR_PRIVACYIDEA => array('twoFactorURLs', 'twoFactorClientId', 'twoFactorSecretKey'), - TwoFactorProviderService::TWO_FACTOR_YUBICO => array('twoFactorURL', 'twoFactorAttribute'), - TwoFactorProviderService::TWO_FACTOR_DUO => array('twoFactorURLs', 'twoFactorOptional', 'twoFactorInsecure', 'twoFactorLabel'), - TwoFactorProviderService::TWO_FACTOR_WEBAUTHN => array('twoFactorURL', 'twoFactorURLs', 'twoFactorInsecure', 'twoFactorLabel', - 'twoFactorOptional', 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorAttribute'), + TwoFactorProviderService::TWO_FACTOR_NONE => array('twoFactorURL', 'twoFactorURLs', 'twoFactorInsecure', 'twoFactorLabel', + 'twoFactorOptional', 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorAttribute', 'twoFactorDomain'), + TwoFactorProviderService::TWO_FACTOR_PRIVACYIDEA => array('twoFactorURLs', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorDomain'), + 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'), )); $twoFactorSelect->setTableRowsToShow(array( - TwoFactorProviderService::TWO_FACTOR_PRIVACYIDEA => array('twoFactorURL', 'twoFactorInsecure', 'twoFactorLabel', - 'twoFactorOptional', 'twoFactorCaption', 'twoFactorAttribute'), - TwoFactorProviderService::TWO_FACTOR_YUBICO => array('twoFactorURLs', 'twoFactorInsecure', 'twoFactorLabel', - 'twoFactorOptional', 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey'), - TwoFactorProviderService::TWO_FACTOR_DUO => array('twoFactorURL', 'twoFactorLabel', - 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorAttribute'), + TwoFactorProviderService::TWO_FACTOR_PRIVACYIDEA => array('twoFactorURL', 'twoFactorInsecure', 'twoFactorLabel', + 'twoFactorOptional', 'twoFactorCaption', 'twoFactorAttribute'), + TwoFactorProviderService::TWO_FACTOR_YUBICO => array('twoFactorURLs', 'twoFactorInsecure', 'twoFactorLabel', + 'twoFactorOptional', 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey'), + TwoFactorProviderService::TWO_FACTOR_DUO => array('twoFactorURL', 'twoFactorLabel', + 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorAttribute'), + TwoFactorProviderService::TWO_FACTOR_WEBAUTHN => array('twoFactorDomain') )); $row->add($twoFactorSelect, 12); $twoFactorAttribute = new htmlResponsiveInputField(_("User name attribute"), 'twoFactorAttribute', $conf->getTwoFactorAuthenticationAttribute(), '528'); @@ -500,8 +501,10 @@ if (extension_loaded('curl')) { $row->add($twoFactorUrl, 12); $twoFactorClientId = new htmlResponsiveInputField(_("Client id"), 'twoFactorClientId', $conf->getTwoFactorAuthenticationClientId(), '524'); $row->add($twoFactorClientId, 12); - $twoFactorSecretKey = new htmlResponsiveInputField(_("Secret key"), 'twoFactorSecretKey', $conf->getTwoFactorAuthenticationSecretKey(), '525'); + $twoFactorSecretKey = new htmlResponsiveInputField(_("Secret key"), 'twoFactorSecretKey', $conf->getTwoFactorAuthenticationSecretKey(), '528'); $row->add($twoFactorSecretKey, 12); + $twoFactorDomain = new htmlResponsiveInputField(_("Domain"), 'twoFactorDomain', $conf->getTwoFactorAuthenticationDomain(), '529'); + $row->add($twoFactorDomain, 12); $twoFactorLabel = new htmlResponsiveInputField(_("Label"), 'twoFactorLabel', $conf->getTwoFactorAuthenticationLabel(), '517'); $row->add($twoFactorLabel, 12); $row->add(new htmlResponsiveInputCheckbox('twoFactorOptional', $conf->getTwoFactorAuthenticationOptional(), _('Optional'), '519'), 12); @@ -741,6 +744,7 @@ function checkInput() { } $conf->setTwoFactorAuthenticationClientId($_POST['twoFactorClientId']); $conf->setTwoFactorAuthenticationSecretKey($_POST['twoFactorSecretKey']); + $conf->setTwoFactorAuthenticationDomain($_POST['twoFactorDomain']); $conf->setTwoFactorAuthenticationInsecure(isset($_POST['twoFactorInsecure']) && ($_POST['twoFactorInsecure'] == 'on')); $conf->setTwoFactorAuthenticationLabel($_POST['twoFactorLabel']); $conf->setTwoFactorAuthenticationOptional(isset($_POST['twoFactorOptional']) && ($_POST['twoFactorOptional'] == 'on')); diff --git a/lam/templates/lib/500_lam.js b/lam/templates/lib/500_lam.js index 6452f71c..e30787e1 100644 --- a/lam/templates/lib/500_lam.js +++ b/lam/templates/lib/500_lam.js @@ -1411,19 +1411,17 @@ window.lam.webauthn.run = function(prefix) { * @param publicKey registration object */ window.lam.webauthn.register = function(publicKey) { - console.log(publicKey); publicKey.challenge = Uint8Array.from(window.atob(publicKey.challenge), c=>c.charCodeAt(0)); publicKey.user.id = Uint8Array.from(window.atob(publicKey.user.id), c=>c.charCodeAt(0)); navigator.credentials.create({publicKey}) .then(function (data) { - console.log(data); let publicKeyCredential = { id: data.id, type: data.type, rawId: btoa(String.fromCharCode(new Uint8Array(data.rawId))), response: { - clientDataJSON: btoa(String.fromCharCode((new Uint8Array(data.response.clientDataJSON)))), - attestationObject: btoa(String.fromCharCode((new Uint8Array(data.response.attestationObject)))) + clientDataJSON: window.lam.webauthn.arrayToBase64String(new Uint8Array(data.response.clientDataJSON)), + attestationObject: window.lam.webauthn.arrayToBase64String(new Uint8Array(data.response.attestationObject)) } }; console.log(publicKeyCredential); @@ -1433,6 +1431,16 @@ window.lam.webauthn.register = function(publicKey) { }); } +/** + * Converts an array to a base64 string. + * + * @param input array + * @returns base64 string + */ +window.lam.webauthn.arrayToBase64String = function(input) { + return btoa(String.fromCharCode(...input)); +} + jQuery(document).ready(function() { window.lam.gui.equalHeight(); window.lam.form.autoTrim(); diff --git a/lam/templates/misc/ajax.php b/lam/templates/misc/ajax.php index de69266c..c88f3fe9 100644 --- a/lam/templates/misc/ajax.php +++ b/lam/templates/misc/ajax.php @@ -196,7 +196,7 @@ class Ajax { $userDN = $_SESSION['ldap']->getUserName(); $isRegistered = isRegistered($userDN); if (!$isRegistered) { - $registrationObject = getRegistrationObject($userDN); + $registrationObject = getRegistrationObject($userDN, $isSelfService); echo json_encode( array( 'action' => 'register', diff --git a/lam/tests/lib/LAMConfigTest.php b/lam/tests/lib/LAMConfigTest.php index 6d0c2738..e946d72d 100644 --- a/lam/tests/lib/LAMConfigTest.php +++ b/lam/tests/lib/LAMConfigTest.php @@ -565,6 +565,17 @@ class LAMConfigTest extends PHPUnit_Framework_TestCase { $this->assertEquals($val, $this->lAMConfig->getTwoFactorAuthenticationSecretKey()); } + /** + * Tests LAMConfig->getTwoFactorAuthenticationDomain() and LAMConfig->setTwoFactorAuthenticationDomain() + */ + public function testTwoFactorAuthenticationDomain() { + $val = 'test.com'; + $this->lAMConfig->setTwoFactorAuthenticationDomain($val); + $this->assertEquals($val, $this->lAMConfig->getTwoFactorAuthenticationDomain()); + $this->doSave(); + $this->assertEquals($val, $this->lAMConfig->getTwoFactorAuthenticationDomain()); + } + /** * Tests LAMConfig->getTwoFactorAuthenticationInsecure() and LAMConfig->setTwoFactorAuthenticationInsecure() */