Skip to content
Snippets Groups Projects
Commit 9716a3a4 authored by Andreas Åkre Solberg's avatar Andreas Åkre Solberg
Browse files

Adding YubiKey authentication module from Simon Josefsson

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@1151 44740490-163a-0410-bde0-09ae8108e29a
parent 5c7008db
No related branches found
No related tags found
No related merge requests found
<?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
<?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;
}
}
?>
<?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;
}
}
?>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment