function _authCRAM_MD5($uid, $pwd, $cmdid) { if (PEAR::isError($error = $this->_putCMD($cmdid, "AUTHENTICATE", "CRAM-MD5"))) { return $error; } if (PEAR::isError($args = $this->_recvLn())) { return $args; } $plus = ""; $this->_getNextToken($args, $plus); $space = ""; $this->_getNextToken($args, $space); $challenge = ""; $this->_getNextToken($args, $challenge); $challenge = base64_decode($challenge); $cram =& Auth_SASL::factory('crammd5'); $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge)); if (PEAR::isError($error = $this->_send($auth_str . "\r\n"))) { return $error; } return true; }
/** * Authenticates the user using the CRAM-MD5 method. * * @param string The userid to authenticate as. * @param string The password to authenticate with. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access private * @since 1.1.0 */ function _authCRAM_MD5($uid, $pwd) { if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) { return $error; } /* 334: Continue authentication request */ if (PEAR::isError($error = $this->_parseResponse(334))) { /* 503: Error: already authenticated */ if ($this->_code === 503) { return true; } return $error; } $challenge = base64_decode($this->_arguments[0]); $cram =& Auth_SASL::factory('crammd5'); $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge)); if (PEAR::isError($error = $this->_put($auth_str))) { return $error; } /* 235: Authentication successful */ if (PEAR::isError($error = $this->_parseResponse(235))) { return $error; } }
/** * DIGEST-MD5/CRAM-MD5/PLAIN Authentication * * @param string $user * @param string $pass * @param string $type Authentication type (PLAIN/CRAM-MD5/DIGEST-MD5) * * @return resource Connection resourse on success, error code on error */ function authenticate($user, $pass, $type = 'PLAIN') { if ($type == 'CRAM-MD5' || $type == 'DIGEST-MD5') { if ($type == 'DIGEST-MD5' && !class_exists('Auth_SASL')) { $this->setError(self::ERROR_BYE, "The Auth_SASL package is required for DIGEST-MD5 authentication"); return self::ERROR_BAD; } $this->putLine($this->nextTag() . " AUTHENTICATE {$type}"); $line = trim($this->readReply()); if ($line[0] == '+') { $challenge = substr($line, 2); } else { return $this->parseResult($line); } if ($type == 'CRAM-MD5') { // RFC2195: CRAM-MD5 $ipad = ''; $opad = ''; // initialize ipad, opad for ($i = 0; $i < 64; $i++) { $ipad .= chr(0x36); $opad .= chr(0x5c); } // pad $pass so it's 64 bytes $padLen = 64 - strlen($pass); for ($i = 0; $i < $padLen; $i++) { $pass .= chr(0); } // generate hash $hash = md5($this->_xor($pass, $opad) . pack("H*", md5($this->_xor($pass, $ipad) . base64_decode($challenge)))); $reply = base64_encode($user . ' ' . $hash); // send result $this->putLine($reply, true, true); } else { // RFC2831: DIGEST-MD5 // proxy authorization if (!empty($this->prefs['auth_cid'])) { $authc = $this->prefs['auth_cid']; $pass = $this->prefs['auth_pw']; } else { $authc = $user; $user = ''; } $auth_sasl = Auth_SASL::factory('digestmd5'); $reply = base64_encode($auth_sasl->getResponse($authc, $pass, base64_decode($challenge), $this->host, 'imap', $user)); // send result $this->putLine($reply, true, true); $line = trim($this->readReply()); if ($line[0] == '+') { $challenge = substr($line, 2); } else { return $this->parseResult($line); } // check response $challenge = base64_decode($challenge); if (strpos($challenge, 'rspauth=') === false) { $this->setError(self::ERROR_BAD, "Unexpected response from server to DIGEST-MD5 response"); return self::ERROR_BAD; } $this->putLine(''); } $line = $this->readReply(); $result = $this->parseResult($line); } else { // PLAIN // proxy authorization if (!empty($this->prefs['auth_cid'])) { $authc = $this->prefs['auth_cid']; $pass = $this->prefs['auth_pw']; } else { $authc = $user; $user = ''; } $reply = base64_encode($user . chr(0) . $authc . chr(0) . $pass); // RFC 4959 (SASL-IR): save one round trip if ($this->getCapability('SASL-IR')) { list($result, $line) = $this->execute("AUTHENTICATE PLAIN", array($reply), self::COMMAND_LASTLINE | self::COMMAND_CAPABILITY | self::COMMAND_ANONYMIZED); } else { $this->putLine($this->nextTag() . " AUTHENTICATE PLAIN"); $line = trim($this->readReply()); if ($line[0] != '+') { return $this->parseResult($line); } // send result, get reply and process it $this->putLine($reply, true, true); $line = $this->readReply(); $result = $this->parseResult($line); } } if ($result == self::ERROR_OK) { // optional CAPABILITY response if ($line && preg_match('/\\[CAPABILITY ([^]]+)\\]/i', $line, $matches)) { $this->parseCapability($matches[1], true); } return $this->fp; } else { $this->setError($result, "AUTHENTICATE {$type}: {$line}"); } return $result; }
/** * Authenticates the user using the DIGEST-MD5 method. * * @param string $user The userid to authenticate as. * @param string $pass The password to authenticate with. * @param string $euser The effective uid to authenticate as. * * @return void */ function _authDigestMD5($user, $pass, $euser) { if (PEAR::isError($challenge = $this->_doCmd('AUTHENTICATE "DIGEST-MD5"', true))) { return $challenge; } $challenge = base64_decode(trim($challenge)); $digest = Auth_SASL::factory('digestmd5'); // @todo Really 'localhost'? if (PEAR::isError($response = $digest->getResponse($user, $pass, $challenge, 'localhost', 'sieve', $euser))) { return $response; } if (PEAR::isError($result = $this->_sendStringResponse(base64_encode($response)))) { return $result; } if (PEAR::isError($result = $this->_doCmd('', true))) { return $result; } if ($this->_toUpper(substr($result, 0, 2)) == 'OK') { return; } /* We don't use the protocol's third step because SIEVE doesn't allow * subsequent authentication, so we just silently ignore it. */ if (PEAR::isError($result = $this->_sendStringResponse(''))) { return $result; } return $this->_doCmd(); }
/** * DIGEST-MD5/CRAM-MD5/PLAIN Authentication * * @param string $user Username * @param string $pass Password * @param string $type Authentication type (PLAIN/CRAM-MD5/DIGEST-MD5) * * @return resource Connection resourse on success, error code on error */ protected function authenticate($user, $pass, $type = 'PLAIN') { if ($type == 'CRAM-MD5' || $type == 'DIGEST-MD5') { if ($type == 'DIGEST-MD5' && !class_exists('Auth_SASL')) { $this->setError(self::ERROR_BYE, "The Auth_SASL package is required for DIGEST-MD5 authentication"); return self::ERROR_BAD; } $this->putLine($this->nextTag() . " AUTHENTICATE {$type}"); $line = trim($this->readReply()); if ($line[0] == '+') { $challenge = substr($line, 2); } else { return $this->parseResult($line); } if ($type == 'CRAM-MD5') { // RFC2195: CRAM-MD5 $ipad = ''; $opad = ''; // initialize ipad, opad for ($i = 0; $i < 64; $i++) { $ipad .= chr(0x36); $opad .= chr(0x5c); } // pad $pass so it's 64 bytes $padLen = 64 - strlen($pass); for ($i = 0; $i < $padLen; $i++) { $pass .= chr(0); } // generate hash $hash = md5($this->_xor($pass, $opad) . pack("H*", md5($this->_xor($pass, $ipad) . base64_decode($challenge)))); $reply = base64_encode($user . ' ' . $hash); // send result $this->putLine($reply, true, true); } else { // RFC2831: DIGEST-MD5 // proxy authorization if (!empty($this->prefs['auth_cid'])) { $authc = $this->prefs['auth_cid']; $pass = $this->prefs['auth_pw']; } else { $authc = $user; $user = ''; } $auth_sasl = Auth_SASL::factory('digestmd5'); $reply = base64_encode($auth_sasl->getResponse($authc, $pass, base64_decode($challenge), $this->host, 'imap', $user)); // send result $this->putLine($reply, true, true); $line = trim($this->readReply()); if ($line[0] != '+') { return $this->parseResult($line); } // check response $challenge = substr($line, 2); $challenge = base64_decode($challenge); if (strpos($challenge, 'rspauth=') === false) { $this->setError(self::ERROR_BAD, "Unexpected response from server to DIGEST-MD5 response"); return self::ERROR_BAD; } $this->putLine(''); } $line = $this->readReply(); $result = $this->parseResult($line); } else { if ($type == 'GSSAPI') { if (!extension_loaded('krb5')) { $this->setError(self::ERROR_BYE, "The krb5 extension is required for GSSAPI authentication"); return self::ERROR_BAD; } if (empty($this->prefs['gssapi_cn'])) { $this->setError(self::ERROR_BYE, "The gssapi_cn parameter is required for GSSAPI authentication"); return self::ERROR_BAD; } if (empty($this->prefs['gssapi_context'])) { $this->setError(self::ERROR_BYE, "The gssapi_context parameter is required for GSSAPI authentication"); return self::ERROR_BAD; } putenv('KRB5CCNAME=' . $this->prefs['gssapi_cn']); try { $ccache = new KRB5CCache(); $ccache->open($this->prefs['gssapi_cn']); $gssapicontext = new GSSAPIContext(); $gssapicontext->acquireCredentials($ccache); $token = ''; $success = $gssapicontext->initSecContext($this->prefs['gssapi_context'], null, null, null, $token); $token = base64_encode($token); } catch (Exception $e) { trigger_error($e->getMessage(), E_USER_WARNING); $this->setError(self::ERROR_BYE, "GSSAPI authentication failed"); return self::ERROR_BAD; } $this->putLine($this->nextTag() . " AUTHENTICATE GSSAPI " . $token); $line = trim($this->readReply()); if ($line[0] != '+') { return $this->parseResult($line); } try { $challenge = base64_decode(substr($line, 2)); $gssapicontext->unwrap($challenge, $challenge); $gssapicontext->wrap($challenge, $challenge, true); } catch (Exception $e) { trigger_error($e->getMessage(), E_USER_WARNING); $this->setError(self::ERROR_BYE, "GSSAPI authentication failed"); return self::ERROR_BAD; } $this->putLine(base64_encode($challenge)); $line = $this->readReply(); $result = $this->parseResult($line); } else { // PLAIN // proxy authorization if (!empty($this->prefs['auth_cid'])) { $authc = $this->prefs['auth_cid']; $pass = $this->prefs['auth_pw']; } else { $authc = $user; $user = ''; } $reply = base64_encode($user . chr(0) . $authc . chr(0) . $pass); // RFC 4959 (SASL-IR): save one round trip if ($this->getCapability('SASL-IR')) { list($result, $line) = $this->execute("AUTHENTICATE PLAIN", array($reply), self::COMMAND_LASTLINE | self::COMMAND_CAPABILITY | self::COMMAND_ANONYMIZED); } else { $this->putLine($this->nextTag() . " AUTHENTICATE PLAIN"); $line = trim($this->readReply()); if ($line[0] != '+') { return $this->parseResult($line); } // send result, get reply and process it $this->putLine($reply, true, true); $line = $this->readReply(); $result = $this->parseResult($line); } } } if ($result == self::ERROR_OK) { // optional CAPABILITY response if ($line && preg_match('/\\[CAPABILITY ([^]]+)\\]/i', $line, $matches)) { $this->parseCapability($matches[1], true); } return $this->fp; } else { $this->setError($result, "AUTHENTICATE {$type}: {$line}"); } return $result; }
function _authDigest_MD5($uid, $pwd) { if (PEAR::isError($ret = $this->_send('AUTH DIGEST-MD5'))) { return $ret; } if (PEAR::isError($challenge = $this->_recvLn())) { return $challenge; } if (PEAR::isError($ret = $this->_checkResponse($challenge))) { return $ret; } // remove '+ ' $challenge = substr($challenge, 2); $challenge = base64_decode($challenge); $digest =& Auth_SASL::factory('digestmd5'); $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, "localhost", "pop3")); if (PEAR::isError($error = $this->_send($auth_str))) { return $error; } if (PEAR::isError($challenge = $this->_recvLn())) { return $challenge; } if (PEAR::isError($ret = $this->_checkResponse($challenge))) { return $ret; } /* * We don't use the protocol's third step because POP3 doesn't allow * subsequent authentication, so we just silently ignore it. */ if (PEAR::isError($challenge = $this->_send("\r\n"))) { return $challenge; } if (PEAR::isError($challenge = $this->_recvLn())) { return $challenge; } return $this->_checkResponse($challenge); }
/** * Try to authenticate * * @throws XmppPrebindException if invalid login * @return bool */ public function auth() { $auth = Auth_SASL::factory($this->encryption); switch ($this->encryption) { case self::ENCRYPTION_PLAIN: $authXml = $this->buildPlainAuth($auth); break; case self::ENCRYPTION_DIGEST_MD5: $authXml = $this->sendChallengeAndBuildDigestMd5Auth($auth); break; case self::ENCRYPTION_CRAM_MD5: $authXml = $this->sendChallengeAndBuildCramMd5Auth($auth); break; } /*$response = $this->send($authXml); $body = self::getBodyFromXml($response); error_log("Body of the response: " . $body); if (!$body->hasChildNodes() || $body->firstChild->nodeName !== 'success') { throw new XmppPrebindException("Invalid login"); } */ $this->rid--; $this->sendRestart(); $this->sendBindIfRequired(); $this->sendSessionIfRequired(); return true; }
/** * Authenticates the user using the DIGEST-MD5 method. * * @param string The userid to authenticate as. * @param string The password to authenticate with. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access private * @since 1.1.0 */ function _authDigest_MD5($uid, $pwd) { if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) { return $error; } /* 334: Continue authentication request */ if (PEAR::isError($error = $this->_parseResponse(334))) { /* 503: Error: already authenticated */ if ($this->_code === 503) { return true; } return $error; } $challenge = base64_decode($this->_arguments[0]); $digest =& Auth_SASL::factory('digestmd5'); $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, $this->host, "smtp")); if (PEAR::isError($error = $this->_put($auth_str))) { return $error; } /* 334: Continue authentication request */ if (PEAR::isError($error = $this->_parseResponse(334))) { return $error; } /* We don't use the protocol's third step because SMTP doesn't * allow subsequent authentication, so we just silently ignore * it. */ if (PEAR::isError($error = $this->_put(' '))) { return $error; } /* 235: Authentication successful */ if (PEAR::isError($error = $this->_parseResponse(235))) { return $error; } }
/** * Authenticates the user using the DIGEST-MD5 method. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * @param string $euser The effective uid to authenticate as. * * @return array Returns an array containing the response * * @access private * @since 1.0 */ function _authDigest_MD5($uid, $pwd, $euser) { if (PEAR::isError($challenge = $this->_doCmd('AUTHENTICATE "DIGEST-MD5"'))) { $this->_error = challenge; return challenge; } $challenge = base64_decode($challenge); $digest =& Auth_SASL::factory('digestmd5'); if (PEAR::isError($param = $digest->getResponse($uid, $pwd, $challenge, "localhost", "sieve", $euser))) { return $param; } $auth_str = base64_encode($param); if (PEAR::isError($error = $this->_sendStringResponse($auth_str))) { $this->_error = $error; return $error; } if (PEAR::isError($challenge = $this->_doCmd())) { $this->_error = $challenge; return $challenge; } if (strtoupper(substr($challenge, 0, 2)) == 'OK') { return true; } /** * We don't use the protocol's third step because SIEVE doesn't allow * subsequent authentication, so we just silently ignore it. */ if (PEAR::isError($error = $this->_sendStringResponse(''))) { $this->_error = $error; return $error; } if (PEAR::isError($res = $this->_doCmd())) { return $res; } }
/** * Authenticates the user using the CRAM-MD5 method. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * @param string $cmdid The cmdID. * * @return array Returns an array containing the response * @access private * @since 1.0 */ function _authCramMD5($uid, $pwd, $cmdid) { $error = $this->_putCMD($cmdid, 'AUTHENTICATE', 'CRAM-MD5'); if ($error instanceof PEAR_Error) { return $error; } $args = $this->_recvLn(); if ($args instanceof PEAR_Error) { return $args; } $this->_getNextToken($args, $plus); $this->_getNextToken($args, $space); $this->_getNextToken($args, $challenge); $challenge = base64_decode($challenge); $cram =& Auth_SASL::factory('crammd5'); $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge)); $error = $this->_send($auth_str . "\r\n"); if ($error instanceof PEAR_Error) { return $error; } }
/** * Factory class. Returns an object of the request * type. * * @param string $type One of: Anonymous * Plain * CramMD5 * DigestMD5 * SCRAM-* (any mechanism of the SCRAM family) * Types are not case sensitive */ function &factory($type) { switch (strtolower($type)) { case 'anonymous': $filename = 'include/Auth/SASL/Anonymous.php'; $classname = 'Auth_SASL_Anonymous'; break; case 'login': $filename = 'include/Auth/SASL/Login.php'; $classname = 'Auth_SASL_Login'; break; case 'plain': $filename = 'include/Auth/SASL/Plain.php'; $classname = 'Auth_SASL_Plain'; break; case 'external': $filename = 'include/Auth/SASL/External.php'; $classname = 'Auth_SASL_External'; break; case 'crammd5': // $msg = 'Deprecated mechanism name. Use IANA-registered name: CRAM-MD5.'; // trigger_error($msg, E_USER_DEPRECATED); // $msg = 'Deprecated mechanism name. Use IANA-registered name: CRAM-MD5.'; // trigger_error($msg, E_USER_DEPRECATED); case 'cram-md5': $filename = 'include/Auth/SASL/CramMD5.php'; $classname = 'Auth_SASL_CramMD5'; break; case 'digestmd5': // $msg = 'Deprecated mechanism name. Use IANA-registered name: DIGEST-MD5.'; // trigger_error($msg, E_USER_DEPRECATED); // $msg = 'Deprecated mechanism name. Use IANA-registered name: DIGEST-MD5.'; // trigger_error($msg, E_USER_DEPRECATED); case 'digest-md5': // $msg = 'DIGEST-MD5 is a deprecated SASL mechanism as per RFC-6331. Using it could be a security risk.'; // trigger_error($msg, E_USER_NOTICE); $filename = 'include/Auth/SASL/DigestMD5.php'; $classname = 'Auth_SASL_DigestMD5'; break; default: $scram = '/^SCRAM-(.{1,9})$/i'; if (preg_match($scram, $type, $matches)) { $hash = $matches[1]; $filename = 'include/Auth/SASL/SCRAM.php'; $classname = 'Auth_SASL_SCRAM'; $parameter = $hash; break; } return Auth_SASL::raiseError('Invalid SASL mechanism type'); break; } require_once $filename; if (isset($parameter)) { $obj = new $classname($parameter); } else { $obj = new $classname(); } return $obj; }
function _authCRAM_MD5($uid, $pwd) { if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) { return $error; } if (PEAR::isError($error = $this->_parseResponse(334))) { return $error; } $challenge = base64_decode($this->_arguments[0]); $cram =& Auth_SASL::factory('crammd5'); $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge)); if (PEAR::isError($error = $this->_put($auth_str))) { return $error; } if (PEAR::isError($error = $this->_parseResponse(235))) { return $error; } }
/** * Connect to the XMPP server with the supplied node & password * * @param string $node Node without domain * @param string $password Password */ public function connect($node = null, $password = null) { if ($node != null && $password != null) { $this->node = $node; $this->password = $password; $this->jid = $this->node . '@' . $this->domain; if ($this->resource != '') { $this->jid .= '/' . $this->resource; } } // Init connection & get server supported mechanisms $response = $this->sendInitConnection(); $body = self::getBodyFromXml($response); $this->sid = $body->getAttribute('sid'); $mechanisms = $body->firstChild->getElementsByTagName('mechanism'); foreach ($mechanisms as $value) { $this->mechanisms[] = $value->nodeValue; } /* If node & password not provided and server supports ANONYMOUS * we select that mechanism. To select other mechanisms we must have node * & password */ if (in_array(self::ENCRYPTION_ANONYMOUS, $this->mechanisms) && $node == null && $password == null) { $this->encryption = self::ENCRYPTION_ANONYMOUS; } else { if (in_array(self::ENCRYPTION_DIGEST_MD5, $this->mechanisms) && $node != null && $password != null) { $this->encryption = self::ENCRYPTION_DIGEST_MD5; } elseif (in_array(self::ENCRYPTION_CRAM_MD5, $this->mechanisms) && $node != null && $password != null) { $this->encryption = self::ENCRYPTION_CRAM_MD5; } elseif (in_array(self::ENCRYPTION_PLAIN, $this->mechanisms) && $node != null && $password != null) { $this->encryption = self::ENCRYPTION_PLAIN; } else { throw new XmppBoshConnectionException("No encryption supported by the server is supported by this library."); } } // Authentication process using selected mechanism $auth = Auth_SASL::factory($this->encryption); switch ($this->encryption) { case self::ENCRYPTION_PLAIN: $response = $this->buildPlainAuth($auth); break; case self::ENCRYPTION_DIGEST_MD5: $response = $this->sendChallengeAndBuildDigestMd5Auth($auth); break; case self::ENCRYPTION_CRAM_MD5: $response = $this->sendChallengeAndBuildCramMd5Auth($auth); break; case self::ENCRYPTION_ANONYMOUS: $response = $this->buildAnonymousAuth($auth); break; } $response = $this->send($response); $body = self::getBodyFromXml($response); // Authentication success or failure if (!$body->hasChildNodes() || $body->firstChild->nodeName !== 'success') { throw new XmppBoshException("Invalid login"); } // For ANONYMOUS mechanism we send new init, for others restart if ($this->encryption != self::ENCRYPTION_ANONYMOUS) { $response = $this->sendRestart(); } else { $response = $this->sendInitConnection(); } // Detect are bind and session required $body = self::getBodyFromXml($response); foreach ($body->childNodes as $bodyChildNodes) { if ($bodyChildNodes->nodeName === 'stream:features') { foreach ($bodyChildNodes->childNodes as $streamFeatures) { if ($streamFeatures->nodeName === 'bind') { $this->doBind = true; } elseif ($streamFeatures->nodeName === 'session') { $this->doSession = true; } } } } // If bind required we send it if ($this->doBind) { $response = $this->sendBind(); // For ANONYMOUS mechanism bind returns full JID, so lets store it if ($this->encryption == self::ENCRYPTION_ANONYMOUS) { $body = self::getBodyFromXml($response); if ($body->hasChildNodes() && $body->firstChild->getAttribute('type') == 'result') { $this->jid = $body->firstChild->firstChild->firstChild->nodeValue; } else { throw new XmppException('Subscription request not successful'); } } } // If session required we send it if ($this->doSession) { $response = $this->sendSession(); } }
/** * Authenticates the user using the DIGEST-MD5 method. * * @param string $user The userid to authenticate as. * @param string $pass The password to authenticate with. * @param string $euser The effective uid to authenticate as. * * @throws \Horde\ManageSieve\Exception */ protected function _authDigestMD5($user, $pass, $euser) { $challenge = $this->_doCmd('AUTHENTICATE "DIGEST-MD5"', true); $challenge = base64_decode(trim($challenge)); $digest = Auth_SASL::factory('digestmd5'); // @todo Really 'localhost'? $response = $digest->getResponse($user, $pass, $challenge, 'localhost', 'sieve', $euser); if (is_a($response, 'PEAR_Error')) { throw new Exception($response); } $this->_sendStringResponse(base64_encode($response)); $this->_doCmd('', true); if (\Horde_String::upper(substr($result, 0, 2)) == 'OK') { return; } /* We don't use the protocol's third step because SIEVE doesn't allow * subsequent authentication, so we just silently ignore it. */ $this->_sendStringResponse(''); $this->_doCmd(); }
/** * Log the user into the IMAP server. * * @access private * * @param string $username Username. * @param string $password Encrypted password. * @param string $method IMAP login method. * * @return mixed True on success, PEAR_Error on error. */ function _login($username, $password, $method) { switch ($method) { case 'cram-md5': case 'digest-md5': /* If we don't have Auth_SASL package install, return error. */ if (!@(include_once 'Auth/SASL.php')) { return PEAR::raiseError(lang("CRAM-MD5 or DIGEST-MD5 requires the Auth_SASL package to be installed."), 'horde.error'); } $tag = $this->_generateSid(); fwrite($this->_stream, $tag . ' AUTHENTICATE ' . strtoupper($method) . "\r\n"); $challenge = explode(' ', $this->_fgets(), 3); if ($method == 'cram-md5') { $auth_sasl = Auth_SASL::factory('crammd5'); $response = $auth_sasl->getResponse($username, $password, base64_decode($challenge[1])); fwrite($this->_stream, base64_encode($response) . "\r\n"); $read = $this->_fgets(); } elseif ($method == 'digest-md5') { $auth_sasl = Auth_SASL::factory('digestmd5'); $response = $auth_sasl->getResponse($username, $password, base64_decode($challenge[1]), $this->_host, 'imap'); fwrite($this->_stream, base64_encode($response) . "\r\n"); $response = explode(' ', $this->_fgets()); $response = base64_decode($response[1]); if (strpos($response, 'rspauth=') === false) { return PEAR::raiseError(lang("Unexpected response from server to Digest-MD5 response."), 'horde.error'); } fwrite($this->_stream, "\r\n"); $read = $this->_fgets(); } else { return PEAR::raiseError(lang("The IMAP server does not appear to support the authentication method selected. Please contact your system administrator."), 'horde.error'); } break; case 'login': $tag = $this->_generateSid(); $query = $tag . " LOGIN {$username} {" . strlen($password) . "}\r\n"; fwrite($this->_stream, $query); $read = $this->_fgets(); if (substr($read, 0, 1) == '+') { fwrite($this->_stream, "{$password}\r\n"); $read = $this->_fgets(); } else { return PEAR::raiseError(lang("Unexpected response from server to LOGIN command."), 'horde.error'); } break; case 'plain': $tag = $this->_generateSid(); $sasl = $this->queryCapability('SASL-IR'); $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"; fwrite($this->_stream, $query); $read = $this->_fgets(); } else { $query = $tag . " AUTHENTICATE PLAIN\r\n"; fwrite($this->_stream, $query); $read = $this->_fgets(); if (substr($read, 0, 1) == '+') { fwrite($this->_stream, "{$auth}\r\n"); $read = $this->_fgets(); } else { return PEAR::raiseError(lang("Unexpected response from server to AUTHENTICATE command."), 'horde.error'); } } break; } /* Check for failed login. */ $results = explode(' ', $read, 3); $response = $results[1]; if ($response != 'OK') { $message = !empty($results[2]) ? htmlspecialchars($results[2]) : lang("No message returned."); switch ($response) { case 'NO': return PEAR::raiseError(sprintf(lang("Bad login name or password."), $message), 'horde.error'); case 'BAD': default: return PEAR::raiseError(sprintf(lang("Bad request: %s"), $message), 'horde.error'); } } return true; }