parent
8b0201f117
commit
f1e95758e1
12 changed files with 450 additions and 17 deletions
|
@ -245,6 +245,71 @@ class Deliver_SMTP extends Deliver {
|
|||
);
|
||||
if (boolean_hook_function('smtp_authenticate', $smtp_auth_args, 1)) {
|
||||
// authentication succeeded
|
||||
} else if ( substr($smtp_auth_mech, 0, 6) == 'scram-' ) {
|
||||
// Doing SCRAM
|
||||
$hAlg = scram_supports(substr($smtp_auth_mech, 6));
|
||||
if ($hAlg === false) {
|
||||
return(0);
|
||||
}
|
||||
fputs($stream, 'AUTH '.strtoupper($smtp_auth_args)."\r\n");
|
||||
|
||||
$tmp = fgets($stream,1024);
|
||||
|
||||
if ($this->errorCheck($tmp,$stream)) {
|
||||
return(0);
|
||||
}
|
||||
// At this point, $tmp should hold "334 "
|
||||
|
||||
|
||||
// No Channel Binding support...
|
||||
$cbf = 'n';
|
||||
// Generate 20 random bytes
|
||||
$cliNonce = scram_nonce();
|
||||
// Build SCRAM request
|
||||
$scram_request = scram_request($user, $cbf, $cliNonce);
|
||||
|
||||
fputs($stream, $scram_request."\r\n");
|
||||
|
||||
// Get SCRAM response
|
||||
$tmp = fgets($stream,1024);
|
||||
if ($this->errorCheck($tmp,$stream)) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
// At this point, $tmp should hold "334 <challenge string>"
|
||||
$chall = substr($tmp,4);
|
||||
|
||||
$serData = scram_parse_challenge($chall, $cliNonce);
|
||||
if ($serData === false) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
$scram_response = scram_response($hAlg, $user, $cbf, $cliNonce, $serData['r'], $pass, $serData['s'], $serData['i']);
|
||||
|
||||
fputs($stream, $scram_response."\r\n");
|
||||
|
||||
// Get SCRAM validation
|
||||
$tmp = fgets($stream,1024);
|
||||
if ($this->errorCheck($tmp,$stream)) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
// At this point, $tmp should hold "334 <server verification string>"
|
||||
$serVer = substr($tmp,4);
|
||||
|
||||
$valid = scram_verify($hAlg, $user, $cbf, $cliNonce, $serData['r'], $pass, $serData['s'], $serData['i'], $serVer);
|
||||
|
||||
if ($valid === false) {
|
||||
return(0);
|
||||
}
|
||||
// TODO: For compatibility, this may need to either be blank or NOOP. Find out which!!!
|
||||
fputs($stream, "\r\n");
|
||||
// Just to make sure we're really logged in
|
||||
$tmp = fgets($stream,1024);
|
||||
if ($this->errorCheck($tmp,$stream)) {
|
||||
return(0);
|
||||
}
|
||||
// SCRAM code ends here
|
||||
} else if (( $smtp_auth_mech == 'cram-md5') or ( $smtp_auth_mech == 'digest-md5' )) {
|
||||
// Doing some form of non-plain auth
|
||||
if ($smtp_auth_mech == 'cram-md5') {
|
||||
|
|
|
@ -1492,7 +1492,7 @@ sub command111 {
|
|||
return $new_optional_delimiter;
|
||||
}
|
||||
# IMAP authentication type
|
||||
# Possible values: login, plain, cram-md5, digest-md5
|
||||
# Possible values: login, plain, cram-md5, digest-md5, scram-[digest-algo]
|
||||
# Now offers to detect supported mechs, assuming server & port are set correctly
|
||||
|
||||
sub command112a {
|
||||
|
@ -1504,7 +1504,7 @@ sub command112a {
|
|||
} else {
|
||||
print "If you have already set the hostname and port number, I can try to\n";
|
||||
print "detect the mechanisms your IMAP server supports.\n";
|
||||
print "I will try to detect CRAM-MD5 and DIGEST-MD5 support. I can't test\n";
|
||||
print "I will try to detect SCRAM, CRAM-MD5, and DIGEST-MD5 support. I can't test\n";
|
||||
print "for \"login\" or \"plain\" without knowing a username and password.\n";
|
||||
print "Auto-detecting is optional - you can safely say \"n\" here.\n";
|
||||
print "\nTry to detect supported mechanisms? [y/N]: ";
|
||||
|
@ -1514,6 +1514,30 @@ sub command112a {
|
|||
# Yes, let's try to detect.
|
||||
print "Trying to detect IMAP capabilities...\n";
|
||||
my $host = $imapServerAddress . ':'. $imapPort;
|
||||
print "SCRAM-SHA-1:\t";
|
||||
my $tmp = detect_auth_support('IMAP',$host,'SCRAM-SHA-1');
|
||||
if (defined($tmp)) {
|
||||
if ($tmp eq 'YES') {
|
||||
print "$WHT SUPPORTED$NRM\n";
|
||||
} else {
|
||||
print "$WHT NOT SUPPORTED$NRM\n";
|
||||
}
|
||||
} else {
|
||||
print $WHT . " ERROR DETECTING$NRM\n";
|
||||
}
|
||||
|
||||
print "SCRAM-SHA-256:\t";
|
||||
my $tmp = detect_auth_support('IMAP',$host,'SCRAM-SHA-256');
|
||||
if (defined($tmp)) {
|
||||
if ($tmp eq 'YES') {
|
||||
print "$WHT SUPPORTED$NRM\n";
|
||||
} else {
|
||||
print "$WHT NOT SUPPORTED$NRM\n";
|
||||
}
|
||||
} else {
|
||||
print $WHT . " ERROR DETECTING$NRM\n";
|
||||
}
|
||||
|
||||
print "CRAM-MD5:\t";
|
||||
my $tmp = detect_auth_support('IMAP',$host,'CRAM-MD5');
|
||||
if (defined($tmp)) {
|
||||
|
@ -1545,12 +1569,15 @@ sub command112a {
|
|||
print $WHT . "plain" . $NRM . " - SASL PLAIN. If you need this, you already know it.\n";
|
||||
print $WHT . "cram-md5" . $NRM . " - Historic. No longer considered secure.\n";
|
||||
print $WHT . "digest-md5" . $NRM . " - Historic. No longer considered secure.\n";
|
||||
print $WHT . "scram-sha-1" . $NRM . " - Salted and hashed. Security contested.\n";
|
||||
print $WHT . "scram-sha-256" . $NRM . " - Salted and hashed. Safer than sha-1.\n";
|
||||
print "\n*** YOUR IMAP SERVER MUST SUPPORT THE MECHANISM YOU CHOOSE HERE ***\n";
|
||||
print "If you don't understand or are unsure, you probably want \"login\"\n\n";
|
||||
print "login, plain, cram-md5, or digest-md5 [$WHT$imap_auth_mech$NRM]: $WHT";
|
||||
print "login, plain, cram-md5, digest-md5, scram-* [$WHT$imap_auth_mech$NRM]: $WHT";
|
||||
$inval=<STDIN>;
|
||||
chomp($inval);
|
||||
if ( ($inval =~ /^cram-md5\b/i) || ($inval =~ /^digest-md5\b/i) || ($inval =~ /^login\b/i) || ($inval =~ /^plain\b/i)) {
|
||||
if ( ($inval =~ /^cram-md5\b/i) || ($inval =~ /^digest-md5\b/i) || ($inval =~ /^scram-.+\b/i) ||
|
||||
($inval =~ /^login\b/i) || ($inval =~ /^plain\b/i)) {
|
||||
return lc($inval);
|
||||
} else {
|
||||
# user entered garbage or default value so nothing needs to be set
|
||||
|
@ -1560,7 +1587,7 @@ sub command112a {
|
|||
|
||||
|
||||
# SMTP authentication type
|
||||
# Possible choices: none, login, plain, cram-md5, digest-md5
|
||||
# Possible choices: none, login, plain, cram-md5, digest-md5, scram-[digest-algo]
|
||||
sub command112b {
|
||||
if ($use_smtp_tls ne "0") {
|
||||
print "Auto-detection of login methods is unavailable when using TLS or STARTTLS.\n";
|
||||
|
@ -1641,7 +1668,6 @@ sub command112b {
|
|||
print $WHT . "ERROR DETECTING$NRM\n";
|
||||
}
|
||||
|
||||
|
||||
print "Testing DIGEST-MD5:\t";
|
||||
$tmp=detect_auth_support('SMTP',$host,'DIGEST-MD5');
|
||||
if (defined($tmp)) {
|
||||
|
@ -1653,6 +1679,32 @@ sub command112b {
|
|||
} else {
|
||||
print $WHT . "ERROR DETECTING$NRM\n";
|
||||
}
|
||||
|
||||
# Try SCRAM-SHA-1
|
||||
print "Testing SCRAM-SHA-1:\t";
|
||||
$tmp=detect_auth_support('SMTP',$host,'SCRAM-SHA-1');
|
||||
if (defined($tmp)) {
|
||||
if ($tmp eq 'YES') {
|
||||
print $WHT . "SUPPORTED$NRM\n";
|
||||
} else {
|
||||
print $WHT . "NOT SUPPORTED$NRM\n";
|
||||
}
|
||||
} else {
|
||||
print $WHT . "ERROR DETECTING$NRM\n";
|
||||
}
|
||||
|
||||
# Try SCRAM-SHA-256
|
||||
print "Testing SCRAM-SHA-256:\t";
|
||||
$tmp=detect_auth_support('SMTP',$host,'SCRAM-SHA-256');
|
||||
if (defined($tmp)) {
|
||||
if ($tmp eq 'YES') {
|
||||
print $WHT . "SUPPORTED$NRM\n";
|
||||
} else {
|
||||
print $WHT . "NOT SUPPORTED$NRM\n";
|
||||
}
|
||||
} else {
|
||||
print $WHT . "ERROR DETECTING$NRM\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
print "\nWhat authentication mechanism do you want to use for SMTP connections?\n";
|
||||
|
@ -1661,9 +1713,11 @@ sub command112b {
|
|||
print $WHT . "plain" . $NRM . " - SASL PLAIN. Plaintext. If you can do better, you probably should.\n";
|
||||
print $WHT . "cram-md5" . $NRM . " - Historic. No longer considered secure.\n";
|
||||
print $WHT . "digest-md5" . $NRM . " - Historic. No longer considered secure.\n";
|
||||
print $WHT . "scram-sha-1" . $NRM . " - Salted and hashed. Security contested.\n";
|
||||
print $WHT . "scram-sha-256" . $NRM . " - Salted and hashed. Safer than sha-1.\n";
|
||||
print $WHT . "\n*** YOUR SMTP SERVER MUST SUPPORT THE MECHANISM YOU CHOOSE HERE ***\n" . $NRM;
|
||||
print "If you don't understand or are unsure, you probably want \"none\"\n\n";
|
||||
print "none, login, plain, cram-md5, or digest-md5 [$WHT$smtp_auth_mech$NRM]: $WHT";
|
||||
print "none, login, plain, cram-md5, digest-md5, scram-* [$WHT$smtp_auth_mech$NRM]: $WHT";
|
||||
$inval=<STDIN>;
|
||||
chomp($inval);
|
||||
if ($inval =~ /^none\b/i) {
|
||||
|
@ -1672,7 +1726,7 @@ sub command112b {
|
|||
$smtp_sitewide_pass = '';
|
||||
# SMTP doesn't necessarily require logins
|
||||
return "none";
|
||||
} elsif ( ($inval =~ /^cram-md5\b/i) || ($inval =~ /^digest-md5\b/i) ||
|
||||
} elsif ( ($inval =~ /^cram-md5\b/i) || ($inval =~ /^digest-md5\b/i) || ($inval =~ /^scram-.+\b/i) ||
|
||||
($inval =~ /^login\b/i) || ($inval =~/^plain\b/i)) {
|
||||
command_smtp_sitewide_userpass($inval);
|
||||
return lc($inval);
|
||||
|
|
|
@ -263,7 +263,10 @@ $use_smtp_tls = 0;
|
|||
/**
|
||||
* SMTP authentication mechanism
|
||||
*
|
||||
* auth_mech can be either 'none', 'login','plain', 'cram-md5', or 'digest-md5'
|
||||
* auth_mech can be either 'none', 'login', 'plain', 'cram-md5', 'digest-md5',
|
||||
* or 'scram-*'. For SCRAM, any algorithm your PHP install supports for both
|
||||
* the hash and hash_hmac functions is supported.
|
||||
* Note that CRAM-MD5 & DIGEST-MD5 are historic and no longer considered safe.
|
||||
* @global string $smtp_auth_mech
|
||||
*/
|
||||
$smtp_auth_mech = 'none';
|
||||
|
@ -293,7 +296,10 @@ $smtp_sitewide_pass = '';
|
|||
/**
|
||||
* IMAP authentication mechanism
|
||||
*
|
||||
* auth_mech can be either 'login','plain', 'cram-md5', or 'digest-md5'
|
||||
* auth_mech can be either 'login', 'plain', 'cram-md5', 'digest-md5', or
|
||||
* 'scram-*'. For SCRAM, any algorithm your PHP install supports for both the
|
||||
* hash and hash_hmac functions is supported.
|
||||
* Note that CRAM-MD5 & DIGEST-MD5 are historic and no longer considered safe.
|
||||
* @global string $imap_auth_mech
|
||||
*/
|
||||
$imap_auth_mech = 'login';
|
||||
|
|
|
@ -431,6 +431,8 @@ Version 1.5.2 - SVN
|
|||
(see notes in config/config_local.example.php for more details)
|
||||
- Added handling for RCDATA and RAWTEXT elements in HTML sanitizer
|
||||
[CVE-2019-12970]
|
||||
- Added SCRAM authentication support (RFC5802) (RFC7677) for IMAP
|
||||
and SMTP
|
||||
|
||||
Version 1.5.1 (branched on 2006-02-12)
|
||||
--------------------------------------
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
IMAP AND SMTP AUTHENTICATION WITH SQUIRRELMAIL
|
||||
$Id$
|
||||
Chris Hilts tassium@squirrelmail.org
|
||||
Andrew Sachen webmaster@realityripple.com
|
||||
**********************************************
|
||||
|
||||
Prior to SquirrelMail 1.4.0, only plaintext logins for IMAP and SMTP were
|
||||
|
@ -12,6 +13,13 @@ SMTP. TLS is able to be enabled on a per-service basis as well.
|
|||
Unless the administrator changes the authentication methods, SquirrelMail
|
||||
will default to the "classic" plaintext methods, without TLS.
|
||||
|
||||
As of 1.5.2, the SCRAM auth mechanism has also been added. This supercedes the
|
||||
now deprecated CRAM-MD5 and DIGEST-MD5 with a salted hash, typically with SHA-1
|
||||
or SHA-256. While SHA-1 is potentially insecure, HMAC makes things much safer,
|
||||
so SCRAM-SHA-1 is still considered functionally secure. If your mail server
|
||||
supports SCRAM, please consider using it, especially if it doesn't support TLS
|
||||
or you aren't using it. More especially if you're still using MD5.
|
||||
|
||||
Note: There is no point in using TLS if your IMAP server is localhost. You need
|
||||
root to sniff the loopback interface, and if you don't trust root, or an attacker
|
||||
already has root, the game is over. You've got a lot more to worry about beyond
|
||||
|
@ -20,6 +28,10 @@ having the loopback interface sniffed.
|
|||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
SCRAM-SHA-1/SCRAM-SHA-256
|
||||
* SquirrelMail 1.5.2 or higher
|
||||
* PHP 7.0 or higher (random_int() function for nonce generation)
|
||||
|
||||
CRAM/DIGEST-MD5
|
||||
* SquirrelMail 1.4.0 or higher
|
||||
* If you have the mhash extension to PHP, it will automatically
|
||||
|
@ -108,7 +120,7 @@ configuration utility.
|
|||
|
||||
These configuration variables will be used to connect to the SMTP server as long
|
||||
as the authentication mechanism is something besides 'none', i.e. 'login',
|
||||
'plain', 'cram-md5', or 'digest-md5'.
|
||||
'plain', 'cram-md5', 'digest-md5', 'scram-sha-1', or 'scram-sha-256'.
|
||||
|
||||
DEBUGGING SSL ERROR MESSAGES
|
||||
----------------------------
|
||||
|
|
|
@ -41,8 +41,9 @@ on our wiki. Links to all these manuals and wikis can be found on our
|
|||
<dt><a href="authentication.txt">Authentication</a></dt>
|
||||
<dd>
|
||||
SquirrelMail allows you to log in to your IMAP and SMTP servers using
|
||||
plaintext, CRAM-MD5 or DIGEST-MD5, as well as use SSL for extra security.
|
||||
This document describes how to use this new code, and the requirements.
|
||||
plaintext, SCRAM, CRAM-MD5 or DIGEST-MD5, as well as use SSL/TLS for
|
||||
extra security. This document describes how to use this new code,
|
||||
and the requirements.
|
||||
</dd>
|
||||
|
||||
<dt><a href="presets.txt">Specific IMAP server setups</a></dt>
|
||||
|
|
|
@ -136,6 +136,224 @@ function sqauth_save_password($pass) {
|
|||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an algorithm is supported by hash() and hash_hmac()
|
||||
*
|
||||
* @param string $algo Algorithm to find.
|
||||
*
|
||||
* @return string Functional $algo as used by hash() and hash_hmac()
|
||||
* or boolean FALSE
|
||||
*
|
||||
* @since 1.5.2
|
||||
*/
|
||||
|
||||
function scram_supports($algo) {
|
||||
$HASHs = hash_algos();
|
||||
if (check_php_version(7,2)) {
|
||||
$HMACs = hash_hmac_algos();
|
||||
$HASHs = array_values(array_intersect($HASHs, $HMACs));
|
||||
}
|
||||
$fAlgo = strtolower(str_replace('-', '', $algo));
|
||||
if (in_array($fAlgo, $HASHs))
|
||||
return $fAlgo;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build client nonce for SCRAM (See RFC 5802 for details)
|
||||
*
|
||||
* @return string A set of twenty random printable ASCII characters
|
||||
*
|
||||
* @since 1.5.2
|
||||
*/
|
||||
function scram_nonce () {
|
||||
// All printable ASCII characters except commas are OK
|
||||
// (For simplicity, we're just going to use letters and numbers, though)
|
||||
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
$max = strlen($chars) - 1;
|
||||
$nonce = '';
|
||||
for($i = 0; $i < 20; $i++) {
|
||||
$rndChr = random_int(0, $max);
|
||||
$nonce.= $chars[$rndChr];
|
||||
}
|
||||
return $nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build client request for SCRAM (See RFC 5802 for details)
|
||||
*
|
||||
* @param string $username User ID
|
||||
* @param string $cbf Channel Binding Flag ('n', 'y', or 'p=tls-unique'/'p=tls-server-end-point')
|
||||
* @param string $nonce Client's random nonce data
|
||||
*
|
||||
* @return string The response to be sent to the server (base64 encoded)
|
||||
*
|
||||
* @since 1.5.2
|
||||
*/
|
||||
function scram_request ($username,$cbf,$nonce) {
|
||||
return base64_encode($cbf.',,n='.$username.',r='.$nonce);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse SCRAM challenge.
|
||||
* This function parses the challenge sent during SCRAM authentication and
|
||||
* returns an array. See the RFC for details on what's in the challenge string.
|
||||
*
|
||||
* @param string $challenge SCRAM Challenge
|
||||
* @param string $nonce Client's random nonce data
|
||||
*
|
||||
* @return array SCRAM challenge decoded data
|
||||
* or boolean FALSE
|
||||
*
|
||||
* @since 1.5.2
|
||||
*/
|
||||
function scram_parse_challenge ($challenge,$nonce) {
|
||||
$chall = base64_decode($challenge, true);
|
||||
if ($chall === false) {
|
||||
// The challenge must be base64 encoded
|
||||
return false;
|
||||
}
|
||||
// Chall should now be r=NONCE,s=SALT,i=ITER
|
||||
$sReq = explode(',', $chall);
|
||||
$serNonce = '';
|
||||
$serSalt = '';
|
||||
$serIter = 0;
|
||||
for($i = 0; $i < count($sReq); $i++) {
|
||||
switch(substr($sReq[$i], 0, 2)) {
|
||||
case 'r=':
|
||||
$serNonce = substr($sReq[$i], 2);
|
||||
break;
|
||||
case 's=':
|
||||
$serSalt = substr($sReq[$i], 2);
|
||||
break;
|
||||
case 'i=':
|
||||
$serIter = substr($sReq[$i], 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (strlen($serNonce) <= strlen($nonce)) {
|
||||
//the server 'r' value must be bigger than the client 'r' value
|
||||
return false;
|
||||
}
|
||||
if (substr($serNonce, 0, strlen($nonce)) !== $nonce) {
|
||||
// The server 'r' value must begin with the client 'r' value
|
||||
return false;
|
||||
}
|
||||
if (is_numeric($serIter)) {
|
||||
$serIter = intval($serIter);
|
||||
} else {
|
||||
// The iteration value must be a number
|
||||
return false;
|
||||
}
|
||||
$serSaltV = base64_decode($serSalt, true);
|
||||
if ($serSaltV === false) {
|
||||
// The salt must be base64-encoded
|
||||
return false;
|
||||
}
|
||||
$parsed = array();
|
||||
$parsed['r'] = $serNonce;
|
||||
$parsed['s'] = $serSaltV;
|
||||
$parsed['i'] = $serIter;
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build SCRAM response to challenge.
|
||||
* This function hashes the heck out of the password and all previous communications
|
||||
* to create a proof value which is then sent to the server as authentication.
|
||||
*
|
||||
* @param string $alg Hash algorithm to use ('sha1' or 'sha256')
|
||||
* @param string $username User ID
|
||||
* @param string $cbf Channel Binding Flag ('n', 'y', or 'p=tls-unique'/'p=tls-server-end-point')
|
||||
* @param string $cli_nonce Client's random nonce data
|
||||
* @param string $ser_nonce Client + Server's random nonce data
|
||||
* @param string $password User password supplied by User
|
||||
* @param string $salt Raw binary salt data, supplied by the server challenge
|
||||
* @param string $iter PBKDF2 iterations, supplied by the server challenge
|
||||
*
|
||||
* @return string The response to be sent to the server (base64 encoded)
|
||||
*
|
||||
* @since 1.5.2
|
||||
*/
|
||||
function scram_response ($alg,$username,$cbf,$cli_nonce,$ser_nonce,$password,$salt,$iter) {
|
||||
// salt and hash password
|
||||
$salted_pass = hash_pbkdf2($alg, $password, $salt, $iter, 0, true);
|
||||
$cli_hash = hash_hmac($alg, 'Client Key', $salted_pass, true);
|
||||
$cli_key = hash($alg, $cli_hash, true);
|
||||
|
||||
$c = base64_encode($cbf.',,');
|
||||
|
||||
//generate unproofed communications
|
||||
$cli_request = 'n='.$username.',r='.$cli_nonce;
|
||||
$ser_challenge = 'r='.$ser_nonce.',s='.base64_encode($salt).',i='.$iter;
|
||||
$cli_response_unp = 'c='.$c.',r='.$ser_nonce;
|
||||
$comm_unp = $cli_request.','.$ser_challenge.','.$cli_response_unp;
|
||||
|
||||
//hash unproofed communications
|
||||
$cli_sig = hash_hmac($alg, $comm_unp, $cli_key, true);
|
||||
$cli_proof = $cli_hash ^ $cli_sig;
|
||||
|
||||
//generate proofed response
|
||||
$cli_response = $cli_response_unp.',p='.base64_encode($cli_proof);
|
||||
|
||||
return base64_encode($cli_response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify SCRAM server response.
|
||||
* The final step in SCRAM is to make sure the server isn't just faking validation.
|
||||
* This is done by hashing the unproofed communications with a 'Server Key'
|
||||
* version of the hashed password, and comparing it with the server's final SCRAM message.
|
||||
*
|
||||
* @param string $alg Hash algorithm to use ('sha1' or 'sha256')
|
||||
* @param string $username User ID
|
||||
* @param string $cbf Channel Binding Flag ('n', 'y', or 'p=tls-unique'/'p=tls-server-end-point')
|
||||
* @param string $cli_nonce Client's random nonce data
|
||||
* @param string $ser_nonce Client + Server's random nonce data
|
||||
* @param string $password User password supplied by User
|
||||
* @param string $salt Raw binary salt data, supplied by the server challenge
|
||||
* @param string $iter PBKDF2 iterations, supplied by the server challenge
|
||||
* @param string $proof The server's final SCRAM message (base64 encoded)
|
||||
*
|
||||
* @return boolean Success or failure
|
||||
*
|
||||
* @since 1.5.2
|
||||
*/
|
||||
function scram_verify ($alg,$username,$cbf,$cli_nonce,$ser_nonce,$password,$salt,$iter,$proof) {
|
||||
$proof = base64_decode($proof, true);
|
||||
if ($proof === false) {
|
||||
// The proof must be base64 encoded
|
||||
return false;
|
||||
}
|
||||
if (substr($proof, 0, 2) !== 'v=') {
|
||||
// The proof was not provided correctly
|
||||
return false;
|
||||
}
|
||||
$proof = substr($proof, 2);
|
||||
$proof = base64_decode($proof, true);
|
||||
if ($proof === false) {
|
||||
// The proof v value must be base64 encoded
|
||||
return false;
|
||||
}
|
||||
// salt and hash password
|
||||
$salted_pass = hash_pbkdf2($alg, $password, $salt, $iter, 0, true);
|
||||
$cli_hash = hash_hmac($alg, 'Client Key', $salted_pass, true);
|
||||
$cli_key = hash($alg, $cli_hash, true);
|
||||
|
||||
$c = base64_encode($cbf.',,');
|
||||
|
||||
//generate unproofed communications
|
||||
$cli_request = 'n='.$username.',r='.$cli_nonce;
|
||||
$ser_challenge = 'r='.$ser_nonce.',s='.base64_encode($salt).',i='.$iter;
|
||||
$cli_response_unp = 'c='.$c.',r='.$ser_nonce;
|
||||
$comm_unp = $cli_request.','.$ser_challenge.','.$cli_response_unp;
|
||||
|
||||
//hash for server
|
||||
$ser_hash = hash_hmac($alg, 'Server Key', $salted_pass, true);
|
||||
$ser_proof = hash_hmac($alg, $comm_unp, $ser_hash, true);
|
||||
return $ser_proof === $proof;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the challenge from the server, supply the response using cram-md5 (See
|
||||
* RFC 2195 for details)
|
||||
|
|
|
@ -879,7 +879,78 @@ function sqimap_login ($username, $password, $imap_server_address,
|
|||
|
||||
$imap_stream = sqimap_create_stream($imap_server_address,$imap_port,$use_imap_tls,$stream_options);
|
||||
|
||||
if (($imap_auth_mech == 'cram-md5') OR ($imap_auth_mech == 'digest-md5')) {
|
||||
if (substr($imap_auth_mech, 0, 6) == 'scram-') {
|
||||
// Doing SCRAM
|
||||
$hAlg = scram_supports(substr($imap_auth_mech, 6));
|
||||
if ($hAlg === false) {
|
||||
$response="BAD";
|
||||
$message='PHP server does not appear to support the authentication method selected.';
|
||||
$message .= ' Please contact your system administrator.';
|
||||
} else {
|
||||
$tag=sqimap_session_id(false);
|
||||
fputs($imap_stream, $tag.' AUTHENTICATE '.strtoupper($imap_auth_mech)."\r\n");
|
||||
$tmp = sqimap_fgets($imap_stream);
|
||||
if ($tmp[0] !== '+') {
|
||||
$response="BAD";
|
||||
$message='IMAP server does not appear to support the authentication method selected.';
|
||||
$message .= ' Please contact your system administrator.';
|
||||
} else {
|
||||
// No Channel Binding support...
|
||||
$cbf = 'n';
|
||||
// Generate 20 random bytes
|
||||
$cliNonce = scram_nonce();
|
||||
// Build SCRAM request
|
||||
$scram_request = scram_request($username, $cbf, $cliNonce);
|
||||
fputs($imap_stream, $scram_request."\r\n");
|
||||
// Get SCRAM response
|
||||
$tmp = sqimap_fgets($imap_stream);
|
||||
if ($tmp[0] !== '+'){
|
||||
$tmp = explode(' ', $tmp, 3);
|
||||
$response=$tmp[1];
|
||||
$message=$tmp[2];
|
||||
} else {
|
||||
// At this point, $tmp should hold "+ <challenge string>"
|
||||
$chall = substr($tmp,2);
|
||||
$serData = scram_parse_challenge($chall, $cliNonce);
|
||||
if ($serData === false) {
|
||||
$response="BAD";
|
||||
$message='IMAP server challenge could not be parsed.';
|
||||
$message .= ' Please contact your system administrator.';
|
||||
} else {
|
||||
$scram_response = scram_response($hAlg, $username, $cbf, $cliNonce, $serData['r'], $password, $serData['s'], $serData['i']);
|
||||
|
||||
fputs($imap_stream, $scram_response."\r\n");
|
||||
|
||||
// Get SCRAM validation
|
||||
$tmp = sqimap_fgets($imap_stream);
|
||||
if ($tmp[0] !== '+'){
|
||||
$tmp = explode(' ', $tmp, 3);
|
||||
$response=$tmp[1];
|
||||
$message=$tmp[2];
|
||||
} else {
|
||||
// At this point, $tmp should hold "+ <server verification string>"
|
||||
$serVer = substr($tmp,2);
|
||||
$valid = scram_verify($hAlg, $username, $cbf, $cliNonce, $serData['r'], $password, $serData['s'], $serData['i'], $serVer);
|
||||
if ($valid === false) {
|
||||
$response="BAD";
|
||||
$message='IMAP server challenge response could not be verified.';
|
||||
$message .= ' Please contact your system administrator.';
|
||||
} else {
|
||||
fputs($imap_stream, "\r\n");
|
||||
|
||||
// Just to make sure we're really logged in
|
||||
$tmp = sqimap_fgets($imap_stream);
|
||||
$tmp = explode(' ', $tmp, 3);
|
||||
$response=$tmp[1];
|
||||
$message=$tmp[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// SCRAM code ends here
|
||||
} else if (($imap_auth_mech == 'cram-md5') OR ($imap_auth_mech == 'digest-md5')) {
|
||||
// We're using some sort of authentication OTHER than plain or login
|
||||
$tag=sqimap_session_id(false);
|
||||
if ($imap_auth_mech == 'digest-md5') {
|
||||
|
|
|
@ -138,6 +138,8 @@ $defcfg = array( '$config_version' => array( 'name' => _("Config File Version"),
|
|||
'$imap_auth_mech' => array( 'name' => _("IMAP Authentication Type"),
|
||||
'type' => SMOPT_TYPE_STRLIST,
|
||||
'posvals' => array('login' => _("IMAP login"),
|
||||
'scram-sha-1' => 'SCRAM-SHA-1',
|
||||
'scram-sha-256' => 'SCRAM-SHA-256',
|
||||
'cram-md5' => 'CRAM-MD5',
|
||||
'digest-md5' => 'DIGEST-MD5'),
|
||||
'default' => 'login' ),
|
||||
|
@ -166,6 +168,8 @@ $defcfg = array( '$config_version' => array( 'name' => _("Config File Version"),
|
|||
'type' => SMOPT_TYPE_STRLIST,
|
||||
'posvals' => array('none' => _("No SMTP auth"),
|
||||
'login' => _("Login (plain text)"),
|
||||
'scram-sha-1' => 'SCRAM-SHA-1',
|
||||
'scram-sha-256' => 'SCRAM-SHA-256',
|
||||
'cram-md5' => 'CRAM-MD5',
|
||||
'digest-md5' => 'DIGEST-MD5'),
|
||||
'default' => 'none'),
|
||||
|
|
|
@ -243,7 +243,7 @@ msgstr ""
|
|||
msgid "The IMAP server is reporting that plain text logins are disabled."
|
||||
msgstr ""
|
||||
|
||||
msgid "Using CRAM-MD5 or DIGEST-MD5 authentication instead may work."
|
||||
msgid "Using SCRAM, CRAM-MD5, or DIGEST-MD5 authentication instead may work."
|
||||
msgstr ""
|
||||
|
||||
msgid "Also, the use of TLS may allow SquirrelMail to login."
|
||||
|
|
|
@ -804,7 +804,7 @@ echo $IND . 'Capabilities: <tt>'.sm_encode_html_special_chars($capline)."</tt><b
|
|||
|
||||
if($imap_auth_mech == 'login' && stristr($capline, 'LOGINDISABLED') !== FALSE) {
|
||||
do_err('Your server doesn\'t allow plaintext logins. '.
|
||||
'Try enabling another authentication mechanism like CRAM-MD5, DIGEST-MD5 or TLS-encryption '.
|
||||
'Try enabling another authentication mechanism like SCRAM, CRAM-MD5, DIGEST-MD5 or TLS-encryption '.
|
||||
'in the SquirrelMail configuration.', FALSE);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ if($imap_auth_mech == 'login') {
|
|||
sqimap_logout($imap);
|
||||
if ($logindisabled) {
|
||||
$string = _("The IMAP server is reporting that plain text logins are disabled.").'<br />'.
|
||||
_("Using CRAM-MD5 or DIGEST-MD5 authentication instead may work.").'<br />';
|
||||
_("Using SCRAM, CRAM-MD5, or DIGEST-MD5 authentication instead may work.").'<br />';
|
||||
if (!$use_imap_tls) {
|
||||
$string .= _("Also, the use of TLS may allow SquirrelMail to login.").'<br />';
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue