diff options
Diffstat (limited to 'srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor')
4 files changed, 511 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Application.php b/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Application.php new file mode 100644 index 0000000..15e99b9 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Application.php @@ -0,0 +1,162 @@ +<?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * Second authentication factor handling + * + * @package PhpMyAdmin + */ +declare(strict_types=1); + +namespace PhpMyAdmin\Plugins\TwoFactor; + +use BaconQrCode\Renderer\Image\SvgImageBackEnd; +use PhpMyAdmin\Plugins\TwoFactorPlugin; +use PhpMyAdmin\TwoFactor; +use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException; +use PragmaRX\Google2FA\Exceptions\InvalidCharactersException; +use PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException; +use PragmaRX\Google2FAQRCode\Google2FA; + +/** + * HOTP and TOTP based two-factor authentication + * + * Also known as Google, Authy, or OTP + * + * @package PhpMyAdmin + */ +class Application extends TwoFactorPlugin +{ + /** + * @var string + */ + public static $id = 'application'; + + protected $_google2fa; + + /** + * Creates object + * + * @param TwoFactor $twofactor TwoFactor instance + */ + public function __construct(TwoFactor $twofactor) + { + parent::__construct($twofactor); + if (extension_loaded('imagick')) { + $this->_google2fa = new Google2FA(); + } else { + $this->_google2fa = new Google2FA(new SvgImageBackEnd()); + } + $this->_google2fa->setWindow(8); + if (! isset($this->_twofactor->config['settings']['secret'])) { + $this->_twofactor->config['settings']['secret'] = ''; + } + } + + /** + * Get any property of this class + * + * @param string $property name of the property + * + * @return mixed|void if property exist, value of the relevant property + */ + public function __get($property) + { + switch ($property) { + case 'google2fa': + return $this->_google2fa; + } + } + + /** + * Checks authentication, returns true on success + * + * @return boolean + * @throws IncompatibleWithGoogleAuthenticatorException + * @throws InvalidCharactersException + * @throws SecretKeyTooShortException + */ + public function check() + { + $this->_provided = false; + if (! isset($_POST['2fa_code'])) { + return false; + } + $this->_provided = true; + return $this->_google2fa->verifyKey( + $this->_twofactor->config['settings']['secret'], + $_POST['2fa_code'] + ); + } + + /** + * Renders user interface to enter two-factor authentication + * + * @return string HTML code + */ + public function render() + { + return $this->template->render('login/twofactor/application'); + } + + /** + * Renders user interface to configure two-factor authentication + * + * @return string HTML code + */ + public function setup() + { + $secret = $this->_twofactor->config['settings']['secret']; + $inlineUrl = $this->_google2fa->getQRCodeInline( + 'phpMyAdmin (' . $this->getAppId(false) . ')', + $this->_twofactor->user, + $secret + ); + return $this->template->render('login/twofactor/application_configure', [ + 'image' => $inlineUrl, + 'secret' => $secret, + 'has_imagick' => extension_loaded('imagick'), + ]); + } + + /** + * Performs backend configuration + * + * @return boolean + * @throws IncompatibleWithGoogleAuthenticatorException + * @throws InvalidCharactersException + * @throws SecretKeyTooShortException + */ + public function configure() + { + if (! isset($_SESSION['2fa_application_key'])) { + $_SESSION['2fa_application_key'] = $this->_google2fa->generateSecretKey(); + } + $this->_twofactor->config['settings']['secret'] = $_SESSION['2fa_application_key']; + + $result = $this->check(); + if ($result) { + unset($_SESSION['2fa_application_key']); + } + return $result; + } + + /** + * Get user visible name + * + * @return string + */ + public static function getName() + { + return __('Authentication Application (2FA)'); + } + + /** + * Get user visible description + * + * @return string + */ + public static function getDescription() + { + return __('Provides authentication using HOTP and TOTP applications such as FreeOTP, Google Authenticator or Authy.'); + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Invalid.php b/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Invalid.php new file mode 100644 index 0000000..31fffc0 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Invalid.php @@ -0,0 +1,68 @@ +<?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * Second authentication factor handling + * + * @package PhpMyAdmin + */ +declare(strict_types=1); + +namespace PhpMyAdmin\Plugins\TwoFactor; + +use PhpMyAdmin\Plugins\TwoFactorPlugin; +use PhpMyAdmin\Template; + +/** + * Invalid two-factor authentication showing that configured choice is not available. + * + * @package PhpMyAdmin + */ +class Invalid extends TwoFactorPlugin +{ + /** + * @var string + */ + public static $id = 'invalid'; + + public static $showSubmit = false; + + /** + * Checks authentication, returns true on success + * + * @return boolean + */ + public function check() + { + return false; + } + + /** + * Renders user interface to enter two-factor authentication + * + * @return string HTML code + */ + public function render() + { + return $this->template->render('login/twofactor/invalid'); + } + + /** + * Get user visible name + * + * @return string + */ + public static function getName() + { + return 'Invalid two-factor authentication'; + } + + /** + * Get user visible description + * + * @return string + */ + public static function getDescription() + { + return 'Error fallback only!'; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Key.php b/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Key.php new file mode 100644 index 0000000..cbb118c --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Key.php @@ -0,0 +1,213 @@ +<?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * Second authentication factor handling + * + * @package PhpMyAdmin + */ +declare(strict_types=1); + +namespace PhpMyAdmin\Plugins\TwoFactor; + +use PhpMyAdmin\Plugins\TwoFactorPlugin; +use PhpMyAdmin\Response; +use PhpMyAdmin\Template; +use PhpMyAdmin\TwoFactor; +use Samyoul\U2F\U2FServer\U2FException; +use Samyoul\U2F\U2FServer\U2FServer; +use stdClass; +use Throwable; +use Twig_Error_Loader; +use Twig_Error_Runtime; +use Twig_Error_Syntax; + +/** + * Hardware key based two-factor authentication + * + * Supports FIDO U2F tokens + * + * @package PhpMyAdmin + */ +class Key extends TwoFactorPlugin +{ + /** + * @var string + */ + public static $id = 'key'; + + /** + * Creates object + * + * @param TwoFactor $twofactor TwoFactor instance + */ + public function __construct(TwoFactor $twofactor) + { + parent::__construct($twofactor); + if (! isset($this->_twofactor->config['settings']['registrations'])) { + $this->_twofactor->config['settings']['registrations'] = []; + } + } + + /** + * Returns array of U2F registration objects + * + * @return array + */ + public function getRegistrations() + { + $result = []; + foreach ($this->_twofactor->config['settings']['registrations'] as $index => $data) { + $reg = new stdClass(); + $reg->keyHandle = $data['keyHandle']; + $reg->publicKey = $data['publicKey']; + $reg->certificate = $data['certificate']; + $reg->counter = $data['counter']; + $reg->index = $index; + $result[] = $reg; + } + return $result; + } + + /** + * Checks authentication, returns true on success + * + * @return boolean + */ + public function check() + { + $this->_provided = false; + if (! isset($_POST['u2f_authentication_response']) || ! isset($_SESSION['authenticationRequest'])) { + return false; + } + $this->_provided = true; + try { + $response = json_decode($_POST['u2f_authentication_response']); + if ($response === null) { + return false; + } + $authentication = U2FServer::authenticate( + $_SESSION['authenticationRequest'], + $this->getRegistrations(), + $response + ); + $this->_twofactor->config['settings']['registrations'][$authentication->index]['counter'] = $authentication->counter; + $this->_twofactor->save(); + return true; + } catch (U2FException $e) { + $this->_message = $e->getMessage(); + return false; + } + } + + /** + * Loads needed javascripts into the page + * + * @return void + */ + public function loadScripts() + { + $response = Response::getInstance(); + $scripts = $response->getHeader()->getScripts(); + $scripts->addFile('vendor/u2f-api-polyfill.js'); + $scripts->addFile('u2f.js'); + } + + /** + * Renders user interface to enter two-factor authentication + * + * @return string HTML code + */ + public function render() + { + $request = U2FServer::makeAuthentication( + $this->getRegistrations(), + $this->getAppId(true) + ); + $_SESSION['authenticationRequest'] = $request; + $this->loadScripts(); + return $this->template->render('login/twofactor/key', [ + 'request' => json_encode($request), + 'is_https' => $GLOBALS['PMA_Config']->isHttps(), + ]); + } + + /** + * Renders user interface to configure two-factor authentication + * + * @return string HTML code + * @throws U2FException + * @throws Throwable + * @throws Twig_Error_Loader + * @throws Twig_Error_Runtime + * @throws Twig_Error_Syntax + */ + public function setup() + { + $registrationData = U2FServer::makeRegistration( + $this->getAppId(true), + $this->getRegistrations() + ); + $_SESSION['registrationRequest'] = $registrationData['request']; + + $this->loadScripts(); + return $this->template->render('login/twofactor/key_configure', [ + 'request' => json_encode($registrationData['request']), + 'signatures' => json_encode($registrationData['signatures']), + 'is_https' => $GLOBALS['PMA_Config']->isHttps(), + ]); + } + + /** + * Performs backend configuration + * + * @return boolean + */ + public function configure() + { + $this->_provided = false; + if (! isset($_POST['u2f_registration_response']) || ! isset($_SESSION['registrationRequest'])) { + return false; + } + $this->_provided = true; + try { + $response = json_decode($_POST['u2f_registration_response']); + if ($response === null) { + return false; + } + $registration = U2FServer::register( + $_SESSION['registrationRequest'], + $response + ); + $this->_twofactor->config['settings']['registrations'][] = [ + 'keyHandle' => $registration->getKeyHandle(), + 'publicKey' => $registration->getPublicKey(), + 'certificate' => $registration->getCertificate(), + 'counter' => $registration->getCounter(), + ]; + return true; + } catch (U2FException $e) { + $this->_message = $e->getMessage(); + return false; + } + } + + /** + * Get user visible name + * + * @return string + */ + public static function getName() + { + return __('Hardware Security Key (FIDO U2F)'); + } + + /** + * Get user visible description + * + * @return string + */ + public static function getDescription() + { + return __('Provides authentication using hardware security tokens supporting FIDO U2F.'); + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Simple.php b/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Simple.php new file mode 100644 index 0000000..721d602 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/TwoFactor/Simple.php @@ -0,0 +1,68 @@ +<?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * Second authentication factor handling + * + * @package PhpMyAdmin + */ +declare(strict_types=1); + +namespace PhpMyAdmin\Plugins\TwoFactor; + +use PhpMyAdmin\Plugins\TwoFactorPlugin; +use PhpMyAdmin\Template; + +/** + * Simple two-factor authentication auth asking just for confirmation. + * + * This has no practical use, but can be used for testing. + * + * @package PhpMyAdmin + */ +class Simple extends TwoFactorPlugin +{ + /** + * @var string + */ + public static $id = 'simple'; + + /** + * Checks authentication, returns true on success + * + * @return boolean + */ + public function check() + { + return isset($_POST['2fa_confirm']); + } + + /** + * Renders user interface to enter two-factor authentication + * + * @return string HTML code + */ + public function render() + { + return $this->template->render('login/twofactor/simple'); + } + + /** + * Get user visible name + * + * @return string + */ + public static function getName() + { + return __('Simple two-factor authentication'); + } + + /** + * Get user visible description + * + * @return string + */ + public static function getDescription() + { + return __('For testing purposes only!'); + } +} |
