From 47a2932647c61c732b649a27b5dc7853bb6388ff Mon Sep 17 00:00:00 2001 From: tassium Date: Wed, 6 Nov 2002 18:55:13 +0000 Subject: [PATCH] Merging in the tassium-auth branch. git-svn-id: https://svn.code.sf.net/p/squirrelmail/code/trunk/squirrelmail@4118 7612ce4b-ef26-0410-bec9-ea0150e637f0 --- class/deliver/Deliver_SMTP.class.php | 140 ++++++++++++------ config/conf.pl | 208 ++++++++++++++++++++++----- config/config_default.php | 18 +++ doc/rfc_documents.txt | 2 + functions/auth.php | 113 +++++++++++++++ functions/imap_general.php | 61 +++++++- src/compose.php | 16 ++- src/read_body.php | 10 +- 8 files changed, 477 insertions(+), 91 deletions(-) diff --git a/class/deliver/Deliver_SMTP.class.php b/class/deliver/Deliver_SMTP.class.php index 756b298c..4c8e49a0 100644 --- a/class/deliver/Deliver_SMTP.class.php +++ b/class/deliver/Deliver_SMTP.class.php @@ -22,18 +22,24 @@ class Deliver_SMTP extends Deliver { } function initStream($message, $domain, $length=0, $host='', $port='', $user='', $pass='', $authpop=false) { - - if ($authpop) { - $this->authPop($host, '', $user, $pass); + global $use_smtp_tls,$smtp_auth_mech,$username,$key,$onetimepad; + + if ($authpop) { + $this->authPop($host, '', $username, $pass); } - - $rfc822_header = $message->rfc822_header; + + $rfc822_header = $message->rfc822_header; $from = $rfc822_header->from[0]; $to = $rfc822_header->to; $cc = $rfc822_header->cc; - $bcc = $rfc822_header->bcc; + $bcc = $rfc822_header->bcc; + + if (($use_smtp_tls == true) and (check_php_version(4,3)) and (extension_loaded('openssl'))) { + $stream = fsockopen('tls://' . $host, $port, $errorNumber, $errorString); + } else { + $stream = fsockopen($host, $port, $errorNumber, $errorString); + } - $stream = fsockopen($host, $port, $errorNumber, $errorString); if (!$stream) { $this->dlv_msg = $errorString; $this->dlv_ret_nr = $errorNumber; @@ -45,36 +51,84 @@ class Deliver_SMTP extends Deliver { } /* Lets introduce ourselves */ - if (! isset ($use_authenticated_smtp) - || $use_authenticated_smtp == false) { - fputs($stream, "HELO $domain\r\n"); - $tmp = fgets($stream, 1024); - if ($this->errorCheck($tmp, $stream)) { - return(0); - } - } else { - fputs($stream, "EHLO $domain\r\n"); - $tmp = fgets($stream, 1024); - if ($this->errorCheck($tmp, $stream)) { - return(0); - } - fputs($stream, "AUTH LOGIN\r\n"); - $tmp = fgets($stream, 1024); + if ((( $smtp_auth_mech == 'cram-md5') or ( $smtp_auth_mech == 'digest-md5' )) and (extension_loaded('mhash'))) + { + // Doing some form of non-plain auth + fputs($stream, "EHLO $domain\r\n"); + $tmp = fgets($stream,1024); + if ($this->errorCheck($tmp,$stream)) { + return(0); + } + if ($smtp_auth_mech == 'cram-md5') { + fputs($stream, "AUTH CRAM-MD5\r\n"); + } elseif ($smtp_auth_mech == 'digest-md5') { + fputs($stream, "AUTH DIGEST-MD5\r\n"); + } + $tmp = fgets($stream,1024); + + if ($this->errorCheck($tmp,$stream)) { + return(0); + } + + // At this point, $tmp should hold "334 " + $chall = substr($tmp,4); + // Depending on mechanism, generate response string + if ($smtp_auth_mech == 'cram-md5') { + $response = cram_md5_response($username,$pass,$chall); + } elseif ($smtp_auth_mech == 'digest-md5') { + $response = digest_md5_response($username,$pass,$chall,'smtp',$host); + } + fputs($stream, $response); + + // Let's see what the server had to say about that + $tmp = fgets($stream,1024); + if ($this->errorCheck($tmp,$stream)) { + return(0); + } + + // CRAM-MD5 is done at this point. If DIGEST-MD5, there's a bit more to go + if ($smtp_auth_mech == 'digest-md5') + { + // $tmp contains rspauth, but I don't store that yet. (No need yet) + fputs($stream,"\r\n"); + $tmp = fgets($stream,1024); + + if ($this->errorCheck($tmp,$stream)) { + return(0); + } + } + // CRAM-MD5 and DIGEST-MD5 code ends here + } elseif ($smtp_auth_mech == 'none') { + // No auth at all, just send helo and then send the mail + fputs($stream, "HELO $domain\r\n"); + $tmp = fgets($stream, 1024); + if ($this->errorCheck($tmp, $stream)) { + return(0); + } + } elseif ($smtp_auth_mech == 'plain') { + // The plain LOGIN method + fputs($stream, "EHLO $domain\r\n"); + $tmp = fgets($stream, 1024); + if ($this->errorCheck($tmp, $stream)) { + return(0); + } + fputs($stream, "AUTH LOGIN\r\n"); + $tmp = fgets($stream, 1024); - if ($this->errorCheck($tmp, $stream)) { - return(0); - } - fputs($stream, base64_encode ($user) . "\r\n"); - $tmp = fgets($stream, 1024); - if ($this->errorCheck($tmp, $stream)) { - return(0); - } + if ($this->errorCheck($tmp, $stream)) { + return(0); + } + fputs($stream, base64_encode ($username) . "\r\n"); + $tmp = fgets($stream, 1024); + if ($this->errorCheck($tmp, $stream)) { + return(0); + } - fputs($stream, base64_encode($pass) . "\r\n"); - $tmp = fgets($stream, 1024); - if ($this->errorCheck($tmp, $stream)) { - return(0); - } + fputs($stream, base64_encode($pass) . "\r\n"); + $tmp = fgets($stream, 1024); + if ($this->errorCheck($tmp, $stream)) { + return(0); + } } /* Ok, who is sending the message? */ @@ -139,7 +193,7 @@ class Deliver_SMTP extends Deliver { function errorCheck($line, $smtpConnection) { global $color, $compose_new_win; - + /* Read new lines on a multiline response */ $lines = $line; while(ereg("^[0-9]+-", $line)) { @@ -178,18 +232,21 @@ class Deliver_SMTP extends Deliver { case 221: $message = 'Service closing transmission channel'; $status = 5; break; - case 421: $message = 'Service not available, closing chanel'; + case 421: $message = 'Service not available, closing channel'; $status = 0; break; - case 235: return(5); - break; + case 235: $message = 'Authentication successful'; + $status = 5; + break; case 250: $message = 'Requested mail action okay, completed'; $status = 5; break; case 251: $message = 'User not local; will forward'; $status = 5; break; - case 334: return(5); break; + case 334: $message = 'OK - continue request'; + $status = 5; + break; case 450: $message = 'Requested mail action not taken: mailbox unavailable'; $status = 0; break; @@ -234,6 +291,9 @@ class Deliver_SMTP extends Deliver { $status = 0; break; /* end RFC2554 */ + case 535: $message = 'Authentication failed'; + $status = 0; + break; default: $message = 'Unknown response: '. nl2br(htmlspecialchars($lines)); $status = 0; $err_num = '001'; diff --git a/config/conf.pl b/config/conf.pl index 2215074e..f0ee8f82 100755 --- a/config/conf.pl +++ b/config/conf.pl @@ -270,9 +270,6 @@ if ( !$force_username_lowercase ) { if ( !$optional_delimiter ) { $optional_delimiter = "detect"; } -if ( !$use_authenticated_smtp ) { - $use_authenticated_smtp = "false"; -} if ( !$auto_create_special ) { $auto_create_special = "false"; } @@ -334,6 +331,22 @@ if ( !$prefs_val_field ) { $prefs_val_field = 'prefval'; } +if ( !$use_smtp_tls) { + $use_smtp_tls= 'false'; +} + +if ( !$smtp_auth_mech ) { + $smtp_auth_mech = 'none'; +} + +if ( !$use_imap_tls ) { + $use_imap_tls = 'false'; +} + +if ( !$imap_auth_mech ) { + $imap_auth_mech = 'plain'; +} + if ( $ARGV[0] eq '--install-plugin' ) { print "Activating plugin " . $ARGV[1] . "\n"; push @plugins, $ARGV[1]; @@ -398,28 +411,67 @@ while ( ( $command ne "q" ) && ( $command ne "Q" ) ) { print "\n"; print "R Return to Main Menu\n"; } elsif ( $menu == 2 ) { - print $WHT. "Server Settings\n" . $NRM; - print "1. Domain : $WHT$domain$NRM\n"; - print "2. IMAP Server : $WHT$imapServerAddress$NRM\n"; - print "3. IMAP Port : $WHT$imapPort$NRM\n"; - print "4. Use Sendmail/SMTP : $WHT"; + print $WHT. "Server Settings\n\n" . $NRM; + print $WHT . "General" . $NRM . "\n"; + print "-------\n"; + print "1. Domain : $WHT$domain$NRM\n"; + print "2. Invert Time : $WHT$invert_time$NRM\n"; + print "3. Sendmail or SMTP : $WHT"; if ( lc($useSendmail) eq "true" ) { print "Sendmail"; } else { print "SMTP"; } print "$NRM\n"; - if ( lc($useSendmail) eq "true" ) { - print "5. Sendmail Path : $WHT$sendmail_path$NRM\n"; - } else { - print "6. SMTP Server : $WHT$smtpServerAddress$NRM\n"; - print "7. SMTP Port : $WHT$smtpPort$NRM\n"; - print "8. Authenticated SMTP : $WHT$use_authenticated_smtp$NRM\n"; - print "9. POP Before SMTP : $WHT$pop_before_smtp$NRM\n"; + print "\n"; + + if ( $show_imap_settings ) { + print $WHT . "IMAP Settings". $NRM . "\n--------------\n"; + print "4. IMAP Server : $WHT$imapServerAddress$NRM\n"; + print "5. IMAP Port : $WHT$imapPort$NRM\n"; + print "6. Authentication type : $WHT$imap_auth_mech$NRM\n"; + print "7. Secure IMAP (TLS) : $WHT$use_imap_tls$NRM\n"; + print "8. Server software : $WHT$imap_server_type$NRM\n"; + print "9. Delimiter : $WHT$optional_delimiter$NRM\n"; + print "\n"; + } elsif ( $show_smtp_settings ) { + if ( lc($useSendmail) eq "true" ) { + print $WHT . "Sendmail" . $NRM . "\n--------\n"; + print "4. Sendmail Path : $WHT$sendmail_path$NRM\n"; + print "\n"; + } else { + print $WHT . "SMTP Settings" . $NRM . "\n-------------\n"; + print "4. SMTP Server : $WHT$smtpServerAddress$NRM\n"; + print "5. SMTP Port : $WHT$smtpPort$NRM\n"; + print "6. POP before SMTP : $WHT$pop_before_smtp$NRM\n"; + print "7. SMTP Authentication : $WHT$smtp_auth_mech$NRM\n"; + print "8. Secure SMTP (TLS) : $WHT$use_smtp_tls$NRM\n"; + print "\n"; + } } - print "10. Server : $WHT$imap_server_type$NRM\n"; - print "11. Invert Time : $WHT$invert_time$NRM\n"; - print "12. Delimiter : $WHT$optional_delimiter$NRM\n"; + + if ($show_imap_settings == 0) { + print "A. Update IMAP Settings : "; + print "$WHT$imapServerAddress$NRM:"; + print "$WHT$imapPort$NRM "; + print "($WHT$imap_server_type$NRM)\n"; + } + if ($show_smtp_settings == 0) { + if ( lc($useSendmail) eq "true" ) { + print "B. Change Sendmail Config : $WHT$sendmail_path$NRM\n"; + } else { + print "B. Update SMTP Settings : "; + print "$WHT$smtpServerAddress$NRM:"; + print "$WHT$smtpPort$NRM\n"; + } + } + if ( $show_smtp_settings || $show_imap_settings ) + { + print "H. Hide " . + ($show_imap_settings ? "IMAP Server" : + (lc($useSendmail) eq "true") ? "Sendmail" : "SMTP") . " Settings\n"; + } + print "\n"; print "R Return to Main Menu\n"; } elsif ( $menu == 3 ) { @@ -600,18 +652,30 @@ while ( ( $command ne "q" ) && ( $command ne "Q" ) ) { elsif ( $command == 9 ) { $provider_name = command8(); } } elsif ( $menu == 2 ) { - if ( $command == 1 ) { $domain = command11(); } - elsif ( $command == 2 ) { $imapServerAddress = command12(); } - elsif ( $command == 3 ) { $imapPort = command13(); } - elsif ( $command == 4 ) { $useSendmail = command14(); } - elsif ( $command == 5 ) { $sendmail_path = command15(); } - elsif ( $command == 6 ) { $smtpServerAddress = command16(); } - elsif ( $command == 7 ) { $smtpPort = command17(); } - elsif ( $command == 8 ) { $use_authenticated_smtp = command18(); } - elsif ( $command == 9 ) { $pop_before_smtp = command18a(); } - elsif ( $command == 10 ) { $imap_server_type = command19(); } - elsif ( $command == 11 ) { $invert_time = command110(); } - elsif ( $command == 12 ) { $optional_delimiter = command111(); } + if ( $command eq "a" ) { $show_imap_settings = 1; $show_smtp_settings = 0; } + elsif ( $command eq "b" ) { $show_imap_settings = 0; $show_smtp_settings = 1; } + elsif ( $command eq "h" ) { $show_imap_settings = 0; $show_smtp_settings = 0; } + elsif ( $command <= 3 ) { + if ( $command == 1 ) { $domain = command11(); } + elsif ( $command == 2 ) { $invert_time = command110(); } + elsif ( $command == 3 ) { $useSendmail = command14(); } + $show_imap_settings = 0; $show_smtp_settings = 0; + } elsif ( $show_imap_settings ) { + if ( $command == 4 ) { $imapServerAddress = command12(); } + elsif ( $command == 5 ) { $imapPort = command13(); } + elsif ( $command == 6 ) { $imap_auth_mech = command112("IMAP",$imap_auth_mech); } + elsif ( $command == 7 ) { $use_imap_tls = command113("IMAP",$use_imap_tls); } + elsif ( $command == 8 ) { $imap_server_type = command19(); } + elsif ( $command == 9 ) { $optional_delimiter = command111(); } + } elsif ( $show_smtp_settings && lc($useSendmail) eq "true" ) { + if ( $command == 4 ) { $sendmail_path = command15(); } + } elsif ( $show_smtp_settings ) { + if ( $command == 4 ) { $smtpServerAddress = command16(); } + elsif ( $command == 5 ) { $smtpPort = command17(); } + elsif ( $command == 6 ) { $pop_before_smtp = command18a(); } + elsif ( $command == 7 ) { $smtp_auth_mech = command112("SMTP",$smtp_auth_mech); } + elsif ( $command == 8 ) { $use_smtp_tls = command113("SMTP",$use_smtp_tls); } + } } elsif ( $menu == 3 ) { if ( $command == 1 ) { $default_folder_prefix = command21(); } elsif ( $command == 2 ) { $show_prefix_option = command22(); } @@ -852,7 +916,7 @@ sub command11 { # imapServerAddress sub command12 { - print "This is the address where your IMAP server resides.\n"; + print "This is the hostname where your IMAP server can be contacted.\n"; print "[$WHT$imapServerAddress$NRM]: $WHT"; $new_imapServerAddress = ; if ( $new_imapServerAddress eq "\n" ) { @@ -918,7 +982,7 @@ sub command15 { # smtpServerAddress sub command16 { - print "This is the location of your SMTP server.\n"; + print "This is the hostname of your SMTP server.\n"; print "[$WHT$smtpServerAddress$NRM]: $WHT"; $new_smtpServerAddress = ; if ( $new_smtpServerAddress eq "\n" ) { @@ -944,6 +1008,8 @@ sub command17 { # authenticated server sub command18 { + return; + # This sub disabled by tassium - it has been replaced with smtp_auth_mech print "Do you wish to use an authenticated SMTP server? Your server must\n"; print "support this in order for SquirrelMail to work with it. We implemented\n"; print "it according to RFC 2554.\n"; @@ -1044,6 +1110,72 @@ sub command111 { return $new_optional_delimiter; } +# authentication type +# This sub gets reused for IMAP and SMTP, so pass in the server type as an arg +# Second arg is the default value to use +# Possible choices: plain, cram-md5, digest-md5 +# SMTP also can be disabled with 'none' +sub command112 { + my($default_val,$service,$inval); + $service=$_[0]; + $default_val=$_[1]; + print "What authentication mechanism do you want to use for " . $service . " logins?\n"; + if ($service eq "SMTP") { + print $WHT . "none" . $NRM . " - Your SMTP server does not require authorization.\n"; + } + print $WHT . "plain" . $NRM . " - Plaintext. If you can do better, you probably should.\n"; + print $WHT . "cram-md5" . $NRM . " - Slightly better than plaintext. (Requires PHP mhash extension)\n"; + print $WHT . "digest-md5" . $NRM . " - Privacy protection - better than cram-md5. (Requires PHP mhash extension)\n"; + print "\n*** YOUR " . $service . " SERVER MUST SUPPORT THE MECHANISM YOU CHOOSE ***\n"; + print "If you don't understand or are unsure, you probably want \"plain\"\n\n"; + if ($service eq "SMTP") { + print "none, "; + } + print "plain, cram-md5, or digest-md5 [$WHT$default_val$NRM]: $WHT"; + $inval=; + chomp($inval); + if ( ($service eq "SMTP") && ($inval =~ /^none\b/i) ) { + # SMTP doesn't necessarily require logins + return "none"; + } + if ( ($inval =~ /^cram-md5\b/i) || ($inval =~ /^digest-md5\b/i) || + ($inval =~ /^plain\b/i)) { + return lc($inval); + } else { + return $default_val; + } +} + +# TLS +# This sub is reused for IMAP and SMTP +# Args: service name, default value +sub command113 { + my($default_val,$service,$inval); + $service=$_[0]; + $default_val=$_[1]; + print "TLS (Transport Layer Security) encrypts the traffic between server and client.\n"; + print "If you're familiar with SSL, you get the idea.\n"; + print "To use this feature, your " . $service . " server must offer TLS\n"; + print "capability, plus PHP 4.3.x with OpenSSL support.\n"; + print "\nIf your " . $service . " server is localhost, you can safely disable this.\n"; + print "If it is remote, you may wish to seriously consider enabling this.\n"; + print "Enable TLS (y/n) [$WHT"; + if ($default_val eq "true") { + print "y"; + } else { + print "n"; + } + print "$NRM]: $WHT"; + $inval=; + $inval =~ tr/yn//cd; + return "true" if ( $inval eq "y" ); + return "false" if ( $inval eq "n" ); + return $default_val; + + +} + + # MOTD sub command71 { print "\nYou can now create the welcome message that is displayed\n"; @@ -2374,7 +2506,7 @@ sub save_data { # string print CF "\$sendmail_path = '$sendmail_path';\n"; # boolean - print CF "\$use_authenticated_smtp = $use_authenticated_smtp;\n"; +# print CF "\$use_authenticated_smtp = $use_authenticated_smtp;\n"; # boolean print CF "\$pop_before_smtp = $pop_before_smtp;\n"; # string @@ -2526,6 +2658,14 @@ sub save_data { print CF "\$prefs_val_field = '$prefs_val_field';\n"; # boolean print CF "\$no_list_for_subscribe = $no_list_for_subscribe;\n"; + + # string + print CF "\$smtp_auth_mech = '$smtp_auth_mech';\n"; + print CF "\$imap_auth_mech = '$imap_auth_mech';\n"; + # boolean + print CF "\$use_imap_tls = $use_imap_tls;\n"; + print CF "\$use_smtp_tls = $use_smtp_tls;\n"; + print CF "\n"; print CF "/**\n"; @@ -2682,6 +2822,7 @@ sub change_to_SM_path() { # If the path is absolute, don't bother. return "\'" . $old_path . "\'" if ( $old_path eq ''); return "\'" . $old_path . "\'" if ( $old_path =~ /^\// ); + return $old_path if ( $old_path =~ /^\$/); return $old_path if ( $old_path =~ /^SM_PATH/ ); # For relative paths, split on '../' @@ -2719,6 +2860,7 @@ sub change_to_rel_path() { my $new_path = ''; return $old_path if ( $old_path eq ''); + return $old_path if ( $old_path =~ /^\$/ ); return $old_path if ( $old_path =~ /^\// ); return $old_path if ( $old_path =~ /^\.\./ ); diff --git a/config/config_default.php b/config/config_default.php index d37a2fee..c6a79d7d 100644 --- a/config/config_default.php +++ b/config/config_default.php @@ -511,6 +511,24 @@ $noselect_fix_enable = false; global $no_list_for_subscribe; $no_list_for_subscribe = false; +/** + * Advanced authentication options + * CRAM-MD5, DIGEST-MD5, Plain, and TLS + * Set reasonable defaults - you'd never know this was there unless you ask for it + */ +global $use_imap_tls; +global $use_smtp_tls; +$use_imap_tls = false; +$use_smtp_tls = false; + +/* auth_mech can be either 'plain', 'cram-md5', or 'digest-md5' + SMTP can also be 'none' +*/ +global $smtp_auth_mech; +global $imap_auth_mech; +$smtp_auth_mech = 'none'; +$imap_auth_mech = 'plain'; + /** * Make sure there are no characters after the PHP closing * tag below (including newline characters and whitespace). diff --git a/doc/rfc_documents.txt b/doc/rfc_documents.txt index c74be673..afb8075b 100644 --- a/doc/rfc_documents.txt +++ b/doc/rfc_documents.txt @@ -13,8 +13,10 @@ NUMBER DESCRIPTION RFC 1730 IMAP version 4 RFC 2060 IMAP version 4rev1 RFC 2183 Content-Disposition MIME header +RFC 2195 IMAP/POP AUTHorize Extension for Simple Challenge/Response RFC 2298 Details return receipts RFC 2342 The IMAP4 NAMESPACE command RFC 2616 HTTP/1.1 RFC 2683 IMAP4 Implementation Recommendations RFC 2822 Format of an email message (sendmail) +RFC 2831 Using Digest Authentication as a SASL Mechanism (digest-md5) diff --git a/functions/auth.php b/functions/auth.php index 24c92ff0..0de160c9 100644 --- a/functions/auth.php +++ b/functions/auth.php @@ -11,6 +11,24 @@ * $Id$ */ +/* Put in a safety net here, in case a naughty admin didn't run conf.pl when they upgraded */ + +if (! isset($smtp_auth_mech)) { + $smtp_auth_mech = 'none'; +} + +if (! isset($imap_auth_mech)) { + $imap_auth_mech = 'plain'; +} + +if (! isset($use_imap_tls)) { + $use_imap_tls = false; +} + +if (! isset($use_smtp_tls)) { + $use_smtp_tls = false; +} + function is_logged_in() { if ( sqsession_is_registered('user_is_logged_in') ) { @@ -37,4 +55,99 @@ function is_logged_in() { } } +function cram_md5_response ($username,$password,$challenge) { + +/* Given the challenge from the server, supply the response using + cram-md5 (See RFC 2195 for details) + NOTE: Requires mhash extension to PHP +*/ +$challenge=base64_decode($challenge); +$hash=bin2hex(mhash(MHASH_MD5,$challenge,$password)); +$response=base64_encode($username . " " . $hash) . "\r\n"; +return $response; +} + +function digest_md5_response ($username,$password,$challenge,$service,$host) { +/* Given the challenge from the server, calculate and return the response-string + for digest-md5 authentication. (See RFC 2831 for more details) */ + $result=digest_md5_parse_challenge($challenge); + +// verify server supports qop=auth + $qop = explode(",",$result['qop']); + //if (!in_array("auth",$qop)) { + // rfc2831: client MUST fail if no qop methods supported + // return false; + //} + $cnonce = base64_encode(bin2hex(mhash(MHASH_MD5, microtime()))); + $ncount = "00000001"; + + /* This can be auth (authentication only), auth-int (integrity protection), or + auth-conf (confidentiality protection). Right now only auth is supported. + DO NOT CHANGE THIS VALUE */ + $qop_value = "auth"; + + $digest_uri_value = $service . '/' . $host; + + // build the $response_value + //FIXME This will probably break badly if a server sends more than one realm + $string_a1 = utf8_encode($username).":"; + $string_a1 .= utf8_encode($result['realm']).":"; + $string_a1 .= utf8_encode($password); + $string_a1 = mhash(MHASH_MD5, $string_a1); + $A1 = $string_a1 . ":" . $result['nonce'] . ":" . $cnonce; + $A1 = bin2hex(mhash(MHASH_MD5, $A1)); + $A2 = "AUTHENTICATE:$digest_uri_value"; + // If qop is auth-int or auth-conf, A2 gets a little extra + if ($qop_value != 'auth') { + $A2 .= ':00000000000000000000000000000000'; + } + $A2 = bin2hex(mhash(MHASH_MD5, $A2)); + + $string_response = $result['nonce'] . ':' . $ncount . ':' . $cnonce . ':' . $qop_value; + $response_value = bin2hex(mhash(MHASH_MD5, $A1.":".$string_response.":".$A2)); + + $reply = 'charset=utf-8,username="' . $username . '",realm="' . $result["realm"] . '",'; + $reply .= 'nonce="' . $result['nonce'] . '",nc=' . $ncount . ',cnonce="' . $cnonce . '",'; + $reply .= "digest-uri=\"$digest_uri_value\",response=$response_value"; + $reply .= ',qop=' . $qop_value; + $reply = base64_encode($reply); + return $reply . "\r\n"; + +} + +function digest_md5_parse_challenge($challenge) { +/* This function parses the challenge sent during DIGEST-MD5 authentication and + returns an array. See the RFC for details on what's in the challenge string. +*/ + $challenge=base64_decode($challenge); + while (strlen($challenge)) { + if ($challenge{0} == ',') { // First char is a comma, must not be 1st time through loop + $challenge=substr($challenge,1); + } + $key=explode('=',$challenge,2); + $challenge=$key[1]; + $key=$key[0]; + if ($challenge{0} == '"') { + // We're in a quoted value + // Drop the first quote, since we don't care about it + $challenge=substr($challenge,1); + // Now explode() to the next quote, which is the end of our value + $val=explode('"',$challenge,2); + $challenge=$val[1]; // The rest of the challenge, work on it in next iteration of loop + $value=explode(',',$val[0]); + // Now, for those quoted values that are only 1 piece.. + if (sizeof($value) == 1) { + $value=$value[0]; // Convert to non-array + } + } else { + // We're in a "simple" value - explode to next comma + $val=explode(',',$challenge,2); + $challenge=$val[1]; + $value=$val[0]; + } + $parsed["$key"]=$value; + } // End of while loop + return $parsed; +} + ?> diff --git a/functions/imap_general.php b/functions/imap_general.php index 8575e7eb..7efb9736 100755 --- a/functions/imap_general.php +++ b/functions/imap_general.php @@ -12,6 +12,8 @@ */ require_once(SM_PATH . 'functions/page_header.php'); +require_once(SM_PATH . 'functions/auth.php'); + global $sqimap_session_id; $sqimap_session_id = 1; @@ -205,10 +207,16 @@ function sqimap_read_data ($imap_stream, $pre, $handle_errors, &$response, &$mes * will be displayed. This function returns the imap connection handle. */ function sqimap_login ($username, $password, $imap_server_address, $imap_port, $hide) { - global $color, $squirrelmail_language, $onetimepad; + global $color, $squirrelmail_language, $onetimepad, $use_imap_tls, $imap_auth_mech; $imap_server_address = sqimap_get_user_server($imap_server_address, $username); - + $host=$imap_server_address; + + if (($use_imap_tls == true) and (check_php_version(4,3)) and (extension_loaded('openssl'))) { + /* Use TLS by prefixing "tls://" to the hostname */ + $imap_server_address = 'tls://' . $imap_server_address; + } + $imap_stream = fsockopen ( $imap_server_address, $imap_port, $error_number, $error_string, 15); /* Do some error correction */ @@ -229,10 +237,51 @@ function sqimap_login ($username, $password, $imap_server_address, $imap_port, $ /* Decrypt the password */ $password = OneTimePadDecrypt($password, $onetimepad); - $query = 'LOGIN "' . quoteIMAP($username) . '" "' . quoteIMAP($password) . '"'; - $read = sqimap_run_command ($imap_stream, $query, false, $response, $message); - - /* If the connection was not successful, lets see why */ + if ((($imap_auth_mech == 'cram-md5') OR ($imap_auth_mech == 'digest-md5')) AND (extension_loaded('mhash'))) { + // We're using some sort of authentication OTHER than plain + $tag=sqimap_session_id(false); + if ($imap_auth_mech == 'digest-md5') { + $query = $tag . " AUTHENTICATE DIGEST-MD5\r\n"; + } elseif ($imap_auth_mech == 'cram-md5') { + $query = $tag . " AUTHENTICATE CRAM-MD5\r\n"; + } + fputs($imap_stream,$query); + $answer=sqimap_fgets($imap_stream); + // Trim the "+ " off the front + $response=explode(" ",$answer,3); + if ($response[0] == '+') { + // Got a challenge back + $challenge=$response[1]; + if ($imap_auth_mech == 'digest-md5') { + $reply = digest_md5_response($username,$password,$challenge,'imap',$host); + } elseif ($imap_auth_mech == 'cram-md5') { + $reply = cram_md5_response($username,$password,$challenge); + } + fputs($imap_stream,$reply); + $read=sqimap_fgets($imap_stream); + if ($imap_auth_mech == 'digest-md5') { + // DIGEST-MD5 has an extra step.. + if (substr($read,0,1) == '+') { // OK so far.. + fputs($imap_stream,"\r\n"); + $read=sqimap_fgets($imap_stream); + } + } + $results=explode(" ",$read,3); + $response=$results[1]; + $message=$results[2]; + } else { + // Fake the response, so the error trap at the bottom will work + $response="BAD"; + $message='IMAP server does not appear to support the authentication method selected.'; + $message .= ' Please contact your system administrator.'; + } + } else { + // Original PLAIN login code + $query = 'LOGIN "' . quoteIMAP($username) . '" "' . quoteIMAP($password) . '"'; + $read = sqimap_run_command ($imap_stream, $query, false, $response, $message); + } + + /* If the connection was not successful, lets see why */ if ($response != 'OK') { if (!$hide) { if ($response != 'NO') { diff --git a/src/compose.php b/src/compose.php index 03f9124e..36cdf51a 100644 --- a/src/compose.php +++ b/src/compose.php @@ -1322,15 +1322,17 @@ function deliverMessage($composeMessage, $draft=false) { if (!$useSendmail && !$draft) { require_once(SM_PATH . 'class/deliver/Deliver_SMTP.class.php'); $deliver = new Deliver_SMTP(); - global $smtpServerAddress, $smtpPort, $use_authenticated_smtp, $pop_before_smtp; - if ($use_authenticated_smtp) { - global $key, $onetimepad; - $user = $username; - $pass = OneTimePadDecrypt($key, $onetimepad); + global $smtpServerAddress, $smtpPort, $pop_before_smtp, $smtp_auth_mech; + + if ($smtp_auth_mech == 'none') { + $user = ''; + $pass = ''; } else { - $user = ''; - $pass = ''; + global $key, $onetimepad; + $user = $username; + $pass = OneTimePadDecrypt($key, $onetimepad); } + $authPop = (isset($pop_before_smtp) && $pop_before_smtp) ? true : false; $stream = $deliver->initStream($composeMessage,$domain,0, $smtpServerAddress, $smtpPort, $user, $pass, $authPop); diff --git a/src/read_body.php b/src/read_body.php index cfab22a3..ce813fb6 100644 --- a/src/read_body.php +++ b/src/read_body.php @@ -269,14 +269,14 @@ function SendMDN ( $mailbox, $passed_id, $sender, $message, $imapConnection) { } else { require_once(SM_PATH . 'class/deliver/Deliver_SMTP.class.php'); $deliver = new Deliver_SMTP(); - global $smtpServerAddress, $smtpPort, $use_authenticated_smtp, $pop_before_smtp; - if ($use_authenticated_smtp) { + global $smtpServerAddress, $smtpPort, $smtp_auth_mech, $pop_before_smtp; + if ($smtp_auth_mech == 'none') { + $user = ''; + $pass = ''; + } else { global $key, $onetimepad; $user = $username; $pass = OneTimePadDecrypt($key, $onetimepad); - } else { - $user = ''; - $pass = ''; } $authPop = (isset($pop_before_smtp) && $pop_before_smtp) ? true : false; $stream = $deliver->initStream($composeMessage,$domain,0, -- 2.25.1