diff --git a/modules/authYubiKey/default-disable b/modules/authYubiKey/default-disable new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/authYubiKey/lib/Auth/Process/OTP2YubiPrefix.php b/modules/authYubiKey/lib/Auth/Process/OTP2YubiPrefix.php new file mode 100644 index 0000000000000000000000000000000000000000..3c98e9ee2aa0c9d9f8e72ecd25950804cad1828b --- /dev/null +++ b/modules/authYubiKey/lib/Auth/Process/OTP2YubiPrefix.php @@ -0,0 +1,78 @@ +<?php + +/* + * Copyright (C) 2009 Simon Josefsson <simon@yubico.com>. + * + * This file is part of simpleSAMLphp + * + * simpleSAMLphp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * simpleSAMLphp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License License along with GNU SASL Library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +/** + * A processing filter to replace the 'otp' attribute with an attribute + * 'yubiPrefix' that contains the static YubiKey prefix. + * + * Before: + * otp=ekhgjhbctrgnubeeklijcibbgjnbtjlffdnjbhjluvur + * + * After: + * otp undefined + * yubiPrefix=ekhgjhbctrgn + * + * You use it by adding it as an authentication filter in config.php: + * + * 'authproc.idp' => array( + * ... + * 90 => 'authYubiKey:OTP2YubiPrefix', + * ... + * ); + * + */ +class sspmod_authYubiKey_Auth_Process_OTP2YubiPrefix extends SimpleSAML_Auth_ProcessingFilter { + + + /** + * Filter out YubiKey 'otp' attribute and replace it with + * a 'yubiPrefix' attribute that leaves out the dynamic part. + * + * @param array &$state The state we should update. + */ + public function process(&$state) { + assert('is_array($state)'); + assert('array_key_exists("Attributes", $state)'); + $attributes = $state['Attributes']; + + SimpleSAML_Logger::debug('OTP2YubiPrefix: enter with attributes: ' . implode(',', array_keys($attributes))); + + $otps = $attributes['otp']; + $otp = $otps['0']; + + $token_size = 32; + $identity = substr ($otp, 0, strlen ($otp) - $token_size); + + $attributes['yubiPrefix'] = array($identity); + + SimpleSAML_Logger::info('OTP2YubiPrefix: otp: ' . $otp . ' identity: ' . $identity . ' (otp keys: ' . implode(',', array_keys($otps)) . ')'); + + unset($attributes['otp']); + + SimpleSAML_Logger::debug('OTP2YubiPrefix: leaving with attributes: ' . implode(',', array_keys($attributes))); + } + +} + +?> \ No newline at end of file diff --git a/modules/authYubiKey/lib/Auth/Source/YubiKey.php b/modules/authYubiKey/lib/Auth/Source/YubiKey.php new file mode 100644 index 0000000000000000000000000000000000000000..e6be76d4cf52b66f6e7d2e13caa6a405ab90ad96 --- /dev/null +++ b/modules/authYubiKey/lib/Auth/Source/YubiKey.php @@ -0,0 +1,117 @@ +<?php + +/* + * Copyright (C) 2009 Andreas Ă…kre Solberg <andreas.solberg@uninett.no> + * Copyright (C) 2009 Simon Josefsson <simon@yubico.com>. + * + * This file is part of simpleSAMLphp + * + * simpleSAMLphp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * simpleSAMLphp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License License along with GNU SASL Library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +/** + * YubiKey authentication module, see http://www.yubico.com/developers/intro/ + * * + * Configure it by adding an entry to config/authsources.php such as this: + * + * 'yubikey' => array( + * 'authYubiKey:YubiKey', + * 'id' => 997, + * 'key' => 'b64hmackey', + * ), + * + * To generate your own client id/key you will need one YubiKey, and then + * go to http://yubico.com/developers/api/ + * + * @package simpleSAMLphp + * @version $Id$ + */ +class sspmod_authYubiKey_Auth_Source_YubiKey extends sspmod_core_Auth_UserPassBase { + + /** + * The client id/key for use with the Auth_Yubico PHP module. + */ + private $yubi_id; + private $yubi_key; + + /** + * Constructor for this authentication source. + * + * @param array $info Information about this authentication source. + * @param array $config Configuration. + */ + public function __construct($info, $config) { + assert('is_array($info)'); + assert('is_array($config)'); + + /* Call the parent constructor first, as required by the interface. */ + parent::__construct($info, $config); + + if (array_key_exists('id', $config)) { + $this->yubi_id = $config['id']; + } + + if (array_key_exists('key', $config)) { + $this->yubi_key = $config['key']; + } + } + + + /** + * Attempt to log in using the given username and password. + * + * On a successful login, this function should return the users attributes. On failure, + * it should throw an exception. If the error was caused by the user entering the wrong + * username or password, a SimpleSAML_Error_Error('WRONGUSERPASS') should be thrown. + * + * Note that both the username and the password are UTF-8 encoded. + * + * @param string $username The username the user wrote. + * @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)'); + + require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/libextinc/Yubico.php'; + + $attributes = array(); + + try { + $yubi = &new Auth_Yubico($this->yubi_id, $this->yubi_key); + $auth = $yubi->verify($password); + + $attributes = array('uid' => array($username), 'otp' => array($password)); + } catch (Exception $e) { + SimpleSAML_Logger::info('YubiKey:' . $this->authId . + ': Validation error, debug output from server: ' . $yubi->getLastResponse()); + + throw new SimpleSAML_Error_Error('WRONGUSERPASS', $e); + } + + SimpleSAML_Logger::debug('YubiKey:' . $this->authId . + ': YubiKey OTP validated successfully: ' . $yubi->getLastResponse()); + SimpleSAML_Logger::info('YubiKey:' . $this->authId . + ': Attributes: ' . implode(',', array_keys($attributes))); + + return $attributes; + } + +} + +?> diff --git a/modules/authYubiKey/libextinc/Yubico.php b/modules/authYubiKey/libextinc/Yubico.php new file mode 100644 index 0000000000000000000000000000000000000000..740bee5c9f6a8cf06d350645e1df7dace5f2187e --- /dev/null +++ b/modules/authYubiKey/libextinc/Yubico.php @@ -0,0 +1,167 @@ +<?php + /** + * Class for verifying Yubico One-Time-Passcodes + * + * LICENSE: + * + * Copyright (c) 2007, 2008 Simon Josefsson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * o Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * o Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * o The names of the authors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category Auth + * @package Auth_Yubico + * @author Simon Josefsson <simon@yubico.com> + * @copyright 2008 Simon Josefsson + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Yubico.php,v 1.7 2007-10-22 12:56:14 jas Exp $ + * @link http://yubico.com/ + */ + +#require_once 'PEAR.php'; + +/** + * Class for verifying Yubico One-Time-Passcodes + * + * Simple example: + * <code> + * require_once 'Auth/Yubico.php'; + * $yubi = &new Auth_Yubico('42'); + * $auth = $yubi->verify("ccbbddeertkrctjkkcglfndnlihhnvekchkcctif"); + * if (PEAR::isError($auth)) { + * print "<p>Authentication failed: " . $auth->getMessage(); + * print "<p>Debug output from server: " . $yubi->getLastResponse(); + * } else { + * print "<p>You are authenticated!"; + * } + * </code> + */ +class Auth_Yubico +{ + /**#@+ + * @access private + */ + + /** + * Yubico client ID + * @var string + */ + var $_id; + + /** + * Yubico client key + * @var string + */ + var $_key; + + /** + * Response from server + * @var string + */ + var $_response; + + /** + * Constructor + * + * Sets up the object + * @param string The client identity + * @param string The client MAC key (optional) + * @access public + */ + function Auth_Yubico($id, $key = '') + { + $this->_id = $id; + $this->_key = base64_decode($key); + } + + /** + * Return the last data received from the server, if any. + * + * @return string Output from server. + * @access public + */ + function getLastResponse() + { + return $this->_response; + } + + /* TODO? Add functions to get parsed parts of server response? */ + + /** + * Verify Yubico OTP + * + * @param string $token Yubico OTP + * @return mixed PEAR error on error, true otherwise + * @access public + */ + function verify($token) + { + $parameters = "id=" . $this->_id . "&otp=" . $token; + /* Generate signature. */ + if($this->_key <> "") { + $signature = base64_encode(hash_hmac('sha1', $parameters, $this->_key, true)); + $parameters .= '&h=' . $signature; + } + /* Support https. */ + $url = "https://api.yubico.com/wsapi/verify?" . $parameters; + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_USERAGENT, "PEAR Auth_Yubico"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $this->_response = curl_exec($ch); + curl_close($ch); + + if(!preg_match("/status=([a-zA-Z0-9_]+)/", $this->_response, $out)) { + throw new Exception('Could not parse response'); + } + + $status = $out[1]; + + /* Verify signature. */ + if($this->_key <> "") { + $rows = split("\r\n", $this->_response); + while (list($key, $val) = each($rows)) { + // = is also used in BASE64 encoding so we only replace the first = by # which is not used in BASE64 + $val = preg_replace('/=/', '#', $val, 1); + $row = split("#", $val); + $response[$row[0]] = $row[1]; + } + + $check = 'status=' . $response[status] . '&t='. $response[t]; + $checksignature = base64_encode(hash_hmac('sha1', $check, $this->_key, true)); + + if($response[h] != $checksignature) { + throw new Exception('Checked Signature failed'); + } + } + + if ($status != 'OK') { + throw new Exception('Status was not OK: ' . $status); + } + + return true; + } +} +?>