function login($username, $password, $host, $sm_caps = array(), &$errno, &$errmsg) { if (!isset($this->capability)) { $aCaps = $this->capability($sm_caps); } else { $aCaps = $this->capability; } /* supported authentication methods */ $aAuth = array('CRAM-MD5' => false, 'DIGEST-MD5' => false); //,'PLAIN'=>false); $aResult = false; /* get the authentication methods */ foreach ($aCaps as $key => $value) { if (substr($key, 0, 5) == 'AUTH=') { $aAuth[substr($key, 5)] = true; } } /* start with the save authentication methods */ switch ($aAuth) { case $aAuth['DIGEST-MD5']: $query = "AUTHENTICATE DIGEST-MD5"; $sTag = $this->sqimap_run_command($query); /* we expect a command continuation request with challenge*/ $aRes = explode(' ', $this->_sqimap_process_stream($sTag)); $challenge = $aRes[0]; /* create the digest-md5 response */ $reply = digest_md5_response($username, $password, $challenge, 'imap', $host); fputs($this->resource, $reply); /* we expect a command continuation request */ $this->_sqimap_process_stream($sTag); /* need testing !! probably just check for the OK response */ $aresult = $this->_sqimap_process_stream($sTag); if ($aResult['RESPONSE'] == 'OK') { break; } case $aAuth['CRAM-MD5']: $query = "AUTHENTICATE CRAM-MD5"; $sTag = $this->sqimap_run_command($query); /* we expect a command continuation request with challenge*/ $aRes = explode(' ', $this->_sqimap_process_stream($sTag)); $challenge = $aRes[0]; /* create the cram-md5 reply */ $reply = cram_md5_response($username, $password, $challenge, 'imap', $host); fputs($this->resource, $reply); /* need testing !! probably just check for the OK response */ $aResult = $this->_sqimap_process_stream($sTag); if ($aResult['RESPONSE'] == 'OK') { break; } //case $aAuth['PLAIN']: //case $aAuth['PLAIN']: default: if (isset($aCaps['LOGINDISABLED']) && $aCaps['LOGINDISABLED']) { /* we need to do a STARTTLS which isn't supported on a normal socket */ /* error handling */ $errmsg = _("Notify your system administrator, no authentication mechanism found"); $errno = 0; /* TODO fatal error */ return false; } else { /* use normal login */ /* escaping ??? */ $user = ereg_replace('(["\\])', '\\\\1', $username); $pass = ereg_replace('(["\\])', '\\\\1', $password); $query = 'LOGIN "' . $user . '" "' . $pass . '"'; $sTag = $this->sqimap_run_command($query); $aResult = $this->_sqimap_process_stream($sTag); } break; } if ($aResult['RESPONSE'] == 'OK') { return $aResult; } else { /* error handling */ if ($aResult) { $errmsg = $aResult['MESSAGE']; /* optional, check response for NO, BAD or BYE */ $errno = 0; /* TODO */ } else { /* something went terrible wrong, notify the user */ /* TODO */ /* optionally try to initiate a tls connection if PHP version > 4.3 */ /* reconnect and call $this->login again from here and return the result */ } } return false; }
function sqimap_login($username, $password, $imap_server_address, $imap_port, $hide) { global $color, $squirrelmail_language, $onetimepad, $use_imap_tls, $imap_auth_mech; if (!isset($onetimepad) || empty($onetimepad)) { sqgetglobalvar('onetimepad', $onetimepad, SQ_SESSION); } $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 */ if (!$imap_stream) { if (!$hide) { set_up_language($squirrelmail_language, true); require_once SM_PATH . 'functions/display_messages.php'; $string = sprintf(_("Error connecting to IMAP server: %s.") . "<br>\r\n", $imap_server_address) . "{$error_number} : {$error_string}<br>\r\n"; logout_error($string, $color); } exit; } $server_info = fgets($imap_stream, 1024); /* Decrypt the password */ $password = OneTimePadDecrypt($password, $onetimepad); 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') { $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.'; } } elseif ($imap_auth_mech == 'login') { // Original IMAP login code $query = 'LOGIN "' . quoteimap($username) . '" "' . quoteimap($password) . '"'; $read = sqimap_run_command($imap_stream, $query, false, $response, $message); } elseif ($imap_auth_mech == 'plain') { /* Replace this with SASL PLAIN if it ever gets implemented */ $response = "BAD"; $message = 'SquirrelMail does not support SASL PLAIN yet. Rerun conf.pl and use login instead.'; } else { $response = "BAD"; $message = "Internal SquirrelMail error - unknown IMAP authentication method chosen. Please contact the developers."; } /* If the connection was not successful, lets see why */ if ($response != 'OK') { if (!$hide) { if ($response != 'NO') { /* "BAD" and anything else gets reported here. */ $message = htmlspecialchars($message); set_up_language($squirrelmail_language, true); require_once SM_PATH . 'functions/display_messages.php'; if ($response == 'BAD') { $string = sprintf(_("Bad request: %s") . "<br>\r\n", $message); } else { $string = sprintf(_("Unknown error: %s") . "<br>\n", $message); } if (isset($read) && is_array($read)) { $string .= '<br>' . _("Read data:") . "<br>\n"; foreach ($read as $line) { $string .= htmlspecialchars($line) . "<br>\n"; } } error_box($string, $color); exit; } else { /* * If the user does not log in with the correct * username and password it is not possible to get the * correct locale from the user's preferences. * Therefore, apply the same hack as on the login * screen. * * $squirrelmail_language is set by a cookie when * the user selects language and logs out */ set_up_language($squirrelmail_language, true); include_once SM_PATH . 'functions/display_messages.php'; sqsession_destroy(); logout_error(_("Unknown user or password incorrect.")); exit; } } else { exit; } } return $imap_stream; }
function initStream($message, $domain, $length = 0, $host = '', $port = '', $user = '', $pass = '', $authpop = false) { global $use_smtp_tls, $smtp_auth_mech, $username, $key, $onetimepad; if ($authpop) { $this->authPop($host, '', $username, $pass); } $rfc822_header = $message->rfc822_header; $from = $rfc822_header->from[0]; $to = $rfc822_header->to; $cc = $rfc822_header->cc; $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); } if (!$stream) { $this->dlv_msg = $errorString; $this->dlv_ret_nr = $errorNumber; return 0; } $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } /* If $_SERVER['HTTP_HOST'] is set, use that in our HELO to the SMTP server. This should fix the DNS issues some people have had */ if (sqgetGlobalVar('HTTP_HOST', $HTTP_HOST, SQ_SERVER)) { // HTTP_HOST is set $helohost = $HTTP_HOST; } else { // For some reason, HTTP_HOST is not set - revert to old behavior $helohost = $domain; } /* Lets introduce ourselves */ if ($smtp_auth_mech == 'cram-md5' or $smtp_auth_mech == 'digest-md5') { // Doing some form of non-plain auth fputs($stream, "EHLO {$helohost}\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 <challenge string>" $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 {$helohost}\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } elseif ($smtp_auth_mech == 'login') { // The LOGIN method fputs($stream, "EHLO {$helohost}\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($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; } } else { /* Right here, they've reached an unsupported auth mechanism. This is the ugliest hack I've ever done, but it'll do till I can fix things up better tomorrow. So tired... */ if ($this->errorCheck("535 Unable to use this auth type", $stream)) { return 0; } } /* Ok, who is sending the message? */ fputs($stream, 'MAIL FROM: <' . $from->mailbox . '@' . $from->host . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } /* send who the recipients are */ for ($i = 0, $cnt = count($to); $i < $cnt; $i++) { if (!$to[$i]->host) { $to[$i]->host = $domain; } if ($to[$i]->mailbox) { fputs($stream, 'RCPT TO: <' . $to[$i]->mailbox . '@' . $to[$i]->host . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } } for ($i = 0, $cnt = count($cc); $i < $cnt; $i++) { if (!$cc[$i]->host) { $cc[$i]->host = $domain; } if ($cc[$i]->mailbox) { fputs($stream, 'RCPT TO: <' . $cc[$i]->mailbox . '@' . $cc[$i]->host . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } } for ($i = 0, $cnt = count($bcc); $i < $cnt; $i++) { if (!$bcc[$i]->host) { $bcc[$i]->host = $domain; } if ($bcc[$i]->mailbox) { fputs($stream, 'RCPT TO: <' . $bcc[$i]->mailbox . '@' . $bcc[$i]->host . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } } /* Lets start sending the actual message */ fputs($stream, "DATA\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } return $stream; }
function initStream($message, $domain, $length = 0, $host = '', $port = '', $user = '', $pass = '', $authpop = false, $pop_host = '', $stream_options = array()) { global $use_smtp_tls, $smtp_auth_mech; if ($authpop) { $this->authPop($pop_host, '', $user, $pass); } $rfc822_header = $message->rfc822_header; $from = $rfc822_header->from[0]; $to = $rfc822_header->to; $cc = $rfc822_header->cc; $bcc = $rfc822_header->bcc; $content_type = $rfc822_header->content_type; // MAIL FROM: <from address> MUST be empty in cae of MDN (RFC2298) if ($content_type->type0 == 'multipart' && $content_type->type1 == 'report' && isset($content_type->properties['report-type']) && $content_type->properties['report-type'] == 'disposition-notification') { // reinitialize the from object because otherwise the from header somehow // is affected. This $from var is used for smtp command MAIL FROM which // is not the same as what we put in the rfc822 header. $from = new AddressStructure(); $from->host = ''; $from->mailbox = ''; } // NB: Using "ssl://" ensures the highest possible TLS version // will be negotiated with the server (whereas "tls://" only // uses TLS version 1.0) // if ($use_smtp_tls == 1) { if (check_php_version(4, 3) && extension_loaded('openssl')) { if (function_exists('stream_socket_client')) { $server_address = 'ssl://' . $host . ':' . $port; $ssl_context = @stream_context_create($stream_options); $connect_timeout = ini_get('default_socket_timeout'); // null timeout is broken if ($connect_timeout == 0) { $connect_timeout = 30; } $stream = @stream_socket_client($server_address, $errorNumber, $errorString, $connect_timeout, STREAM_CLIENT_CONNECT, $ssl_context); } else { $stream = @fsockopen('ssl://' . $host, $port, $errorNumber, $errorString); } $this->tls_enabled = true; } else { /** * don't connect to server when user asks for smtps and * PHP does not support it. */ $errorNumber = ''; $errorString = _("Secure SMTP (TLS) is enabled in SquirrelMail configuration, but used PHP version does not support it."); } } else { $stream = @fsockopen($host, $port, $errorNumber, $errorString); } if (!$stream) { // reset tls state var to default value, if connection fails $this->tls_enabled = false; // set error messages $this->dlv_msg = $errorString; $this->dlv_ret_nr = $errorNumber; $this->dlv_server_msg = _("Can't open SMTP stream."); return 0; } // get server greeting $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } /* * If $_SERVER['HTTP_HOST'] is set, use that in our HELO to the SMTP * server. This should fix the DNS issues some people have had */ if (sqgetGlobalVar('HTTP_HOST', $HTTP_HOST, SQ_SERVER)) { // HTTP_HOST is set // optionally trim off port number if ($p = strrpos($HTTP_HOST, ':')) { $HTTP_HOST = substr($HTTP_HOST, 0, $p); } $helohost = $HTTP_HOST; } else { // For some reason, HTTP_HOST is not set - revert to old behavior $helohost = $domain; } // if the host is an IPv4 address, enclose it in brackets // if (preg_match('/^\\d+\\.\\d+\\.\\d+\\.\\d+$/', $helohost)) { $helohost = '[' . $helohost . ']'; } $hook_result = do_hook('smtp_helo_override', $helohost); if (!empty($hook_result)) { $helohost = $hook_result; } /* Lets introduce ourselves */ fputs($stream, "EHLO {$helohost}\r\n"); // Read ehlo response $tmp = $this->parse_ehlo_response($stream); if ($this->errorCheck($tmp, $stream)) { // fall back to HELO if EHLO is not supported (error 5xx) if ($this->dlv_ret_nr[0] == '5') { fputs($stream, "HELO {$helohost}\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } else { return 0; } } /** * Implementing SMTP STARTTLS (rfc2487) in php 5.1.0+ * http://www.php.net/stream-socket-enable-crypto */ if ($use_smtp_tls === 2) { if (function_exists('stream_socket_enable_crypto')) { // don't try starting tls, when client thinks that it is already active if ($this->tls_enabled) { $this->dlv_msg = _("TLS session is already activated."); return 0; } elseif (!array_key_exists('STARTTLS', $this->ehlo)) { // check for starttls in ehlo response $this->dlv_msg = _("SMTP STARTTLS is enabled in SquirrelMail configuration, but used SMTP server does not support it"); return 0; } // issue starttls command fputs($stream, "STARTTLS\r\n"); // get response $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } // start crypto on connection. suppress function errors. if (@stream_socket_enable_crypto($stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { // starttls was successful (rfc2487 5.2 Result of the STARTTLS Command) // get new EHLO response fputs($stream, "EHLO {$helohost}\r\n"); // Read ehlo response $tmp = $this->parse_ehlo_response($stream); if ($this->errorCheck($tmp, $stream)) { // don't revert to helo here. server must support ESMTP return 0; } // set information about started tls $this->tls_enabled = true; } else { /** * stream_socket_enable_crypto() call failed. */ $this->dlv_msg = _("Unable to start TLS."); return 0; // Bug: can't get error message. See comments in sqimap_create_stream(). } } else { // php install does not support stream_socket_enable_crypto() function $this->dlv_msg = _("SMTP STARTTLS is enabled in SquirrelMail configuration, but used PHP version does not support functions that allow to enable encryption on open socket."); return 0; } } // FIXME: check ehlo response before using authentication // Try authentication by a plugin // // NOTE: there is another hook in functions/auth.php called "smtp_auth" // that allows a plugin to specify a different set of login credentials // (so is slightly mis-named, but is too old to change), so be careful // that you do not confuse your hook names. // $smtp_auth_args = array('auth_mech' => $smtp_auth_mech, 'user' => $user, 'pass' => $pass, 'host' => $host, 'port' => $port, 'stream' => $stream); if (boolean_hook_function('smtp_authenticate', $smtp_auth_args, 1)) { // authentication succeeded } 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') { 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 <challenge string>" $chall = substr($tmp, 4); // Depending on mechanism, generate response string if ($smtp_auth_mech == 'cram-md5') { $response = cram_md5_response($user, $pass, $chall); } elseif ($smtp_auth_mech == 'digest-md5') { $response = digest_md5_response($user, $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 // We already said hi earlier, nothing more is needed. } elseif ($smtp_auth_mech == 'login') { // The LOGIN method 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; } fputs($stream, base64_encode($pass) . "\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } elseif ($smtp_auth_mech == "plain") { /* SASL Plain */ $auth = base64_encode("{$user}{$user}{$pass}"); $query = "AUTH PLAIN\r\n"; fputs($stream, $query); $read = fgets($stream, 1024); if (substr($read, 0, 3) == '334') { // OK so far.. fputs($stream, "{$auth}\r\n"); $read = fgets($stream, 1024); } $results = explode(" ", $read, 3); $response = $results[1]; $message = $results[2]; } else { /* Right here, they've reached an unsupported auth mechanism. This is the ugliest hack I've ever done, but it'll do till I can fix things up better tomorrow. So tired... */ if ($this->errorCheck("535 Unable to use this auth type", $stream)) { return 0; } } } /* Ok, who is sending the message? */ $fromaddress = strlen($from->mailbox) && $from->host ? $from->mailbox . '@' . $from->host : ''; fputs($stream, 'MAIL FROM:<' . $fromaddress . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } /* send who the recipients are */ for ($i = 0, $cnt = count($to); $i < $cnt; $i++) { if (!$to[$i]->host) { $to[$i]->host = $domain; } if (strlen($to[$i]->mailbox)) { fputs($stream, 'RCPT TO:<' . $to[$i]->mailbox . '@' . $to[$i]->host . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } } for ($i = 0, $cnt = count($cc); $i < $cnt; $i++) { if (!$cc[$i]->host) { $cc[$i]->host = $domain; } if (strlen($cc[$i]->mailbox)) { fputs($stream, 'RCPT TO:<' . $cc[$i]->mailbox . '@' . $cc[$i]->host . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } } for ($i = 0, $cnt = count($bcc); $i < $cnt; $i++) { if (!$bcc[$i]->host) { $bcc[$i]->host = $domain; } if (strlen($bcc[$i]->mailbox)) { fputs($stream, 'RCPT TO:<' . $bcc[$i]->mailbox . '@' . $bcc[$i]->host . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } } /* Lets start sending the actual message */ fputs($stream, "DATA\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } return $stream; }
/** * Logs the user into the imap server. If $hide is set, no error messages * 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, $use_imap_tls, $imap_auth_mech, $sqimap_capabilities; if (!isset($onetimepad) || empty($onetimepad)) { sqgetglobalvar('onetimepad', $onetimepad, SQ_SESSION); } if (!isset($sqimap_capabilities)) { sqgetglobalvar('sqimap_capabilities', $capability, SQ_SESSION); } $host = $imap_server_address; $imap_server_address = sqimap_get_user_server($imap_server_address, $username); $imap_stream = sqimap_create_stream($imap_server_address, $imap_port, $use_imap_tls); /* Decrypt the password */ //$password = OneTimePadDecrypt($password, $onetimepad); 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') { $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.'; } } elseif ($imap_auth_mech == 'login') { // Original IMAP login code $query = 'LOGIN "' . quoteimap($username) . '" "' . quoteimap($password) . '"'; $read = sqimap_run_command($imap_stream, $query, false, $response, $message); } elseif ($imap_auth_mech == 'plain') { /*** * SASL PLAIN * * RFC 2595 Chapter 6 * * The mechanism consists of a single message from the client to the * server. The client sends the authorization identity (identity to * login as), followed by a US-ASCII NUL character, followed by the * authentication identity (identity whose password will be used), * followed by a US-ASCII NUL character, followed by the clear-text * password. The client may leave the authorization identity empty to * indicate that it is the same as the authentication identity. * **/ $tag = sqimap_session_id(false); $sasl = isset($capability['SASL-IR']) && $capability['SASL-IR'] ? true : false; $auth = base64_encode("{$username}{$username}{$password}"); if ($sasl) { // IMAP Extension for SASL Initial Client Response // <draft-siemborski-imap-sasl-initial-response-01b.txt> $query = $tag . " AUTHENTICATE PLAIN {$auth}\r\n"; fputs($imap_stream, $query); $read = sqimap_fgets($imap_stream); } else { $query = $tag . " AUTHENTICATE PLAIN\r\n"; fputs($imap_stream, $query); $read = sqimap_fgets($imap_stream); if (substr($read, 0, 1) == '+') { // OK so far.. fputs($imap_stream, "{$auth}\r\n"); $read = sqimap_fgets($imap_stream); } } $results = explode(" ", $read, 3); $response = $results[1]; $message = $results[2]; } else { $response = "BAD"; $message = "Internal SquirrelMail error - unknown IMAP authentication method chosen. Please contact the developers."; } /* If the connection was not successful, lets see why */ if ($response != 'OK') { if (!$hide) { if ($response != 'NO') { /* "BAD" and anything else gets reported here. */ $message = htmlspecialchars($message); set_up_language($squirrelmail_language, true); require_once SM_PATH . 'functions/display_messages.php'; if ($response == 'BAD') { $string = sprintf(_("Bad request: %s") . "<br />\r\n", $message); } else { $string = sprintf(_("Unknown error: %s") . "<br />\n", $message); } if (isset($read) && is_array($read)) { $string .= '<br />' . _("Read data:") . "<br />\n"; foreach ($read as $line) { $string .= htmlspecialchars($line) . "<br />\n"; } } error_box($string, $color); exit; } else { /* * If the user does not log in with the correct * username and password it is not possible to get the * correct locale from the user's preferences. * Therefore, apply the same hack as on the login * screen. * * $squirrelmail_language is set by a cookie when * the user selects language and logs out */ set_up_language($squirrelmail_language, true); include_once SM_PATH . 'functions/display_messages.php'; sqsession_destroy(); /* terminate the session nicely */ sqimap_logout($imap_stream); logout_error(_("Unknown user or password incorrect.")); exit; } } else { exit; } } return $imap_stream; }
function initStream($message, $domain, $length = 0, $host = '', $port = '', $user = '', $pass = '', $authpop = false) { global $use_smtp_tls, $smtp_auth_mech; if ($authpop) { $this->authPop($host, '', $user, $pass); } $rfc822_header = $message->rfc822_header; $from = $rfc822_header->from[0]; $to = $rfc822_header->to; $cc = $rfc822_header->cc; $bcc = $rfc822_header->bcc; $content_type = $rfc822_header->content_type; // MAIL FROM: <from address> MUST be empty in cae of MDN (RFC2298) if ($content_type->type0 == 'multipart' && $content_type->type1 == 'report' && isset($content_type->properties['report-type']) && $content_type->properties['report-type'] == 'disposition-notification') { $from->host = ''; $from->mailbox = ''; } 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); } if (!$stream) { $this->dlv_msg = $errorString; $this->dlv_ret_nr = $errorNumber; return 0; } $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } /* * If $_SERVER['HTTP_HOST'] is set, use that in our HELO to the SMTP * server. This should fix the DNS issues some people have had */ if (sqgetGlobalVar('HTTP_HOST', $HTTP_HOST, SQ_SERVER)) { // HTTP_HOST is set // optionally trim off port number if ($p = strrpos($HTTP_HOST, ':')) { $HTTP_HOST = substr($HTTP_HOST, 0, $p); } $helohost = $HTTP_HOST; } else { // For some reason, HTTP_HOST is not set - revert to old behavior $helohost = $domain; } /* Lets introduce ourselves */ fputs($stream, "EHLO {$helohost}\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { // fall back to HELO if EHLO is not supported if ($this->dlv_ret_nr == '500') { fputs($stream, "HELO {$helohost}\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } else { return 0; } } 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') { 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 <challenge string>" $chall = substr($tmp, 4); // Depending on mechanism, generate response string if ($smtp_auth_mech == 'cram-md5') { $response = cram_md5_response($user, $pass, $chall); } elseif ($smtp_auth_mech == 'digest-md5') { $response = digest_md5_response($user, $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 // We already said hi earlier, nothing more is needed. } elseif ($smtp_auth_mech == 'login') { // The LOGIN method 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; } fputs($stream, base64_encode($pass) . "\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } elseif ($smtp_auth_mech == "plain") { /* SASL Plain */ $auth = base64_encode("{$user}{$user}{$pass}"); $query = "AUTH PLAIN\r\n"; fputs($stream, $query); $read = fgets($stream, 1024); if (substr($read, 0, 3) == '334') { // OK so far.. fputs($stream, "{$auth}\r\n"); $read = fgets($stream, 1024); } $results = explode(" ", $read, 3); $response = $results[1]; $message = $results[2]; } else { /* Right here, they've reached an unsupported auth mechanism. This is the ugliest hack I've ever done, but it'll do till I can fix things up better tomorrow. So tired... */ if ($this->errorCheck("535 Unable to use this auth type", $stream)) { return 0; } } /* Ok, who is sending the message? */ $fromaddress = $from->mailbox && $from->host ? $from->mailbox . '@' . $from->host : ''; fputs($stream, 'MAIL FROM:<' . $fromaddress . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } /* send who the recipients are */ for ($i = 0, $cnt = count($to); $i < $cnt; $i++) { if (!$to[$i]->host) { $to[$i]->host = $domain; } if ($to[$i]->mailbox) { fputs($stream, 'RCPT TO:<' . $to[$i]->mailbox . '@' . $to[$i]->host . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } } for ($i = 0, $cnt = count($cc); $i < $cnt; $i++) { if (!$cc[$i]->host) { $cc[$i]->host = $domain; } if ($cc[$i]->mailbox) { fputs($stream, 'RCPT TO:<' . $cc[$i]->mailbox . '@' . $cc[$i]->host . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } } for ($i = 0, $cnt = count($bcc); $i < $cnt; $i++) { if (!$bcc[$i]->host) { $bcc[$i]->host = $domain; } if ($bcc[$i]->mailbox) { fputs($stream, 'RCPT TO:<' . $bcc[$i]->mailbox . '@' . $bcc[$i]->host . ">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } } } /* Lets start sending the actual message */ fputs($stream, "DATA\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return 0; } return $stream; }
/** * Logs the user into the IMAP server. If $hide is set, no error messages * will be displayed (if set to 1, just exits, if set to 2, returns FALSE). * This function returns the IMAP connection handle. * @param string $username user name * @param string $password password encrypted with onetimepad. Since 1.5.2 * function can use internal password functions, if parameter is set to * boolean false. * @param string $imap_server_address address of imap server * @param integer $imap_port port of imap server * @param int $hide controls display connection errors: * 0 = do not hide * 1 = show no errors (just exit) * 2 = show no errors (return FALSE) * 3 = show no errors (return error string) * @param array $stream_options Stream context options, see config_local.php * for more details (OPTIONAL) * @return mixed The IMAP connection stream, or if the connection fails, * FALSE if $hide is set to 2 or an error string if $hide * is set to 3. */ function sqimap_login($username, $password, $imap_server_address, $imap_port, $hide, $stream_options = array()) { global $color, $squirrelmail_language, $onetimepad, $use_imap_tls, $imap_auth_mech, $sqimap_capabilities, $display_imap_login_error; // Note/TODO: This hack grabs the $authz argument from the session. In the short future, // a new argument in function sqimap_login() will be used instead. $authz = ''; global $authz; sqgetglobalvar('authz', $authz, SQ_SESSION); if (!empty($authz)) { /* authz plugin - specific: * Get proxy login parameters from authz plugin configuration. If they * exist, they will override the current ones. * This is useful if we want to use different SASL authentication mechanism * and/or different TLS settings for proxy logins. */ global $authz_imap_auth_mech, $authz_use_imap_tls, $authz_imapPort_tls; $imap_auth_mech = !empty($authz_imap_auth_mech) ? strtolower($authz_imap_auth_mech) : $imap_auth_mech; $use_imap_tls = !empty($authz_use_imap_tls) ? $authz_use_imap_tls : $use_imap_tls; $imap_port = !empty($authz_use_imap_tls) ? $authz_imapPort_tls : $imap_port; if ($imap_auth_mech == 'login' || $imap_auth_mech == 'cram-md5') { logout_error("Misconfigured Plugin (authz or equivalent):<br/>" . "The LOGIN and CRAM-MD5 authentication mechanisms cannot be used when attempting proxy login."); exit; } } /* get imap login password */ if ($password === false) { /* standard functions */ $password = sqauth_read_password(); } else { /* old way. $key must be extracted from cookie */ if (!isset($onetimepad) || empty($onetimepad)) { sqgetglobalvar('onetimepad', $onetimepad, SQ_SESSION); } /* Decrypt the password */ $password = OneTimePadDecrypt($password, $onetimepad); } if (!isset($sqimap_capabilities)) { sqgetglobalvar('sqimap_capabilities', $sqimap_capabilities, SQ_SESSION); } $host = $imap_server_address; $imap_server_address = sqimap_get_user_server($imap_server_address, $username); $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') { // We're using some sort of authentication OTHER than plain or login $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, $authz); } 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.'; } } elseif ($imap_auth_mech == 'login') { // Original IMAP login code $query = 'LOGIN "' . quoteimap($username) . '" "' . quoteimap($password) . '"'; $read = sqimap_run_command($imap_stream, $query, false, $response, $message); } elseif ($imap_auth_mech == 'plain') { /*** * SASL PLAIN, RFC 4616 (updates 2595) * * The mechanism consists of a single message, a string of [UTF-8] * encoded [Unicode] characters, from the client to the server. The * client presents the authorization identity (identity to act as), * followed by a NUL (U+0000) character, followed by the authentication * identity (identity whose password will be used), followed by a NUL * (U+0000) character, followed by the clear-text password. As with * other SASL mechanisms, the client does not provide an authorization * identity when it wishes the server to derive an identity from the * credentials and use that as the authorization identity. */ $tag = sqimap_session_id(false); $sasl = isset($sqimap_capabilities['SASL-IR']) && $sqimap_capabilities['SASL-IR'] ? true : false; if (!empty($authz)) { $auth = base64_encode("{$username}{$authz}{$password}"); } else { $auth = base64_encode("{$username}{$username}{$password}"); } if ($sasl) { // IMAP Extension for SASL Initial Client Response // <draft-siemborski-imap-sasl-initial-response-01b.txt> $query = $tag . " AUTHENTICATE PLAIN {$auth}\r\n"; fputs($imap_stream, $query); $read = sqimap_fgets($imap_stream); } else { $query = $tag . " AUTHENTICATE PLAIN\r\n"; fputs($imap_stream, $query); $read = sqimap_fgets($imap_stream); if (substr($read, 0, 1) == '+') { // OK so far.. fputs($imap_stream, "{$auth}\r\n"); $read = sqimap_fgets($imap_stream); } } $results = explode(" ", $read, 3); $response = $results[1]; $message = $results[2]; } else { $response = "BAD"; $message = "Internal SquirrelMail error - unknown IMAP authentication method chosen. Please contact the developers."; } /* If the connection was not successful, lets see why */ if ($response != 'OK') { if (!$hide || $hide == 3) { //FIXME: UUURG... We don't want HTML in error messages, should also do html sanitizing of error messages elsewhere; should't assume output is destined for an HTML browser here if ($response != 'NO') { /* "BAD" and anything else gets reported here. */ $message = sm_encode_html_special_chars($message); set_up_language($squirrelmail_language, true); if ($response == 'BAD') { if ($hide == 3) { return sprintf(_("Bad request: %s"), $message); } $string = sprintf(_("Bad request: %s") . "<br />\r\n", $message); } else { if ($hide == 3) { return sprintf(_("Unknown error: %s"), $message); } $string = sprintf(_("Unknown error: %s") . "<br />\n", $message); } if (isset($read) && is_array($read)) { $string .= '<br />' . _("Read data:") . "<br />\n"; foreach ($read as $line) { $string .= sm_encode_html_special_chars($line) . "<br />\n"; } } error_box($string); exit; } else { /* * If the user does not log in with the correct * username and password it is not possible to get the * correct locale from the user's preferences. * Therefore, apply the same hack as on the login * screen. * * $squirrelmail_language is set by a cookie when * the user selects language and logs out */ set_up_language($squirrelmail_language, true); sqsession_destroy(); /* terminate the session nicely */ sqimap_logout($imap_stream); // determine what error message to use // $fail_msg = _("Unknown user or password incorrect."); if ($display_imap_login_error) { // See if there is an error message from the server // Skip any rfc5530 response code: '[something]' at the // start of the message if (!empty($message) && $message[0] == '[' && ($end = strstr($message, ']')) && $end != ']') { $message = substr($end, 1); } // Remove surrounding spaces and if there // is anything left, display that as the // error message: $message = trim($message); if (strlen($message)) { $fail_msg = _($message); } } if ($hide == 3) { return $fail_msg; } logout_error($fail_msg); exit; } } else { if ($hide == 2) { return FALSE; } exit; } } /* Special error case: * Login referrals. The server returns: * ? OK [REFERRAL <imap url>] * Check RFC 2221 for details. Since we do not support login referrals yet * we log the user out. */ if (stristr($message, 'REFERRAL imap') === TRUE) { sqimap_logout($imap_stream); set_up_language($squirrelmail_language, true); sqsession_destroy(); logout_error(_("Your mailbox is not located at this server. Try a different server or consult your system administrator")); exit; } return $imap_stream; }