diff --git a/modules/authYubiKey/lib/Auth/Source/YubiKey.php b/modules/authYubiKey/lib/Auth/Source/YubiKey.php index 71b8a3cdf500999eae7c706ef62e68b04b5cf8da..9262ac680a9204f130d6c495bc3f2b5c738faf4c 100644 --- a/modules/authYubiKey/lib/Auth/Source/YubiKey.php +++ b/modules/authYubiKey/lib/Auth/Source/YubiKey.php @@ -40,13 +40,29 @@ * @package simpleSAMLphp * @version $Id$ */ -class sspmod_authYubiKey_Auth_Source_YubiKey extends sspmod_core_Auth_UserPassBase { +class sspmod_authYubiKey_Auth_Source_YubiKey extends SimpleSAML_Auth_Source { - /** - * The client id/key for use with the Auth_Yubico PHP module. - */ - private $yubi_id; - private $yubi_key; + /** + * The string used to identify our states. + */ + const STAGEID = 'sspmod_authYubiKey_Auth_Source_YubiKey.state'; + + /** + * The number of characters of the OTP that is the secure token. + * The rest is the user id. + */ + const TOKENSIZE = 32; + + /** + * The key of the AuthId field in the state. + */ + const AUTHID = 'sspmod_authYubiKey_Auth_Source_YubiKey.AuthId'; + + /** + * The client id/key for use with the Auth_Yubico PHP module. + */ + private $yubi_id; + private $yubi_key; /** * Constructor for this authentication source. @@ -71,6 +87,84 @@ class sspmod_authYubiKey_Auth_Source_YubiKey extends sspmod_core_Auth_UserPassBa } + /** + * Initialize login. + * + * This function saves the information about the login, and redirects to a + * login page. + * + * @param array &$state Information about the current authentication. + */ + public function authenticate(&$state) { + assert('is_array($state)'); + + /* We are going to need the authId in order to retrieve this authentication source later. */ + $state[self::AUTHID] = $this->authId; + + $id = SimpleSAML_Auth_State::saveState($state, self::STAGEID); + + $url = SimpleSAML_Module::getModuleURL('authYubiKey/yubikeylogin.php'); + SimpleSAML_Utilities::redirect($url, array('AuthState' => $id)); + } + + + /** + * Handle login request. + * + * This function is used by the login form (core/www/loginuserpass.php) when the user + * enters a username and password. On success, it will not return. On wrong + * username/password failure, it will return the error code. Other failures will throw an + * exception. + * + * @param string $authStateId The identifier of the authentication state. + * @param string $otp The one time password entered- + * @return string Error code in the case of an error. + */ + public static function handleLogin($authStateId, $otp) { + assert('is_string($authStateId)'); + assert('is_string($otp)'); + + /* Retrieve the authentication state. */ + $state = SimpleSAML_Auth_State::loadState($authStateId, self::STAGEID); + + /* Find authentication source. */ + assert('array_key_exists(self::AUTHID, $state)'); + $source = SimpleSAML_Auth_Source::getById($state[self::AUTHID]); + if ($source === NULL) { + throw new Exception('Could not find authentication source with id ' . $state[self::AUTHID]); + } + + + try { + /* Attempt to log in. */ + $attributes = $source->login($otp); + } catch (SimpleSAML_Error_Error $e) { + /* An error occured during login. Check if it is because of the wrong + * username/password - if it is, we pass that error up to the login form, + * if not, we let the generic error handler deal with it. + */ + if ($e->getErrorCode() === 'WRONGUSERPASS') { + return 'WRONGUSERPASS'; + } + + /* Some other error occured. Rethrow exception and let the generic error + * handler deal with it. + */ + throw $e; + } + + $state['Attributes'] = $attributes; + SimpleSAML_Auth_Source::completeAuth($state); + } + + /** + * Return the user id part of a one time passord + */ + public static function getYubiKeyPrefix($otp) { + $uid = substr ($otp, 0, strlen ($otp) - self::TOKENSIZE); + return $uid; + } + /** * Attempt to log in using the given username and password. * @@ -84,9 +178,8 @@ class sspmod_authYubiKey_Auth_Source_YubiKey extends sspmod_core_Auth_UserPassBa * @param string $password The password the user wrote. * @return array Associative array with the users attributes. */ - protected function login($username, $password) { - assert('is_string($username)'); - assert('is_string($password)'); + protected function login($otp) { + assert('is_string($otp)'); require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/libextinc/Yubico.php'; @@ -94,16 +187,16 @@ class sspmod_authYubiKey_Auth_Source_YubiKey extends sspmod_core_Auth_UserPassBa try { $yubi = &new Auth_Yubico($this->yubi_id, $this->yubi_key); - $auth = $yubi->verify($password); - - $attributes = array('uid' => array($username), 'otp' => array($password)); + $auth = $yubi->verify($otp); + $uid = self::getYubiKeyPrefix($otp); + $attributes = array('uid' => array($uid)); } catch (Exception $e) { - SimpleSAML_Logger::info('YubiKey:' . $this->authId . ': Validation error (user ' . $username . ' otp ' . $password . '), debug output: ' . $yubi->getLastResponse()); + SimpleSAML_Logger::info('YubiKey:' . $this->authId . ': Validation error (otp ' . $otp . '), debug output: ' . $yubi->getLastResponse()); throw new SimpleSAML_Error_Error('WRONGUSERPASS', $e); } - SimpleSAML_Logger::info('YubiKey:' . $this->authId . ': YubiKey otp ' . $password . ' for user ' . $username . ' validated successfully: ' . $yubi->getLastResponse()); + SimpleSAML_Logger::info('YubiKey:' . $this->authId . ': YubiKey otp ' . $password . ' validated successfully: ' . $yubi->getLastResponse()); return $attributes; } diff --git a/modules/authYubiKey/templates/default/yubikeylogin.php b/modules/authYubiKey/templates/default/yubikeylogin.php new file mode 100644 index 0000000000000000000000000000000000000000..6632a19b621eacf95e351e7c7211c4e79e360ef4 --- /dev/null +++ b/modules/authYubiKey/templates/default/yubikeylogin.php @@ -0,0 +1,50 @@ +<?php +$this->data['icon'] = 'lock.png'; +$this->data['header'] = $this->t('{authYubiKey:yubikey:header}'); +$this->data['autofocus'] = 'otp'; + +$this->includeAtTemplateBase('includes/header.php'); + +?> +<div id="content"> + +<?php +if ($this->data['errorcode'] !== NULL) { +?> + <div style="border-left: 1px solid #e8e8e8; border-bottom: 1px solid #e8e8e8; background: #f5f5f5"> + <img src="/<?php echo $this->data['baseurlpath']; ?>resources/icons/bomb.png" style="float: left; margin: 15px " /> + <h2><?php echo $this->t('{login:error_header}'); ?></h2> + <p><b><?php echo $this->t('{errors:title_' . $this->data['errorcode'] . '}'); ?></b></p> + <p><?php echo $this->t('{errors:descr_' . $this->data['errorcode'] . '}'); ?></p> + </div> +<?php +} +?> + + <img style="float: right" src="<?php echo(SimpleSAML_Module::getModuleURL('authYubiKey/resources/logo.jpg')); ?>" /> + <img style="clear: right; float: right" src="<?php echo(SimpleSAML_Module::getModuleURL('authYubiKey/resources/yubikey.jpg')); ?>" /> + + + <h2 style=""><?php echo $this->t('{authYubiKey:yubikey:header}'); ?></h2> + + <form action="?" method="post" name="f"> + + <p><?php echo $this->t('{authYubiKey:yubikey:intro}'); ?></p> + + <p><input id="otp" style="border: 1px solid #ccc; background: #eee; padding: .5em; font-size: medium; width: 70%; color: #aaa" type="text" tabindex="2" name="otp" /></p> + + + + +<?php +foreach ($this->data['stateparams'] as $name => $value) { + echo('<input type="hidden" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value) . '" />'); +} +?> + + </form> + +<?php + +$this->includeAtTemplateBase('includes/footer.php'); +?> \ No newline at end of file diff --git a/modules/authYubiKey/www/resources/logo.jpg b/modules/authYubiKey/www/resources/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3b7e7f360ddb176fd383a8bea0d6b500fff95cf5 Binary files /dev/null and b/modules/authYubiKey/www/resources/logo.jpg differ diff --git a/modules/authYubiKey/www/resources/yubikey.jpg b/modules/authYubiKey/www/resources/yubikey.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f3c8d0bd96f6752de7f5dbaa22be933e261da6cc Binary files /dev/null and b/modules/authYubiKey/www/resources/yubikey.jpg differ diff --git a/modules/authYubiKey/www/yubikeylogin.php b/modules/authYubiKey/www/yubikeylogin.php new file mode 100644 index 0000000000000000000000000000000000000000..3c281507feca9296e5e37ce20ab0f8184e3e7682 --- /dev/null +++ b/modules/authYubiKey/www/yubikeylogin.php @@ -0,0 +1,39 @@ +<?php + +/** + * This page shows a username/password login form, and passes information from it + * to the sspmod_core_Auth_UserPassBase class, which is a generic class for + * username/password authentication. + * + * @author Olav Morken, UNINETT AS. + * @package simpleSAMLphp + * @version $Id$ + */ + +if (!array_key_exists('AuthState', $_REQUEST)) { + throw new SimpleSAML_Error_BadRequest('Missing AuthState parameter.'); +} +$authStateId = $_REQUEST['AuthState']; + +if (array_key_exists('otp', $_REQUEST)) { + $otp = $_REQUEST['otp']; +} else { + $otp = ''; +} + +if (!empty($otp)) { + /* attempt to log in. */ + $errorCode = sspmod_authYubiKey_Auth_Source_YubiKey::handleLogin($authStateId, $otp); +} else { + $errorCode = NULL; +} + +$globalConfig = SimpleSAML_Configuration::getInstance(); +$t = new SimpleSAML_XHTML_Template($globalConfig, 'authYubiKey:yubikeylogin.php'); +$t->data['stateparams'] = array('AuthState' => $authStateId); +$t->data['errorcode'] = $errorCode; +$t->show(); +exit(); + + +?> \ No newline at end of file