0
0
mirror of https://github.com/postfixadmin/postfixadmin.git synced 2024-09-19 19:22:14 +02:00

split up pacrypt() into different functions; add some minimal test coverage

This commit is contained in:
David Goodwin 2018-02-19 20:59:52 +00:00
parent 6ed1527497
commit 6446f3f6cc
2 changed files with 254 additions and 137 deletions

View File

@ -891,43 +891,27 @@ function validate_password($password) {
return $result;
}
/**
* Encrypt a password, using the apparopriate hashing mechanism as defined in
* config.inc.php ($CONF['encrypt']).
* When wanting to compare one pw to another, it's necessary to provide the salt used - hence
* the second parameter ($pw_db), which is the existing hash from the DB.
*
* @param string $pw
* @param string $pw_db optional encrypted password
* @return string encrypted password.
*/
function pacrypt($pw, $pw_db="") {
global $CONF;
$password = "";
$salt = "";
if ($CONF['encrypt'] == 'md5crypt') {
function _pacrypt_md5crypt($pw, $pw_db) {
$split_salt = preg_split('/\$/', $pw_db);
if (isset($split_salt[2])) {
$salt = $split_salt[2];
}
$password = md5crypt($pw, $salt);
} elseif ($CONF['encrypt'] == 'md5') {
$password = md5($pw);
} elseif ($CONF['encrypt'] == 'system') {
if ($pw_db) {
$password = crypt($pw, $pw_db);
} else {
$password = crypt($pw);
}
} elseif ($CONF['encrypt'] == 'cleartext') {
$password = $pw;
return md5crypt($pw, $salt);
}
return md5crypt($pw);
}
function _pacrypt_crypt($pw, $pw_db) {
if ($pw_db) {
return crypt($pw, $pw_db);
}
return crypt($pw);
}
function _pacrypt_mysql_encrypt($pw, $pw_db) {
// See https://sourceforge.net/tracker/?func=detail&atid=937966&aid=1793352&group_id=191583
// this is apparently useful for pam_mysql etc.
elseif ($CONF['encrypt'] == 'mysql_encrypt') {
$pw = escape_string($pw);
if ($pw_db!="") {
$salt=escape_string(substr($pw_db, 0, 2));
@ -937,7 +921,11 @@ function pacrypt($pw, $pw_db="") {
}
$l = db_row($res["result"]);
$password = $l[0];
} elseif ($CONF['encrypt'] == 'authlib') {
return $password;
}
function _pacrypt_authlib($pw, $pw_db) {
global $CONF;
$flavor = $CONF['authlib_default_flavor'];
$salt = substr(create_salt(), 0, 2); # courier-authlib supports only two-character salts
if (preg_match('/^{.*}/', $pw_db)) {
@ -958,7 +946,12 @@ function pacrypt($pw, $pw_db="") {
} else {
die("authlib_default_flavor '" . $flavor . "' unknown. Valid flavors are 'md5raw', 'md5', 'SHA' and 'crypt'");
}
} elseif (preg_match("/^dovecot:/", $CONF['encrypt'])) {
return $password;
}
function _pacrypt_dovecot($pw, $pw_db) {
global $CONF;
$split_method = preg_split('/:/', $CONF['encrypt']);
$method = strtoupper($split_method[1]);
# If $pw_db starts with {method}, change $method accordingly
@ -967,7 +960,9 @@ function pacrypt($pw, $pw_db="") {
}
if (! preg_match("/^[A-Z0-9.-]+$/", $method)) {
die("invalid dovecot encryption method");
} # TODO: check against a fixed list?
}
# TODO: check against a fixed list?
# if (strtolower($method) == 'md5-crypt') die("\$CONF['encrypt'] = 'dovecot:md5-crypt' will not work because dovecotpw generates a random salt each time. Please use \$CONF['encrypt'] = 'md5crypt' instead.");
# $crypt_method = preg_match ("/.*-CRYPT$/", $method);
@ -1004,7 +999,8 @@ function pacrypt($pw, $pw_db="") {
if (!$pipe) {
die("can't proc_open $dovecotpw");
} else {
}
// use dovecot's stdin, it uses getpass() twice (except when using -t)
// Write pass in pipe stdin
if (empty($dovepasstest)) {
@ -1041,13 +1037,46 @@ function pacrypt($pw, $pw_db="") {
$password = str_replace('{' . $method . '}', '', $password);
}
$password = rtrim($password);
}
} else {
die('unknown/invalid $CONF["encrypt"] setting: ' . $CONF['encrypt']);
return rtrim($password);
}
return $password;
/**
* Encrypt a password, using the apparopriate hashing mechanism as defined in
* config.inc.php ($CONF['encrypt']).
* When wanting to compare one pw to another, it's necessary to provide the salt used - hence
* the second parameter ($pw_db), which is the existing hash from the DB.
*
* @param string $pw
* @param string $pw_db optional encrypted password
* @return string encrypted password.
*/
function pacrypt($pw, $pw_db="") {
global $CONF;
$password = "";
$salt = "";
switch ($CONF['encrypt']) {
case 'md5crypt':
return _pacrypt_md5crypt($pw, $pw_db);
case 'md5':
return md5($pw);
case 'system' :
return _pacrypt_crypt($pw, $pw_db);
case 'cleartext' :
return $pw;
case 'mysql_encrypt' :
return _pacrypt_mysql_encrypt($pw, $pw_db);
case 'authlib':
return _pacrypt_authlib($pw, $pw_db);
}
if (preg_match("/^dovecot:/", $CONF['encrypt'])) {
return _pacrypt_dovecot($pw, $pw_db);
}
die('unknown/invalid $CONF["encrypt"] setting: ' . $CONF['encrypt']);
}
//

88
tests/PacryptTest.php Normal file
View File

@ -0,0 +1,88 @@
<?php
require_once('common.php');
class PaCryptTest extends PHPUnit_Framework_TestCase {
public function testMd5Crypt() {
$hash = _pacrypt_md5crypt('test', '');
$this->assertNotEmpty($hash);
$this->assertNotEquals('test', $hash);
$this->assertEquals($hash, _pacrypt_md5crypt('test', $hash));
}
public function testCrypt() {
// E_NOTICE if we pass in '' for the salt
$hash = _pacrypt_crypt('test', 'sa');
$this->assertNotEmpty($hash);
$this->assertNotEquals('test', $hash);
$this->assertEquals($hash, _pacrypt_crypt('test', $hash));
}
public function testMySQLEncrypt() {
if(!db_mysql()) {
$this->markTestSkipped('Not using MySQL');
}
$hash = _pacrypt_mysql_encrypt('test', '');
$this->assertNotEmpty($hash);
$this->assertNotEquals('test', $hash);
$this->assertEquals($hash, _pacrypt_mysql_encrypt('test', $hash));
$hash2 = _pacrypt_mysql_encrypt('test', 'salt');
$this->assertNotEmpty($hash2);
$this->assertNotEquals($hash, $hash2);
$this->assertEquals($hash2, _pacrypt_mysql_encrypt('test', 'salt'));
}
public function testAuthlib() {
// too many options!
foreach(
['md5raw' => '098f6bcd4621d373cade4e832627b4f6',
'md5' => 'CY9rzUYh03PK3k6DJie09g==',
// crypt requires salt ...
'SHA' => 'qUqP5cyxm6YcTAhz05Hph5gvu9M='] as $flavour => $hash) {
$CONF['authlib_default_flavour'] = $flavour;
$stored = "{" . $flavour . "}$hash";
$hash = _pacrypt_authlib('test', $stored);
$this->assertEquals($hash, $stored, "Hash: $hash vs Stored: $stored" );
//var_dump("Hash: $hash from $flavour");
}
}
public function testPacryptDovecot() {
global $CONF;
if(!file_exists('/usr/bin/doveadm')) {
$this->markTestSkipped("No /usr/bin/doveadm");
}
$CONF['encrypt'] = 'dovecot:SHA1';
$expected_hash = '{SHA1}qUqP5cyxm6YcTAhz05Hph5gvu9M=';
$this->assertEquals($expected_hash, _pacrypt_dovecot('test', ''));
$this->assertEquals($expected_hash, _pacrypt_dovecot('test', $expected_hash));
}
}
/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */