[Svn] r4126 - in trunk/roundcubemail: . config program/include

trac at roundcube.net trac at roundcube.net
Sat Oct 23 19:03:44 CEST 2010


Author: alec
Date: 2010-10-23 12:03:44 -0500 (Sat, 23 Oct 2010)
New Revision: 4126

Modified:
   trunk/roundcubemail/CHANGELOG
   trunk/roundcubemail/config/main.inc.php.dist
   trunk/roundcubemail/program/include/rcube_imap_generic.php
Log:
- Add support for AUTH=DIGEST-MD5 in IMAP (RFC 2831)


Modified: trunk/roundcubemail/CHANGELOG
===================================================================
--- trunk/roundcubemail/CHANGELOG	2010-10-23 07:55:55 UTC (rev 4125)
+++ trunk/roundcubemail/CHANGELOG	2010-10-23 17:03:44 UTC (rev 4126)
@@ -46,6 +46,7 @@
 - Add support for AUTH=PLAIN in IMAP authentication
 - Re-implemented SMTP proxy authentication support
 - Add support for IMAP proxy authentication (#1486690)
+- Add support for AUTH=DIGEST-MD5 in IMAP (RFC 2831)
 
 RELEASE 0.4.2
 -------------

Modified: trunk/roundcubemail/config/main.inc.php.dist
===================================================================
--- trunk/roundcubemail/config/main.inc.php.dist	2010-10-23 07:55:55 UTC (rev 4125)
+++ trunk/roundcubemail/config/main.inc.php.dist	2010-10-23 17:03:44 UTC (rev 4126)
@@ -70,8 +70,8 @@
 // TCP port used for IMAP connections
 $rcmail_config['default_port'] = 143;
 
-// IMAP auth type. Can be "auth" (CRAM-MD5), "plain" (PLAIN), "login" (LOGIN)
-// or "check" (or empty) to auto detect. Optional, defaults to "check"
+// IMAP AUTH type (DIGEST-MD5, CRAM-MD5, LOGIN, PLAIN or empty to use
+// best server supported one)
 $rcmail_config['imap_auth_type'] = null;
 
 // If you know your imap's root directory and its folder delimiter,

Modified: trunk/roundcubemail/program/include/rcube_imap_generic.php
===================================================================
--- trunk/roundcubemail/program/include/rcube_imap_generic.php	2010-10-23 07:55:55 UTC (rev 4125)
+++ trunk/roundcubemail/program/include/rcube_imap_generic.php	2010-10-23 17:03:44 UTC (rev 4126)
@@ -371,56 +371,99 @@
     }
 
     /**
-     * CRAM-MD5/PLAIN Authentication
+     * DIGEST-MD5/CRAM-MD5/PLAIN Authentication
      *
      * @param string $user
      * @param string $pass
-     * @param string $type Authentication type (PLAIN or CRAM-MD5)
+     * @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') {
-            $ipad = '';
-            $opad = '';
-
-            // initialize ipad, opad
-            for ($i=0; $i<64; $i++) {
-                $ipad .= chr(0x36);
-                $opad .= chr(0x5C);
+        if ($type == 'CRAM-MD5' || $type == 'DIGEST-MD5') {
+            if ($type == 'DIGEST-MD5' && !class_exists('Auth_SASL')) {
+                $this->set_error(self::ERROR_BYE,
+                    "The Auth_SASL package is required for DIGEST-MD5 authentication");
+			    return self::ERROR_BAD;
             }
 
-            // pad $pass so it's 64 bytes
-            $padLen = 64 - strlen($pass);
-            for ($i=0; $i<$padLen; $i++) {
-                $pass .= chr(0);
-            }
-
-		    $this->putLine($this->next_tag() . " AUTHENTICATE CRAM-MD5");
+		    $this->putLine($this->next_tag() . " AUTHENTICATE $type");
 		    $line = trim($this->readLine(1024));
 
 		    if ($line[0] == '+') {
-			    $challenge = substr($line,2);
+			    $challenge = substr($line, 2);
             }
             else {
-			    return self::ERROR_BYE;
+                return $this->parseResult($line);
 		    }
 
-            // generate hash
-            $hash  = md5($this->_xor($pass, $opad) . pack("H*", md5($this->_xor($pass, $ipad) . base64_decode($encChallenge))));
-            $reply = base64_encode($user . ' ' . $hash);
+            if ($type == 'CRAM-MD5') {
+                // RFC2195: CRAM-MD5
+                $ipad = '';
+                $opad = '';
 
-            // send result, get reply and process it
-            $this->putLine($reply);
+                // 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);
+            }
+            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;
+                }
+                $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);
+                $line = $this->readLine(1024);
+                
+                if ($line[0] == '+') {
+			        $challenge = substr($line, 2);
+                }
+                else {
+                    return $this->parseResult($line);
+                }
+
+                // check response
+                $challenge = base64_decode($challenge);
+                if (strpos($challenge, 'rspauth=') === false) {
+                    $this->set_error(self::ERROR_BAD,
+                        "Unexpected response from server to DIGEST-MD5 response");
+                    return self::ERROR_BAD;
+                }
+
+                $this->putLine('');
+            }
+
             $line = $this->readLine(1024);
             $result = $this->parseResult($line);
-            if ($result != self::ERROR_OK) {
-                $this->set_error($result, "Unble to authenticate user (CRAM-MD5): $line");
-            }
         }
         else { // PLAIN
-            // proxy authentication
+            // proxy authorization
             if (!empty($this->prefs['auth_cid'])) {
                 $authc = $this->prefs['auth_cid'];
                 $pass  = $this->prefs['auth_pw'];
@@ -440,22 +483,22 @@
 	    	    $line = trim($this->readLine(1024));
 
 		        if ($line[0] != '+') {
-    			    return self::ERROR_BYE;
+    			    return $this->parseResult($line);
 	    	    }
 
                 // send result, get reply and process it
                 $this->putLine($reply);
                 $line = $this->readLine(1024);
                 $result = $this->parseResult($line);
-                if ($result != self::ERROR_OK) {
-                    $this->set_error($result, "Unble to authenticate user (AUTH): $line");
-                }
             }
         }
 
         if ($result == self::ERROR_OK) {
             return $this->fp;
         }
+        else {
+            $this->set_error($result, "Unable to authenticate user ($type): $line");
+        }
 
         return $result;
     }
@@ -696,8 +739,11 @@
 
 	    // check for supported auth methods
 	    if ($auth_method == 'CHECK') {
+		    if ($this->getCapability('AUTH=DIGEST-MD5')) {
+			    $auth_methods[] = 'DIGEST-MD5';
+		    }
 		    if ($this->getCapability('AUTH=CRAM-MD5') || $this->getCapability('AUTH=CRAM_MD5')) {
-			    $auth_methods[] = 'AUTH';
+			    $auth_methods[] = 'CRAM-MD5';
 		    }
 		    if ($this->getCapability('AUTH=PLAIN')) {
 			    $auth_methods[] = 'PLAIN';
@@ -708,17 +754,17 @@
 		    }
 	    }
         else {
-            $auth_methods[] = $auth_method;
+            // replace AUTH with CRAM-MD5 for backward compat.
+            $auth_methods[] = $auth_method == 'AUTH' ? 'CRAM-MD5' : $auth_method;
         }
 
         // Authenticate
         foreach ($auth_methods as $method) {
             switch ($method) {
-            case 'AUTH':
-			    $result = $this->authenticate($user, $password, 'CRAM-MD5');
-		        break;
+            case 'DIGEST-MD5':
+            case 'CRAM-MD5':
 	        case 'PLAIN':
-			    $result = $this->authenticate($user, $password, 'PLAIN');
+			    $result = $this->authenticate($user, $password, $method);
 		        break;
             case 'LOGIN':
        	        $result = $this->login($user, $password);

_______________________________________________
http://lists.roundcube.net/mailman/listinfo/svn



More information about the Svn mailing list