CRAM-MD5 / DIGEST-MD5 patch for sieve-php.lib
Scott Russell
lnxgeek at us.ibm.com
Wed Sep 25 11:47:35 EDT 2002
Greets.
The attached patch provides CRAM-MD5 and DIGEST-MD5 support for
sieve-php.lib. It should apply clean against the current 0.0.9
version. The patch requires the php mhash extension found at
http://mhash.sf.net/
Kudos to Dan for his sieve-php.lib class.
--
Scott Russell (lnxgeek at us.ibm.com)
Linux Technology Center, System Admin, RHCE.
Dial 877-735-8200 then ask for 919-543-9289 (TTY)
-------------- next part --------------
--- sieve-php.lib.orig Thu Jan 31 19:48:40 2002
+++ sieve-php.lib Sun Sep 22 20:03:32 2002
@@ -489,6 +489,82 @@
return false;
$this->loggedin=true;
return true;
+ break;
+
+ case "DIGEST-MD5":
+ // SASL DIGEST-MD5 support works with timsieved 1.1.0
+ // follows rfc2831 for generating the $response to $challenge
+ // requires php mhash extension
+ fputs($this->fp, "AUTHENTICATE \"DIGEST-MD5\"\r\n");
+ // $clen is length of server challenge, we ignore it.
+ $clen = fgets($this->fp, 1024);
+ // read for 2048, rfc2831 max length allowed
+ $challenge = fgets($this->fp, 2048);
+ // vars used when building $response_value and $response
+ $cnonce = base64_encode(bin2hex(mhash(MHASH_MD5, microtime())));
+ $ncount = "00000001";
+ $qop_value = "auth";
+ $digest_uri_value = "sieve/$this->host";
+ // decode the challenge string
+ $result = decode_challenge($challenge);
+ // verify server supports qop=auth
+ $qop = explode(",",$result['qop']);
+ if (!in_array($qop_value, $qop)) {
+ // rfc2831: client MUST fail if no qop methods supported
+ return false;
+ }
+ // build the $response_value
+ $string_a1 = utf8_encode($this->user).":";
+ $string_a1 .= utf8_encode($result['realm']).":";
+ $string_a1 .= utf8_encode($this->pass);
+ $string_a1 = mhash(MHASH_MD5, $string_a1);
+ $A1 = $string_a1.":".$result['nonce'].":".$cnonce.":".utf8_encode($this->auth);
+ $A1 = bin2hex(mhash(MHASH_MD5, $A1));
+ $A2 = bin2hex(mhash(MHASH_MD5, "AUTHENTICATE:$digest_uri_value"));
+ $string_response = $result['nonce'].":".$ncount.":".$cnonce.":".$qop_value;
+ $response_value = bin2hex(mhash(MHASH_MD5, $A1.":".$string_response.":".$A2));
+ // build the challenge $response
+ $reply = "charset=utf-8,username=\"".$this->user."\",realm=\"".$result['realm']."\",";
+ $reply .= "nonce=\"".$result['nonce']."\",nc=$ncount,cnonce=\"$cnonce\",";
+ $reply .= "digest-uri=\"$digest_uri_value\",response=$response_value,";
+ $reply .= "qop=$qop_value,authzid=\"".utf8_encode($this->auth)."\"";
+ $response = base64_encode($reply);
+ fputs($this->fp, "\"$response\"\r\n");
+
+ $this->line = fgets($this->fp, 1024);
+ while(sieve::status($this->line) == F_DATA)
+ $this->line = fgets($this->fp,1024);
+
+ if(sieve::status($this->line) == F_NO)
+ return false;
+ $this->loggedin = TRUE;
+ return TRUE;
+ break;
+
+ case "CRAM-MD5":
+ // SASL CRAM-MD5 support works with timsieved 1.1.0
+ // follows rfc2195 for generating the $response to $challenge
+ // CRAM-MD5 does not support proxy of $auth by $user
+ // requires php mhash extension
+ fputs($this->fp, "AUTHENTICATE \"CRAM-MD5\"\r\n");
+ // $clen is the length of the challenge line the server gives us
+ $clen = fgets($this->fp, 1024);
+ // read for 1024, should be long enough?
+ $challenge = fgets($this->fp, 1024);
+ // build a response to the challenge
+ $hash = bin2hex(mhash(MHASH_MD5, base64_decode($challenge), $this->pass));
+ $response = base64_encode($this->user." ".$hash);
+ // respond to the challenge string
+ fputs($this->fp, "\"$response\"\r\n");
+
+ $this->line = fgets($this->fp, 1024);
+ while(sieve::status($this->line) == F_DATA)
+ $this->line = fgets($this->fp,1024);
+
+ if(sieve::status($this->line) == F_NO)
+ return false;
+ $this->loggedin = TRUE;
+ return TRUE;
break;
default:
@@ -503,6 +579,18 @@
}
-
+function decode_challenge ($input) {
+ // FIXME: this function is a hack to decode the challenge
+ // from timsieved 1.1.0. It may not work with other versions
+ // and most certainly won't work with other DIGEST-MD5 implentations
+ $input = base64_decode($input);
+ preg_match("/nonce=\"(.*)\"/U",$input, $matches);
+ $resp['nonce'] = $matches[1];
+ preg_match("/realm=\"(.*)\"/U",$input, $matches);
+ $resp['realm'] = $matches[1];
+ preg_match("/qop=\"(.*)\"/U",$input, $matches);
+ $resp['qop'] = $matches[1];
+ return $resp;
+}
?>
More information about the Info-cyrus
mailing list