Overview

Packages

  • Imap
    • Client

Classes

  • Horde_Imap_Client
  • Horde_Imap_Client_Auth_DigestMD5
  • Horde_Imap_Client_Base
  • Horde_Imap_Client_Cache
  • Horde_Imap_Client_Data_Acl
  • Horde_Imap_Client_Data_AclCommon
  • Horde_Imap_Client_Data_AclNegative
  • Horde_Imap_Client_Data_AclRights
  • Horde_Imap_Client_Data_Envelope
  • Horde_Imap_Client_Data_Fetch
  • Horde_Imap_Client_Data_Fetch_Pop3
  • Horde_Imap_Client_Data_Thread
  • Horde_Imap_Client_DateTime
  • Horde_Imap_Client_Exception
  • Horde_Imap_Client_Exception_NoSupportExtension
  • Horde_Imap_Client_Fetch_Query
  • Horde_Imap_Client_Ids
  • Horde_Imap_Client_Ids_Pop3
  • Horde_Imap_Client_Mailbox
  • Horde_Imap_Client_Search_Query
  • Horde_Imap_Client_Socket
  • Horde_Imap_Client_Socket_Pop3
  • Horde_Imap_Client_Sort
  • Horde_Imap_Client_Translation
  • Horde_Imap_Client_Utf7imap
  • Horde_Imap_Client_Utils
  • Horde_Imap_Client_Utils_Pop3
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Provides the code needed to authenticate via the DIGEST-MD5 SASL mechanism
  4:  * (defined in RFC 2831). This method has been obsoleted by RFC 6331, but
  5:  * still is in use on legacy servers.
  6:  *
  7:  * Copyright (c) 2002-2003 Richard Heyes
  8:  * Copyright 2011-2012 Horde LLC (http://www.horde.org/)
  9:  *
 10:  * @author   Richard Heyes <richard@php.net>
 11:  * @author   Michael Slusarz <slusarz@horde.org>
 12:  * @category Horde
 13:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 14:  * @package  Imap_Client
 15:  *
 16:  * This code is based on the original code contained in the PEAR Auth_SASL
 17:  * package (v0.5.1):
 18:  *   $Id: DigestMD5.php 294702 2010-02-07 16:03:55Z cweiske $
 19:  *
 20:  * That code is covered by the BSD 3-Clause license, as set forth below:
 21:  * +-----------------------------------------------------------------------+
 22:  * | Copyright (c) 2002-2003 Richard Heyes                                 |
 23:  * | All rights reserved.                                                  |
 24:  * |                                                                       |
 25:  * | Redistribution and use in source and binary forms, with or without    |
 26:  * | modification, are permitted provided that the following conditions    |
 27:  * | are met:                                                              |
 28:  * |                                                                       |
 29:  * | o Redistributions of source code must retain the above copyright      |
 30:  * |   notice, this list of conditions and the following disclaimer.       |
 31:  * | o Redistributions in binary form must reproduce the above copyright   |
 32:  * |   notice, this list of conditions and the following disclaimer in the |
 33:  * |   documentation and/or other materials provided with the distribution.|
 34:  * | o The names of the authors may not be used to endorse or promote      |
 35:  * |   products derived from this software without specific prior written  |
 36:  * |   permission.                                                         |
 37:  * |                                                                       |
 38:  * | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
 39:  * | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
 40:  * | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
 41:  * | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
 42:  * | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
 43:  * | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
 44:  * | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
 45:  * | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
 46:  * | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
 47:  * | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
 48:  * | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
 49:  * +-----------------------------------------------------------------------+
 50:  */
 51: 
 52: class Horde_Imap_Client_Auth_DigestMD5
 53: {
 54:     /**
 55:      * Digest response components.
 56:      *
 57:      * @var string
 58:      */
 59:     protected $_response;
 60: 
 61:     /**
 62:      * Generate the Digest-MD5 response.
 63:      *
 64:      * @param string $id         Authentication id (username).
 65:      * @param string $pass       Password.
 66:      * @param string $challenge  The digest challenge sent by the server.
 67:      * @param string $hostname   The hostname of the machine connecting to.
 68:      * @param string $service    The service name (e.g. 'imap', 'pop3').
 69:      *
 70:      * @throws Horde_Imap_Client_Exception
 71:      */
 72:     public function __construct($id, $pass, $challenge, $hostname, $service)
 73:     {
 74:         $challenge = $this->_parseChallenge($challenge);
 75:         $cnonce = $this->_getCnonce();
 76:         $digest_uri = sprintf('%s/%s', $service, $hostname);
 77: 
 78:         /* Get response value. */
 79:         $A1 = sprintf('%s:%s:%s', pack('H32', hash('md5', sprintf('%s:%s:%s', $id, $challenge['realm'], $pass))), $challenge['nonce'], $cnonce);
 80:         $A2 = 'AUTHENTICATE:' . $digest_uri;
 81:         $response_value = hash('md5', sprintf('%s:%s:00000001:%s:auth:%s', hash('md5', $A1), $challenge['nonce'], $cnonce, hash('md5', $A2)));
 82: 
 83:         $this->_response = array(
 84:             'cnonce' => '"' . $cnonce . '"',
 85:             'digest-uri' => '"' . $digest_uri . '"',
 86:             'maxbuf' => $challenge['maxbuf'],
 87:             'nc' => '00000001',
 88:             'nonce' => '"' . $challenge['nonce'] . '"',
 89:             'qop' => 'auth',
 90:             'response' => $response_value,
 91:             'username' => '"' . $id . '"'
 92:         );
 93: 
 94:         if (strlen($challenge['realm'])) {
 95:             $this->_response['realm'] = '"' . $challenge['realm'] . '"';
 96:         }
 97:     }
 98: 
 99:     /**
100:      * Cooerce to string.
101:      *
102:      * @return string  The digest response (not base64 encoded).
103:      */
104:     public function __toString()
105:     {
106:         $out = array();
107:         foreach ($this->_response as $key => $val) {
108:             $out[] = $key . '=' . $val;
109:         }
110:         return implode(',', $out);
111:     }
112: 
113:     /**
114:      * Return specific digest response directive.
115:      *
116:      * @return mixed  Requested directive, or null if it does not exist.
117:      */
118:     public function __get($name)
119:     {
120:         return isset($this->_response[$name])
121:             ? $this->_response[$name]
122:             : null;
123:     }
124: 
125:     /**
126:     * Parses and verifies the digest challenge.
127:     *
128:     * @param string $challenge  The digest challenge
129:     *
130:     * @return array  The parsed challenge as an array with directives as keys.
131:     *
132:     * @throws Horde_Imap_Client_Exception
133:     */
134:     protected function _parseChallenge($challenge)
135:     {
136:         $tokens = array(
137:             'maxbuf' => 65536,
138:             'realm' => ''
139:         );
140: 
141:         preg_match_all('/([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $challenge, $matches, PREG_SET_ORDER);
142: 
143:         foreach ($matches as $val) {
144:             $tokens[$val[1]] = trim($val[2], '"');
145:         }
146: 
147:         // Required directives.
148:         if (!isset($tokens['nonce']) || !isset($tokens['algorithm'])) {
149:             throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Authentication failure."), 'SERVER_CONNECT');
150:         }
151: 
152:         return $tokens;
153:     }
154: 
155:     /**
156:      * Creates the client nonce for the response
157:      *
158:      * @return string  The cnonce value.
159:      */
160:     protected function _getCnonce()
161:     {
162:         if ((@is_readable('/dev/urandom') &&
163:              ($fd = @fopen('/dev/urandom', 'r'))) ||
164:             (@is_readable('/dev/random') &&
165:              ($fd = @fopen('/dev/random', 'r')))) {
166:             $str = fread($fd, 32);
167:             fclose($fd);
168:         } else {
169:             $str = '';
170:             for ($i = 0; $i < 32; ++$i) {
171:                 $str .= chr(mt_rand(0, 255));
172:             }
173:         }
174: 
175:         return base64_encode($str);
176:     }
177: 
178: }
179: 
API documentation generated by ApiGen