Add support for hashed passwords & add authcrypt:Hash authsource.

Thanks to Dyonisius Visser for implementing this.

git-svn-id: http://simplesamlphp.googlecode.com/svn/trunk@2962 44740490-163a-0410-bde0-09ae8108e29a
This commit is contained in:
olavmrk 2011-10-28 08:17:18 +00:00
parent ee383de867
commit 1f722c9f26
8 changed files with 243 additions and 2 deletions

48
bin/pwgen.php Executable file
View File

@ -0,0 +1,48 @@
#!/usr/bin/env php
<?php
/*
* $Id$
* Interactive script to generate password hashes.
*
*/
/* This is the base directory of the simpleSAMLphp installation. */
$baseDir = dirname(dirname(__FILE__));
/* Add library autoloader. */
require_once($baseDir . '/lib/_autoload.php');
echo "Enter password: ";
$password = trim(fgets(STDIN));
if(empty($password)) {
echo "Need at least one character for a password\n";
exit(1);
}
$table = '';
foreach (array_chunk(hash_algos(), 6) as $chunk) {
foreach($chunk as $algo) {
$table .= sprintf('%-13s', $algo);
}
$table .= "\n";
}
echo "The following hashing algorithms are available:\n" . $table . "\n";
echo "Which one do you want? [sha256] ";
$algo = trim(fgets(STDIN));
if(empty($algo)) {
$algo = 'sha256';
}
if(!in_array(strtolower($algo), hash_algos())) {
echo "Hashing algorithm '$algo' is not supported\n";
exit(1);
}
echo "Do you want to use a salt? (yes/no) [yes] ";
$s = (trim(fgets(STDIN)) == 'no') ? '' : 'S';
echo "\n " . SimpleSAML_Utils_Crypto::pwHash($password, strtoupper( $s . $algo ) ). "\n\n";

View File

@ -63,6 +63,17 @@ $config = array(
),
*/
/*
'crypto-hash' => array(
'authcrypto:Hash',
// hashed version of 'verysecret', made with bin/pwgen.php
'professor:{SSHA256}P6FDTEEIY2EnER9a6P2GwHhI5JDrwBgjQ913oVQjBngmCtrNBUMowA==' => array(
'uid' => array('prof_a'),
'eduPersonAffiliation' => array('member', 'employee', 'board'),
),
),
*/
/*
// This authentication source serves as an example of integration with an
// external authentication engine. Take a look at the comment in the beginning

View File

@ -67,6 +67,7 @@ $config = array (
* This password must be kept secret, and modified from the default value 123.
* This password will give access to the installation page of simpleSAMLphp with
* metadata listing and diagnostics pages.
* You can also put a hash here; run "bin/pwgen.php" to generate one.
*/
'auth.adminpassword' => '123',
'admin.protectindexpage' => false,

View File

@ -0,0 +1,83 @@
<?
/**
* A class for crypto related functions
*
* @author Dyonisius Visser, TERENA. <visser@terena.org>
* @package simpleSAMLphp
* @version $Id$
*/
class SimpleSAML_Utils_Crypto {
/**
* This function generates a password hash
* @param $password The unencrypted password
* @param $algo The hashing algorithm, capitals, optionally prepended with 'S' (salted)
* @param $salt Optional salt
*/
public static function pwHash($password, $algo, $salt = NULL) {
assert('is_string($algo)');
assert('is_string($password)');
if(in_array(strtolower($algo), hash_algos())) {
$php_algo = strtolower($algo); // 'sha256' etc
// LDAP compatibility
return '{' . preg_replace('/^SHA1$/', 'SHA', $algo) . '}'
.base64_encode(hash($php_algo, $password, TRUE));
}
// Salt
if(!$salt) {
// Default 8 byte salt, but 4 byte for LDAP SHA1 hashes
$bytes = ($algo == 'SSHA1') ? 4 : 8;
$salt = SimpleSAML_Utilities::generateRandomBytes($bytes, TRUE);
}
if($algo[0] == 'S' && in_array(substr(strtolower($algo),1), hash_algos())) {
$php_algo = substr(strtolower($algo),1); // 'sha256' etc
// Salted hash, with LDAP compatibility
return '{' . preg_replace('/^SSHA1$/', 'SSHA', $algo) . '}' .
base64_encode(hash($php_algo, $password.$salt, TRUE) . $salt);
}
throw new Exception('Hashing algoritm \'' . strtolower($algo) . '\' not supported');
}
/**
* This function checks if a password is valid
* @param $crypted Password as appears in password file, optionally prepended with algorithm
* @param $clear Password to check
*/
public static function pwValid($crypted, $clear) {
assert('is_string($crypted)');
assert('is_string($clear)');
// Match algorithm string ('{SSHA256}', '{MD5}')
if(preg_match('/^{(.*?)}(.*)$/', $crypted, $matches)) {
// LDAP compatibility
$algo = preg_replace('/^(S?SHA)$/', '${1}1', $matches[1]);
$cryptedpw = $matches[2];
if(in_array(strtolower($algo), hash_algos())) {
// Unsalted hash
return ( $crypted == self::pwHash($clear, $algo) );
}
if($algo[0] == 'S' && in_array(substr(strtolower($algo),1), hash_algos())) {
$php_algo = substr(strtolower($algo),1);
// Salted hash
$hash_length = strlen(hash($php_algo, 'whatever', TRUE));
$salt = substr(base64_decode($cryptedpw), $hash_length);
return ( $crypted == self::pwHash($clear, $algo, $salt) );
}
throw new Exception('Hashing algoritm \'' . strtolower($algo) . '\' not supported');
} else {
return $crypted === $clear;
}
}
}

View File

@ -0,0 +1,3 @@
This file indicates that the default state of this module
is disabled. To enable, create a file named enable in the
same directory as this file.

View File

@ -0,0 +1,95 @@
<?php
/**
* Authentication source for username & hashed password.
*
* This class is an authentication source which stores all username/hashes in an array,
* and authenticates users against this array.
*
* @author Dyonisius Visser, TERENA.
* @package simpleSAMLphp
* @version $Id$
*/
class sspmod_authcrypt_Auth_Source_Hash extends sspmod_core_Auth_UserPassBase {
/**
* Our users, stored in an associative array. The key of the array is "<username>:<passwordhash>",
* while the value of each element is a new array with the attributes for each user.
*/
private $users;
/**
* 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);
$this->users = array();
/* Validate and parse our configuration. */
foreach ($config as $userpass => $attributes) {
if (!is_string($userpass)) {
throw new Exception('Invalid <username>:<passwordhash> for authentication source ' .
$this->authId . ': ' . $userpass);
}
$userpass = explode(':', $userpass, 2);
if (count($userpass) !== 2) {
throw new Exception('Invalid <username>:<passwordhash> for authentication source ' .
$this->authId . ': ' . $userpass[0]);
}
$username = $userpass[0];
$passwordhash = $userpass[1];
try {
$attributes = SimpleSAML_Utilities::parseAttributes($attributes);
} catch(Exception $e) {
throw new Exception('Invalid attributes for user ' . $username .
' in authentication source ' . $this->authId . ': ' .
$e->getMessage());
}
$this->users[$username . ':' . $passwordhash] = $attributes;
}
}
/**
* 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.
*
* The username is UTF-8 encoded, and the hash is base64 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)');
foreach($this->users as $userpass=>$attrs) {
if(preg_match("/^$username:(.*)$/", $userpass, $matches)) {
if(SimpleSAML_Utils_Crypto::pwValid($matches[1], $password)) {
return $this->users[$userpass];
} else {
SimpleSAML_Logger::debug('Incorrect password "' . $password . '" for user '. $username);
}
}
}
throw new SimpleSAML_Error_Error('WRONGUSERPASS');
}
}

View File

@ -55,7 +55,7 @@ class sspmod_core_Auth_Source_AdminPassword extends sspmod_core_Auth_UserPassBas
throw new SimpleSAML_Error_Error('WRONGUSERPASS');
}
if ($password !== $adminPassword) {
if (!SimpleSAML_Utils_Crypto::pwValid($adminPassword, $password)) {
throw new SimpleSAML_Error_Error('WRONGUSERPASS');
}

View File

@ -33,7 +33,7 @@ if (isset($_POST['password'])) {
/* Validate and sanitize form data. */
if ($_POST['password'] === $correctpassword) {
if (SimpleSAML_Utils_Crypto::pwValid($correctpassword, $_POST['password'])) {
$username = 'admin';
$password = $_POST['password'];