mirror of
https://github.com/postfixadmin/postfixadmin.git
synced 2024-09-20 03:36:20 +02:00
feat: support Dovecot DIGEST-MD5 (#816)
Add support for dovecot DIGEST-MD5 auth (using : $CONF['pacrypt'] = 'dovecot:DIGEST-MD5') This also changes the pacrypt() function to take an optional 3rd argument (username). Thanks @bestlong
This commit is contained in:
parent
53426ac148
commit
0876c368e4
22
.github/workflows/php.yml
vendored
22
.github/workflows/php.yml
vendored
@ -6,14 +6,14 @@ jobs:
|
|||||||
lint_etc:
|
lint_etc:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: 7.4
|
php-version: 7.4
|
||||||
tools: composer
|
tools: composer
|
||||||
extensions: sqlite3
|
extensions: sqlite3, gd
|
||||||
|
|
||||||
- name: run install.sh
|
- name: run install.sh
|
||||||
run: /bin/bash install.sh
|
run: /bin/bash install.sh
|
||||||
@ -42,14 +42,21 @@ jobs:
|
|||||||
php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]
|
php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Dovecot
|
||||||
|
run: |
|
||||||
|
set -eux
|
||||||
|
sudo apt-get update -q
|
||||||
|
sudo DEBIAN_FRONTEND=noninteractive apt-get install -yq dovecot-core
|
||||||
|
sudo sh -c '/sbin/useradd -G dovecot runner || usermod -aG dovecot runner '
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-versions }}
|
php-version: ${{ matrix.php-versions }}
|
||||||
tools: composer
|
tools: composer
|
||||||
extensions: sqlite3
|
extensions: sqlite3, gd
|
||||||
|
|
||||||
- name: run install.sh
|
- name: run install.sh
|
||||||
run: /bin/bash install.sh
|
run: /bin/bash install.sh
|
||||||
@ -61,21 +68,21 @@ jobs:
|
|||||||
run: composer install --prefer-dist -n
|
run: composer install --prefer-dist -n
|
||||||
|
|
||||||
- name: Build/test
|
- name: Build/test
|
||||||
run: composer test
|
run: sudo -u runner composer test
|
||||||
|
|
||||||
build_coverage_report:
|
build_coverage_report:
|
||||||
needs: [testsuite]
|
needs: [testsuite]
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: '7.4'
|
php-version: '7.4'
|
||||||
tools: composer
|
tools: composer
|
||||||
extensions: sqlite3
|
extensions: sqlite3, gd
|
||||||
|
|
||||||
- name: run install.sh
|
- name: run install.sh
|
||||||
run: /bin/bash install.sh
|
run: /bin/bash install.sh
|
||||||
@ -93,4 +100,3 @@ jobs:
|
|||||||
run: vendor/bin/php-coveralls --coverage_clover=build/logs/clover.xml -v || true
|
run: vendor/bin/php-coveralls --coverage_clover=build/logs/clover.xml -v || true
|
||||||
env:
|
env:
|
||||||
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
||||||
|
|
||||||
|
@ -1060,9 +1060,10 @@ function _pacrypt_authlib($pw, $pw_db)
|
|||||||
*
|
*
|
||||||
* @param string $pw - plain text password
|
* @param string $pw - plain text password
|
||||||
* @param string $pw_db - encrypted password, or '' for generation.
|
* @param string $pw_db - encrypted password, or '' for generation.
|
||||||
|
* @param string $username
|
||||||
* @return string crypted password
|
* @return string crypted password
|
||||||
*/
|
*/
|
||||||
function _pacrypt_dovecot($pw, $pw_db = '')
|
function _pacrypt_dovecot($pw, $pw_db = '', $username = '')
|
||||||
{
|
{
|
||||||
global $CONF;
|
global $CONF;
|
||||||
|
|
||||||
@ -1076,11 +1077,14 @@ function _pacrypt_dovecot($pw, $pw_db = '')
|
|||||||
throw new Exception("invalid dovecot encryption method");
|
throw new Exception("invalid dovecot encryption method");
|
||||||
}
|
}
|
||||||
|
|
||||||
# digest-md5 hashes include the username - until someone implements it, let's declare it as unsupported
|
$doveadm_options = '';
|
||||||
|
|
||||||
if (strtolower($method) == 'digest-md5') {
|
if (strtolower($method) == 'digest-md5') {
|
||||||
throw new Exception("Sorry, \$CONF['encrypt'] = 'dovecot:digest-md5' is not supported by PostfixAdmin.");
|
if (empty($username)) {
|
||||||
|
throw new Exception("\$CONF['encrypt'] = 'dovecot:digest-md5' require username.");
|
||||||
|
}
|
||||||
|
$doveadm_options = ' -u ' . escapeshellarg($username);
|
||||||
}
|
}
|
||||||
# TODO: add -u option for those hashes, or for everything that is salted (-u was available before dovecot 2.1 -> no problem with backward compatibility )
|
|
||||||
|
|
||||||
$dovecotpw = "doveadm pw";
|
$dovecotpw = "doveadm pw";
|
||||||
if (!empty($CONF['dovecotpw'])) {
|
if (!empty($CONF['dovecotpw'])) {
|
||||||
@ -1105,7 +1109,7 @@ function _pacrypt_dovecot($pw, $pw_db = '')
|
|||||||
|
|
||||||
$pipes = [];
|
$pipes = [];
|
||||||
|
|
||||||
$pipe = proc_open("$dovecotpw '-s' $method$dovepasstest", $spec, $pipes);
|
$pipe = proc_open("$dovecotpw -s {$method}{$dovepasstest}{$doveadm_options}", $spec, $pipes);
|
||||||
|
|
||||||
if (!$pipe) {
|
if (!$pipe) {
|
||||||
throw new Exception("can't proc_open $dovecotpw");
|
throw new Exception("can't proc_open $dovecotpw");
|
||||||
@ -1323,9 +1327,10 @@ function _php_crypt_random_string($characters, $length)
|
|||||||
*
|
*
|
||||||
* @param string $pw
|
* @param string $pw
|
||||||
* @param string $pw_db optional encrypted password
|
* @param string $pw_db optional encrypted password
|
||||||
|
* @param string $username optional, but required when $CONF['encrypt'] = 'dovecot:digest-md5'
|
||||||
* @return string encrypted password - if this matches $pw_db then the original password is $pw.
|
* @return string encrypted password - if this matches $pw_db then the original password is $pw.
|
||||||
*/
|
*/
|
||||||
function pacrypt($pw, $pw_db = "")
|
function pacrypt($pw, $pw_db = "", $username = '')
|
||||||
{
|
{
|
||||||
global $CONF;
|
global $CONF;
|
||||||
|
|
||||||
@ -1382,7 +1387,7 @@ function pacrypt($pw, $pw_db = "")
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('/^DOVECOT:(.*)$/i', $mechanism, $matches)) {
|
if (preg_match('/^DOVECOT:(.*)$/i', $mechanism, $matches)) {
|
||||||
return _pacrypt_dovecot($pw, $pw_db);
|
return _pacrypt_dovecot($pw, $pw_db, $username);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($pw_db)) {
|
if (empty($pw_db)) {
|
||||||
|
@ -36,7 +36,7 @@ class Login
|
|||||||
$row = $result[0];
|
$row = $result[0];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$crypt_password = pacrypt($password, $row['password']);
|
$crypt_password = pacrypt($password, $row['password'], $username);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
error_log("Error while trying to call pacrypt()");
|
error_log("Error while trying to call pacrypt()");
|
||||||
error_log("" . $e);
|
error_log("" . $e);
|
||||||
@ -136,7 +136,7 @@ class Login
|
|||||||
}
|
}
|
||||||
|
|
||||||
$set = array(
|
$set = array(
|
||||||
'password' => pacrypt($new_password),
|
'password' => pacrypt($new_password, '', $username),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (Config::bool('password_expiration')) {
|
if (Config::bool('password_expiration')) {
|
||||||
@ -226,7 +226,7 @@ class Login
|
|||||||
throw new \Exception(Config::Lang('pPassword_password_current_text_error'));
|
throw new \Exception(Config::Lang('pPassword_password_current_text_error'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$app_pass = pacrypt($app_pass);
|
$app_pass = pacrypt($app_pass, '', $username);
|
||||||
|
|
||||||
|
|
||||||
$result = db_insert('mailbox_app_password', ['username' => $username, 'description' => $app_desc, 'password_hash' => $app_pass], []);
|
$result = db_insert('mailbox_app_password', ['username' => $username, 'description' => $app_desc, 'password_hash' => $app_pass], []);
|
||||||
|
@ -882,7 +882,7 @@ abstract class PFAHandler
|
|||||||
if (sizeof($result) == 1) {
|
if (sizeof($result) == 1) {
|
||||||
$row = $result[0];
|
$row = $result[0];
|
||||||
|
|
||||||
$crypt_token = pacrypt($token, $row['token']);
|
$crypt_token = pacrypt($token, $row['token'], $username);
|
||||||
|
|
||||||
if ($row['token'] == $crypt_token) {
|
if ($row['token'] == $crypt_token) {
|
||||||
db_update($this->db_table, $this->id_field, $username, array(
|
db_update($this->db_table, $this->id_field, $username, array(
|
||||||
|
@ -35,7 +35,7 @@ class DovecotCrypt extends Crypt
|
|||||||
'CLEARTEXT' => array('NONE', 0, null, 'plain_generate'),
|
'CLEARTEXT' => array('NONE', 0, null, 'plain_generate'),
|
||||||
'CRAM-MD5' => array('HEX', CRAM_MD5_CONTEXTLEN, null, 'cram_md5_generate'),
|
'CRAM-MD5' => array('HEX', CRAM_MD5_CONTEXTLEN, null, 'cram_md5_generate'),
|
||||||
//'HMAC-MD5' => array('HEX', CRAM_MD5_CONTEXTLEN, NULL, 'cram_md5_generate'),
|
//'HMAC-MD5' => array('HEX', CRAM_MD5_CONTEXTLEN, NULL, 'cram_md5_generate'),
|
||||||
//'DIGEST-MD5' => array('HEX', MD5_RESULTLEN, NULL, 'digest_md5_generate'),
|
'DIGEST-MD5' => array('HEX', MD5_RESULTLEN, null, 'digest_md5_generate'),
|
||||||
//'PLAIN-MD4' => array('HEX', MD4_RESULTLEN, NULL, 'plain_md4_generate'),
|
//'PLAIN-MD4' => array('HEX', MD4_RESULTLEN, NULL, 'plain_md4_generate'),
|
||||||
//'PLAIN-MD5' => array('HEX', MD5_RESULTLEN, NULL, 'plain_md5_generate'),
|
//'PLAIN-MD5' => array('HEX', MD5_RESULTLEN, NULL, 'plain_md5_generate'),
|
||||||
//'LDAP-MD5' => array('BASE64', MD5_RESULTLEN, NULL, 'plain_md5_generate'),
|
//'LDAP-MD5' => array('BASE64', MD5_RESULTLEN, NULL, 'plain_md5_generate'),
|
||||||
|
@ -73,7 +73,6 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase
|
|||||||
$this->markTestSkipped("No /usr/bin/doveadm");
|
$this->markTestSkipped("No /usr/bin/doveadm");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$CONF['encrypt'] = 'dovecot:SHA1';
|
$CONF['encrypt'] = 'dovecot:SHA1';
|
||||||
|
|
||||||
$expected_hash = '{SHA1}qUqP5cyxm6YcTAhz05Hph5gvu9M=';
|
$expected_hash = '{SHA1}qUqP5cyxm6YcTAhz05Hph5gvu9M=';
|
||||||
@ -87,6 +86,13 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase
|
|||||||
|
|
||||||
$sha512 = '{SHA512}ClAmHr0aOQ/tK/Mm8mc8FFWCpjQtUjIElz0CGTN/gWFqgGmwElh89WNfaSXxtWw2AjDBmyc1AO4BPgMGAb8kJQ=='; // foobar
|
$sha512 = '{SHA512}ClAmHr0aOQ/tK/Mm8mc8FFWCpjQtUjIElz0CGTN/gWFqgGmwElh89WNfaSXxtWw2AjDBmyc1AO4BPgMGAb8kJQ=='; // foobar
|
||||||
$this->assertNotEquals($sha512, _pacrypt_dovecot('foobarbaz', $sha512));
|
$this->assertNotEquals($sha512, _pacrypt_dovecot('foobarbaz', $sha512));
|
||||||
|
|
||||||
|
$CONF['encrypt'] = 'dovecot:DIGEST-MD5';
|
||||||
|
|
||||||
|
$expected_hash = '{DIGEST-MD5}dad736686b7d1f1db09f3dc9ff538e03';
|
||||||
|
$username = 'test@mail.com';
|
||||||
|
|
||||||
|
$this->assertEquals($expected_hash, _pacrypt_dovecot('test', '', $username));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ $CONF['language_hook'] = '';
|
|||||||
|
|
||||||
if (getenv('DATABASE') == 'sqlite' || getenv('DATABASE') == false) {
|
if (getenv('DATABASE') == 'sqlite' || getenv('DATABASE') == false) {
|
||||||
$version = PHP_VERSION_ID; // try and stop different tests running at the same trying to use the same sqlite db at once
|
$version = PHP_VERSION_ID; // try and stop different tests running at the same trying to use the same sqlite db at once
|
||||||
$db_file = dirname(__FILE__) . '/postfixadmin.sqlite.' . $version . '.test';
|
$db_file = tempnam(sys_get_temp_dir(), 'postfixadmin-test');
|
||||||
$CONF['database_type'] = 'sqlite';
|
$CONF['database_type'] = 'sqlite';
|
||||||
$CONF['database_name'] = $db_file;
|
$CONF['database_name'] = $db_file;
|
||||||
Config::write('database_type', 'sqlite');
|
Config::write('database_type', 'sqlite');
|
||||||
|
Loading…
Reference in New Issue
Block a user