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