/** * Creates a DigestChallenge object from a string * * Example input string: * <pre> * realm="example.com",nonce="GMybUaOM4lpMlJbeRwxOLzTalYDwLAxv/sLf8de4DPA=", * qop="auth,auth-int,auth-conf",cipher="rc4-40,rc4-56,rc4",charset=utf-8, * algorithm=md5-sess * </pre> * * @param string s * @return security.sasl.DigestChallenge * @throws lang.FormatException */ public static function fromString($s) { with($challenge = new DigestChallenge()); $s .= ','; while ($p = strpos($s, '=')) { $key = substr($s, 0, $p); $t = '"' == $s[$p + 1] ? strpos($s, '"', $p + 2) + 1 : strpos($s, ',', $p + 1); $value = trim(substr($s, $p + 1, $t - $p - 1), '"'); switch ($key) { case 'realm': $challenge->setRealm($value); break; case 'domain': $challenge->setDomain($value); break; case 'nonce': $challenge->setNonce($value); break; case 'qop': $challenge->setQop(explode(',', strtolower($value))); break; case 'cipher': $challenge->setCipher(explode(',', strtolower($value))); break; case 'charset': $challenge->setCharset($value); break; case 'algorithm': $challenge->setAlgorithm($value); break; case 'stale': $challenge->setStale(0 == strcasecmp($value, 'true')); break; case 'opaque': $challenge->setOpaque($value); break; case 'maxbuf': $challenge->setMaxbuf(intval($value)); break; default: throw new FormatException('Unrecognized key "' . $key . '"'); } $s = substr($s, $t + 1); } return $challenge; }
/** * Authenticate * * Supported methods: * <ul> * <li>PLAIN</li> * <li>LOGIN</li> * <li>DIGEST-MD5</li> * <li>CRAM-MD5</li> * </ul> * * @param string method one of the SIEVE_SASL_* constants * @param string user * @param string pass * @param string auth default NULL * @return bool success * @throws lang.IllegalArgumentException when the specified method is not supported */ public function authenticate($method, $user, $pass, $auth = NULL) { if (!$this->hasAuthenticationMethod($method)) { throw new IllegalArgumentException('Authentication method ' . $method . ' not supported'); } // Check whether we want to impersonate if (NULL === $auth) { $auth = $user; } // Send auth request depending on specified authentication method switch ($method) { case SIEVE_SASL_PLAIN: $e = base64_encode($auth . "" . $user . "" . $pass); $this->_sendcmd('AUTHENTICATE "PLAIN" {%d+}', strlen($e)); $this->_sendcmd($e); break; case SIEVE_SASL_LOGIN: $this->_sendcmd('AUTHENTICATE "LOGIN"'); $ue = base64_encode($user); $this->_sendcmd('{%d+}', strlen($ue)); $this->_sendcmd($ue); $pe = base64_encode($pass); $this->_sendcmd('{%d+}', strlen($pe)); $this->_sendcmd($pe); break; case SIEVE_SASL_DIGEST_MD5: $this->_sendcmd('AUTHENTICATE "DIGEST-MD5"'); // Read server challenge. Example (decoded): // // realm="example.com",nonce="GMybUaOM4lpMlJbeRwxOLzTalYDwLAxv/sLf8de4DPA=", // qop="auth,auth-int,auth-conf",cipher="rc4-40,rc4-56,rc4",charset=utf-8, // algorithm=md5-sess // // See also xp://security.sasl.DigestChallenge $len = $this->_sock->readLine(0x400); $str = base64_decode($this->_sock->readLine()); $this->cat && $this->cat->debug('Challenge (length ' . $len . '):', $str); $challenge = DigestChallenge::fromString($str); $response = $challenge->responseFor(DC_QOP_AUTH, $user, $pass, $auth); $this->cat && $this->cat->debug($challenge, $response); // Build and send challenge response $response->setDigestUri('sieve/' . $this->_sock->host); $cmd = $response->getString(); $this->cat && $this->cat->debug('Sending challenge response', $cmd); $this->_sendcmd('"%s"', base64_encode($cmd)); // Finally, read the response auth $len = $this->_sock->readLine(); $str = base64_decode($this->_sock->readLine()); $this->cat && $this->cat->debug('Response auth (length ' . $len . '):', $str); return TRUE; case SIEVE_SASL_CRAM_MD5: $this->_sendcmd('AUTHENTICATE "CRAM-MD5"'); // Read server challenge. Example (decoded): // // <*****@*****.**> // // See also rfc://2195 $len = $this->_sock->readLine(0x400); $challenge = base64_decode($this->_sock->readLine()); $this->cat && $this->cat->debug('Challenge (length ' . $len . '):', $challenge); // Build response and send it $response = sprintf('%s %s', $user, bin2hex(HMAC_MD5::hash($challenge, $pass))); $this->cat && $this->cat->debug('Sending challenge response', $response); $this->_sendcmd('"%s"', base64_encode($response)); break; default: throw new IllegalArgumentException('Authentication method ' . $method . ' not implemented'); } // Read the response. Examples: // // - OK // - NO ("SASL" "authentication failure") "Authentication error" return $this->_response(TRUE); }