0
0
mirror of https://github.com/postfixadmin/postfixadmin.git synced 2024-09-19 19:22:14 +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:
Shao Yu-Lung (Allen) 2024-04-12 16:57:19 +08:00 committed by GitHub
parent 53426ac148
commit 0876c368e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 39 additions and 22 deletions

View File

@ -6,14 +6,14 @@ jobs:
lint_etc:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
tools: composer
extensions: sqlite3
extensions: sqlite3, gd
- name: run install.sh
run: /bin/bash install.sh
@ -42,14 +42,21 @@ jobs:
php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]
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
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
tools: composer
extensions: sqlite3
extensions: sqlite3, gd
- name: run install.sh
run: /bin/bash install.sh
@ -61,21 +68,21 @@ jobs:
run: composer install --prefer-dist -n
- name: Build/test
run: composer test
run: sudo -u runner composer test
build_coverage_report:
needs: [testsuite]
continue-on-error: true
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
tools: composer
extensions: sqlite3
extensions: sqlite3, gd
- name: run 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
env:
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}

View File

@ -1060,9 +1060,10 @@ function _pacrypt_authlib($pw, $pw_db)
*
* @param string $pw - plain text password
* @param string $pw_db - encrypted password, or '' for generation.
* @param string $username
* @return string crypted password
*/
function _pacrypt_dovecot($pw, $pw_db = '')
function _pacrypt_dovecot($pw, $pw_db = '', $username = '')
{
global $CONF;
@ -1076,11 +1077,14 @@ function _pacrypt_dovecot($pw, $pw_db = '')
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') {
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";
if (!empty($CONF['dovecotpw'])) {
@ -1105,7 +1109,7 @@ function _pacrypt_dovecot($pw, $pw_db = '')
$pipes = [];
$pipe = proc_open("$dovecotpw '-s' $method$dovepasstest", $spec, $pipes);
$pipe = proc_open("$dovecotpw -s {$method}{$dovepasstest}{$doveadm_options}", $spec, $pipes);
if (!$pipe) {
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_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.
*/
function pacrypt($pw, $pw_db = "")
function pacrypt($pw, $pw_db = "", $username = '')
{
global $CONF;
@ -1382,7 +1387,7 @@ function pacrypt($pw, $pw_db = "")
}
if (preg_match('/^DOVECOT:(.*)$/i', $mechanism, $matches)) {
return _pacrypt_dovecot($pw, $pw_db);
return _pacrypt_dovecot($pw, $pw_db, $username);
}
if (empty($pw_db)) {

View File

@ -36,7 +36,7 @@ class Login
$row = $result[0];
try {
$crypt_password = pacrypt($password, $row['password']);
$crypt_password = pacrypt($password, $row['password'], $username);
} catch (\Exception $e) {
error_log("Error while trying to call pacrypt()");
error_log("" . $e);
@ -136,7 +136,7 @@ class Login
}
$set = array(
'password' => pacrypt($new_password),
'password' => pacrypt($new_password, '', $username),
);
if (Config::bool('password_expiration')) {
@ -226,7 +226,7 @@ class Login
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], []);

View File

@ -882,7 +882,7 @@ abstract class PFAHandler
if (sizeof($result) == 1) {
$row = $result[0];
$crypt_token = pacrypt($token, $row['token']);
$crypt_token = pacrypt($token, $row['token'], $username);
if ($row['token'] == $crypt_token) {
db_update($this->db_table, $this->id_field, $username, array(

View File

@ -35,7 +35,7 @@ class DovecotCrypt extends Crypt
'CLEARTEXT' => array('NONE', 0, null, 'plain_generate'),
'CRAM-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-MD5' => array('HEX', MD5_RESULTLEN, NULL, 'plain_md5_generate'),
//'LDAP-MD5' => array('BASE64', MD5_RESULTLEN, NULL, 'plain_md5_generate'),

View File

@ -73,7 +73,6 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase
$this->markTestSkipped("No /usr/bin/doveadm");
}
$CONF['encrypt'] = 'dovecot:SHA1';
$expected_hash = '{SHA1}qUqP5cyxm6YcTAhz05Hph5gvu9M=';
@ -87,6 +86,13 @@ class PaCryptTest extends \PHPUnit\Framework\TestCase
$sha512 = '{SHA512}ClAmHr0aOQ/tK/Mm8mc8FFWCpjQtUjIElz0CGTN/gWFqgGmwElh89WNfaSXxtWw2AjDBmyc1AO4BPgMGAb8kJQ=='; // foobar
$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));
}

View File

@ -12,7 +12,7 @@ $CONF['language_hook'] = '';
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
$db_file = dirname(__FILE__) . '/postfixadmin.sqlite.' . $version . '.test';
$db_file = tempnam(sys_get_temp_dir(), 'postfixadmin-test');
$CONF['database_type'] = 'sqlite';
$CONF['database_name'] = $db_file;
Config::write('database_type', 'sqlite');