From 3b8c248237a9594849a0dfae435adf36c052573b Mon Sep 17 00:00:00 2001 From: jonathanharker Date: Fri, 12 Dec 2008 04:44:53 +0000 Subject: [PATCH] MDL-17549 auth/radius: add CHAP and MSCHAP auth support, detect PHP extension * Added support for CHAP and MSCHAP authentication schemes contributed by Stanislav Tsymbalov http://www.tsymbalov.net/ original code at http://sourceforge.net/projects/moodleradius/ * Tweak the detection of PHP RADIUS extension and Pear code * Update the warning notices to use more Moodly CSS classes lib/pear: Add RADIUS and CHAP PEAR libs * Add PEAR Auth_RADIUS and Crypt_CHAP packages to lib/pear Author: Jonathan Harker --- auth/radius/auth.php | 52 +- auth/radius/config.html | 35 +- lang/en_utf8/auth.php | 8 +- lib/pear/Auth/RADIUS.php | 1001 ++++++++++++++++++++++++++++++++++++++ lib/pear/Crypt/CHAP.php | 464 ++++++++++++++++++ lib/pear/README.txt | 11 + 6 files changed, 1565 insertions(+), 6 deletions(-) create mode 100644 lib/pear/Auth/RADIUS.php create mode 100644 lib/pear/Crypt/CHAP.php diff --git a/auth/radius/auth.php b/auth/radius/auth.php index 4e2dd44edb..d84e2a956e 100644 --- a/auth/radius/auth.php +++ b/auth/radius/auth.php @@ -9,8 +9,10 @@ * * Authenticates against a RADIUS server. * Contributed by Clive Gould + * CHAP support contributed by Stanislav Tsymbalov http://www.tsymbalov.net/ * * 2006-08-31 File created. + * 2008-03-12 CHAP support added by Stanislav Tsymbalov. */ if (!defined('MOODLE_INTERNAL')) { @@ -42,6 +44,7 @@ class auth_plugin_radius extends auth_plugin_base { */ function user_login ($username, $password) { require_once 'Auth/RADIUS.php'; + require_once 'Crypt/CHAP.php'; // Added by Clive on 7th May for test purposes // printf("Username: $username
"); @@ -50,9 +53,52 @@ class auth_plugin_radius extends auth_plugin_base { // printf("nasport: $this->config->nasport
"); // printf("secret: $this->config->secret
"); - $rauth = new Auth_RADIUS_PAP($username, $password); + // Added by Stanislav Tsymbalov on 12th March 2008 only for test purposes + //$type = 'PAP'; + //$type = 'CHAP_MD5'; + //$type = 'MSCHAPv1'; + //$type = 'MSCHAPv2'; + $type = $this->config->radiustype; + if (empty($type)) { + $type = 'PAP'; + } + + $classname = 'Auth_RADIUS_' . $type; + $rauth = new $classname($username, $password); $rauth->addServer($this->config->host, $this->config->nasport, $this->config->secret); + $rauth->username = $username; + + switch($type) { + case 'CHAP_MD5': + case 'MSCHAPv1': + $classname = $type == 'MSCHAPv1' ? 'Crypt_CHAP_MSv1' : 'Crypt_CHAP_MD5'; + $crpt = new $classname; + $crpt->password = $password; + $rauth->challenge = $crpt->challenge; + $rauth->chapid = $crpt->chapid; + $rauth->response = $crpt->challengeResponse(); + $rauth->flags = 1; + // If you must use deprecated and weak LAN-Manager-Responses use this: + // $rauth->lmResponse = $crpt->lmChallengeResponse(); + // $rauth->flags = 0; + break; + + case 'MSCHAPv2': + $crpt = new Crypt_CHAP_MSv2; + $crpt->username = $username; + $crpt->password = $password; + $rauth->challenge = $crpt->authChallenge; + $rauth->peerChallenge = $crpt->peerChallenge; + $rauth->chapid = $crpt->chapid; + $rauth->response = $crpt->challengeResponse(); + break; + + default: + $rauth->password = $password; + break; + } + if (!$rauth->start()) { printf("Radius start: %s
\n", $rauth->getError()); exit; @@ -122,6 +168,9 @@ class auth_plugin_radius extends auth_plugin_base { if (!isset ($config->nasport)) { $config->nasport = '1812'; } + if (!isset($config->radiustype)) { + $config->radiustype = 'PAP'; + } if (!isset ($config->secret)) { $config->secret = ''; } @@ -134,6 +183,7 @@ class auth_plugin_radius extends auth_plugin_base { set_config('nasport', $config->nasport, 'auth/radius'); set_config('secret', $config->secret, 'auth/radius'); set_config('changepasswordurl', $config->changepasswordurl, 'auth/radius'); + set_config('radiustype', $config->radiustype, 'auth/radius'); return true; } diff --git a/auth/radius/config.html b/auth/radius/config.html index 449f0c0694..d56f9477c9 100644 --- a/auth/radius/config.html +++ b/auth/radius/config.html @@ -1,10 +1,12 @@ Warning: The Auth_RADIUS module does not seem to be present. Please ensure it is installed and enabled.

'; +if (!extension_loaded('radius')) { + print '

Warning: The PHP RADIUS extension is not present. Please ensure it is installed and enabled.

'; +} +include_once 'Auth/RADIUS.php'; +if (!class_exists('Auth_RADIUS')) { + print '

Warning: There is a problem with the PHP Pear Auth_RADIUS package. Please ensure it is installed correctly.

'; } // set to defaults if undefined @@ -14,6 +16,9 @@ if (!isset($config->host)) { if (!isset($config->nasport)) { $config->nasport = '1812'; } +if (!isset($config->radiustype)) { + $config->radiustype = 'PAP'; +} if (!isset($config->secret)) { $config->secret = ''; } @@ -54,6 +59,28 @@ if (!isset($config->changepasswordurl)) { + + : + +radiustype, ''); + + if (isset($err['radiustype'])) { + formerr($err['radiustype']); + } + +?> + + + + + : diff --git a/lang/en_utf8/auth.php b/lang/en_utf8/auth.php index 55b22b2756..db94c0bc6e 100644 --- a/lang/en_utf8/auth.php +++ b/lang/en_utf8/auth.php @@ -314,10 +314,16 @@ $string['auth_radiusdescription'] = 'This method uses a +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This code cannot simply be copied and put under the GNU Public License or +any other GPL-like (LGPL, GPL2) License. + + $Id$ +*/ + +require_once 'PEAR.php'; + +/** +* Client implementation of RADIUS. This are wrapper classes for +* the RADIUS PECL. +* Provides RADIUS Authentication (RFC2865) and RADIUS Accounting (RFC2866). +* +* @package Auth_RADIUS +* @author Michael Bretterklieber +* @access public +* @version $Revision$ +*/ + +PEAR::loadExtension('radius'); + +/** + * class Auth_RADIUS + * + * Abstract base class for RADIUS + * + * @package Auth_RADIUS + */ +class Auth_RADIUS extends PEAR { + + /** + * List of RADIUS servers. + * @var array + * @see addServer(), putServer() + */ + var $_servers = array(); + + /** + * Path to the configuration-file. + * @var string + * @see setConfigFile() + */ + var $_configfile = null; + + /** + * Resource. + * @var resource + * @see open(), close() + */ + var $res = null; + + /** + * Username for authentication and accounting requests. + * @var string + */ + var $username = null; + + /** + * Password for plaintext-authentication (PAP). + * @var string + */ + var $password = null; + + /** + * List of known attributes. + * @var array + * @see dumpAttributes(), getAttributes() + */ + var $attributes = array(); + + /** + * List of raw attributes. + * @var array + * @see dumpAttributes(), getAttributes() + */ + var $rawAttributes = array(); + + /** + * List of raw vendor specific attributes. + * @var array + * @see dumpAttributes(), getAttributes() + */ + var $rawVendorAttributes = array(); + + /** + * Switch whether we should put standard attributes or not + * @var bool + * @see putStandardAttributes() + */ + var $useStandardAttributes = true; + + /** + * Constructor + * + * Loads the RADIUS PECL/extension + * + * @return void + */ + function Auth_RADIUS() + { + $this->PEAR(); + } + + /** + * Adds a RADIUS server to the list of servers for requests. + * + * At most 10 servers may be specified. When multiple servers + * are given, they are tried in round-robin fashion until a + * valid response is received + * + * @access public + * @param string $servername Servername or IP-Address + * @param integer $port Portnumber + * @param string $sharedSecret Shared secret + * @param integer $timeout Timeout for each request + * @param integer $maxtries Max. retries for each request + * @return void + */ + function addServer($servername = 'localhost', $port = 0, $sharedSecret = 'testing123', $timeout = 3, $maxtries = 3) + { + $this->_servers[] = array($servername, $port, $sharedSecret, $timeout, $maxtries); + } + + /** + * Returns an error message, if an error occurred. + * + * @access public + * @return string + */ + function getError() + { + return radius_strerror($this->res); + } + + /** + * Sets the configuration-file. + * + * @access public + * @param string $file Path to the configuration file + * @return void + */ + function setConfigfile($file) + { + $this->_configfile = $file; + } + + /** + * Puts an attribute. + * + * @access public + * @param integer $attrib Attribute-number + * @param mixed $port Attribute-value + * @param type $type Attribute-type + * @return bool true on success, false on error + */ + function putAttribute($attrib, $value, $type = null) + { + if ($type == null) { + $type = gettype($value); + } + + switch ($type) { + case 'integer': + case 'double': + return radius_put_int($this->res, $attrib, $value); + + case 'addr': + return radius_put_addr($this->res, $attrib, $value); + + case 'string': + default: + return radius_put_attr($this->res, $attrib, $value); + } + + } + + /** + * Puts a vendor-specific attribute. + * + * @access public + * @param integer $vendor Vendor (MSoft, Cisco, ...) + * @param integer $attrib Attribute-number + * @param mixed $port Attribute-value + * @param type $type Attribute-type + * @return bool true on success, false on error + */ + function putVendorAttribute($vendor, $attrib, $value, $type = null) + { + + if ($type == null) { + $type = gettype($value); + } + + switch ($type) { + case 'integer': + case 'double': + return radius_put_vendor_int($this->res, $vendor, $attrib, $value); + + case 'addr': + return radius_put_vendor_addr($this->res, $vendor,$attrib, $value); + + case 'string': + default: + return radius_put_vendor_attr($this->res, $vendor, $attrib, $value); + } + + } + + /** + * Prints known attributes received from the server. + * + * @access public + */ + function dumpAttributes() + { + foreach ($this->attributes as $name => $data) { + echo "$name:$data
\n"; + } + } + + /** + * Overwrite this. + * + * @access public + */ + function open() + { + } + + /** + * Overwrite this. + * + * @access public + */ + function createRequest() + { + } + + /** + * Puts standard attributes. + * + * @access public + */ + function putStandardAttributes() + { + if (!$this->useStandardAttributes) + return; + + if (isset($_SERVER)) { + $var = &$_SERVER; + } else { + $var = &$GLOBALS['HTTP_SERVER_VARS']; + } + + $this->putAttribute(RADIUS_NAS_IDENTIFIER, isset($var['HTTP_HOST']) ? $var['HTTP_HOST'] : 'localhost'); + $this->putAttribute(RADIUS_NAS_PORT_TYPE, RADIUS_VIRTUAL); + $this->putAttribute(RADIUS_SERVICE_TYPE, RADIUS_FRAMED); + $this->putAttribute(RADIUS_FRAMED_PROTOCOL, RADIUS_PPP); + $this->putAttribute(RADIUS_CALLING_STATION_ID, isset($var['REMOTE_HOST']) ? $var['REMOTE_HOST'] : '127.0.0.1'); + } + + /** + * Puts custom attributes. + * + * @access public + */ + function putAuthAttributes() + { + if (isset($this->username)) { + $this->putAttribute(RADIUS_USER_NAME, $this->username); + } + } + + /** + * Configures the radius library. + * + * @access public + * @param string $servername Servername or IP-Address + * @param integer $port Portnumber + * @param string $sharedSecret Shared secret + * @param integer $timeout Timeout for each request + * @param integer $maxtries Max. retries for each request + * @return bool true on success, false on error + * @see addServer() + */ + function putServer($servername, $port = 0, $sharedsecret = 'testing123', $timeout = 3, $maxtries = 3) + { + if (!radius_add_server($this->res, $servername, $port, $sharedsecret, $timeout, $maxtries)) { + return false; + } + return true; + } + + /** + * Configures the radius library via external configurationfile + * + * @access public + * @param string $servername Servername or IP-Address + * @return bool true on success, false on error + */ + function putConfigfile($file) + { + if (!radius_config($this->res, $file)) { + return false; + } + return true; + } + + /** + * Initiates a RADIUS request. + * + * @access public + * @return bool true on success, false on errors + */ + function start() + { + if (!$this->open()) { + return false; + } + + foreach ($this->_servers as $s) { + // Servername, port, sharedsecret, timeout, retries + if (!$this->putServer($s[0], $s[1], $s[2], $s[3], $s[4])) { + return false; + } + } + + if (!empty($this->_configfile)) { + if (!$this->putConfigfile($this->_configfile)) { + return false; + } + } + + $this->createRequest(); + $this->putStandardAttributes(); + $this->putAuthAttributes(); + return true; + } + + /** + * Sends a prepared RADIUS request and waits for a response + * + * @access public + * @return mixed true on success, false on reject, PEAR_Error on error + */ + function send() + { + $req = radius_send_request($this->res); + if (!$req) { + return $this->raiseError('Error sending request: ' . $this->getError()); + } + + switch($req) { + case RADIUS_ACCESS_ACCEPT: + if (is_subclass_of($this, 'auth_radius_acct')) { + return $this->raiseError('RADIUS_ACCESS_ACCEPT is unexpected for accounting'); + } + return true; + + case RADIUS_ACCESS_REJECT: + return false; + + case RADIUS_ACCOUNTING_RESPONSE: + if (is_subclass_of($this, 'auth_radius_pap')) { + return $this->raiseError('RADIUS_ACCOUNTING_RESPONSE is unexpected for authentication'); + } + return true; + + default: + return $this->raiseError("Unexpected return value: $req"); + } + + } + + /** + * Reads all received attributes after sending the request. + * + * This methods stores known attributes in the property attributes, + * all attributes (including known attibutes) are stored in rawAttributes + * or rawVendorAttributes. + * NOTE: call this function also even if the request was rejected, because the + * Server returns usualy an errormessage + * + * @access public + * @return bool true on success, false on error + */ + function getAttributes() + { + + while ($attrib = radius_get_attr($this->res)) { + + if (!is_array($attrib)) { + return false; + } + + $attr = $attrib['attr']; + $data = $attrib['data']; + + $this->rawAttributes[$attr] = $data; + + switch ($attr) { + case RADIUS_FRAMED_IP_ADDRESS: + $this->attributes['framed_ip'] = radius_cvt_addr($data); + break; + + case RADIUS_FRAMED_IP_NETMASK: + $this->attributes['framed_mask'] = radius_cvt_addr($data); + break; + + case RADIUS_FRAMED_MTU: + $this->attributes['framed_mtu'] = radius_cvt_int($data); + break; + + case RADIUS_FRAMED_COMPRESSION: + $this->attributes['framed_compression'] = radius_cvt_int($data); + break; + + case RADIUS_SESSION_TIMEOUT: + $this->attributes['session_timeout'] = radius_cvt_int($data); + break; + + case RADIUS_IDLE_TIMEOUT: + $this->attributes['idle_timeout'] = radius_cvt_int($data); + break; + + case RADIUS_SERVICE_TYPE: + $this->attributes['service_type'] = radius_cvt_int($data); + break; + + case RADIUS_CLASS: + $this->attributes['class'] = radius_cvt_string($data); + break; + + case RADIUS_FRAMED_PROTOCOL: + $this->attributes['framed_protocol'] = radius_cvt_int($data); + break; + + case RADIUS_FRAMED_ROUTING: + $this->attributes['framed_routing'] = radius_cvt_int($data); + break; + + case RADIUS_FILTER_ID: + $this->attributes['filter_id'] = radius_cvt_string($data); + break; + + case RADIUS_REPLY_MESSAGE: + $this->attributes['reply_message'] = radius_cvt_string($data); + break; + + case RADIUS_VENDOR_SPECIFIC: + $attribv = radius_get_vendor_attr($data); + if (!is_array($attribv)) { + return false; + } + + $vendor = $attribv['vendor']; + $attrv = $attribv['attr']; + $datav = $attribv['data']; + + $this->rawVendorAttributes[$vendor][$attrv] = $datav; + + if ($vendor == RADIUS_VENDOR_MICROSOFT) { + + switch ($attrv) { + case RADIUS_MICROSOFT_MS_CHAP2_SUCCESS: + $this->attributes['ms_chap2_success'] = radius_cvt_string($datav); + break; + + case RADIUS_MICROSOFT_MS_CHAP_ERROR: + $this->attributes['ms_chap_error'] = radius_cvt_string(substr($datav,1)); + break; + + case RADIUS_MICROSOFT_MS_CHAP_DOMAIN: + $this->attributes['ms_chap_domain'] = radius_cvt_string($datav); + break; + + case RADIUS_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY: + $this->attributes['ms_mppe_encryption_policy'] = radius_cvt_int($datav); + break; + + case RADIUS_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES: + $this->attributes['ms_mppe_encryption_types'] = radius_cvt_int($datav); + break; + + case RADIUS_MICROSOFT_MS_CHAP_MPPE_KEYS: + $demangled = radius_demangle($this->res, $datav); + $this->attributes['ms_chap_mppe_lm_key'] = substr($demangled, 0, 8); + $this->attributes['ms_chap_mppe_nt_key'] = substr($demangled, 8, RADIUS_MPPE_KEY_LEN); + break; + + case RADIUS_MICROSOFT_MS_MPPE_SEND_KEY: + $this->attributes['ms_chap_mppe_send_key'] = radius_demangle_mppe_key($this->res, $datav); + break; + + case RADIUS_MICROSOFT_MS_MPPE_RECV_KEY: + $this->attributes['ms_chap_mppe_recv_key'] = radius_demangle_mppe_key($this->res, $datav); + break; + + case RADIUS_MICROSOFT_MS_PRIMARY_DNS_SERVER: + $this->attributes['ms_primary_dns_server'] = radius_cvt_string($datav); + break; + } + } + break; + + } + } + + return true; + } + + /** + * Frees resources. + * + * Calling this method is always a good idea, because all security relevant + * attributes are filled with Nullbytes to leave nothing in the mem. + * + * @access public + */ + function close() + { + if ($this->res != null) { + radius_close($this->res); + $this->res = null; + } + $this->username = str_repeat("\0", strlen($this->username)); + $this->password = str_repeat("\0", strlen($this->password)); + } + +} + +/** + * class Auth_RADIUS_PAP + * + * Class for authenticating using PAP (Plaintext) + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_PAP extends Auth_RADIUS +{ + + /** + * Constructor + * + * @param string $username Username + * @param string $password Password + * @return void + */ + function Auth_RADIUS_PAP($username = null, $password = null) + { + $this->Auth_RADIUS(); + $this->username = $username; + $this->password = $password; + } + + /** + * Creates a RADIUS resource + * + * Creates a RADIUS resource for authentication. This should be the first + * call before you make any other things with the library. + * + * @return bool true on success, false on error + */ + function open() + { + $this->res = radius_auth_open(); + if (!$this->res) { + return false; + } + return true; + } + + /** + * Creates an authentication request + * + * Creates an authentication request. + * You MUST call this method before you can put any attribute + * + * @return bool true on success, false on error + */ + function createRequest() + { + if (!radius_create_request($this->res, RADIUS_ACCESS_REQUEST)) { + return false; + } + return true; + } + + /** + * Put authentication specific attributes + * + * @return void + */ + function putAuthAttributes() + { + if (isset($this->username)) { + $this->putAttribute(RADIUS_USER_NAME, $this->username); + } + if (isset($this->password)) { + $this->putAttribute(RADIUS_USER_PASSWORD, $this->password); + } + } + +} + +/** + * class Auth_RADIUS_CHAP_MD5 + * + * Class for authenticating using CHAP-MD5 see RFC1994. + * Instead og the plaintext password the challenge and + * the response are needed. + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_CHAP_MD5 extends Auth_RADIUS_PAP +{ + /** + * 8 Bytes binary challenge + * @var string + */ + var $challenge = null; + + /** + * 16 Bytes MD5 response binary + * @var string + */ + var $response = null; + + /** + * Id of the authentication request. Should incremented after every request. + * @var integer + */ + var $chapid = 1; + + /** + * Constructor + * + * @param string $username Username + * @param string $challenge 8 Bytes Challenge (binary) + * @param integer $chapid Requestnumber + * @return void + */ + function Auth_RADIUS_CHAP_MD5($username = null, $challenge = null, $chapid = 1) + { + $this->Auth_RADIUS_PAP(); + $this->username = $username; + $this->challenge = $challenge; + $this->chapid = $chapid; + } + + /** + * Put CHAP-MD5 specific attributes + * + * For authenticating using CHAP-MD5 via RADIUS you have to put the challenge + * and the response. The chapid is inserted in the first byte of the response. + * + * @return void + */ + function putAuthAttributes() + { + if (isset($this->username)) { + $this->putAttribute(RADIUS_USER_NAME, $this->username); + } + if (isset($this->response)) { + $response = pack('C', $this->chapid) . $this->response; + $this->putAttribute(RADIUS_CHAP_PASSWORD, $response); + } + if (isset($this->challenge)) { + $this->putAttribute(RADIUS_CHAP_CHALLENGE, $this->challenge); + } + } + + /** + * Frees resources. + * + * Calling this method is always a good idea, because all security relevant + * attributes are filled with Nullbytes to leave nothing in the mem. + * + * @access public + */ + function close() + { + Auth_RADIUS_PAP::close(); + $this->challenge = str_repeat("\0", strlen($this->challenge)); + $this->response = str_repeat("\0", strlen($this->response)); + } + +} + +/** + * class Auth_RADIUS_MSCHAPv1 + * + * Class for authenticating using MS-CHAPv1 see RFC2433 + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_MSCHAPv1 extends Auth_RADIUS_CHAP_MD5 +{ + /** + * LAN-Manager-Response + * @var string + */ + var $lmResponse = null; + + /** + * Wether using deprecated LM-Responses or not. + * 0 = use LM-Response, 1 = use NT-Response + * @var bool + */ + var $flags = 1; + + /** + * Put MS-CHAPv1 specific attributes + * + * For authenticating using MS-CHAPv1 via RADIUS you have to put the challenge + * and the response. The response has this structure: + * struct rad_mschapvalue { + * u_char ident; + * u_char flags; + * u_char lm_response[24]; + * u_char response[24]; + * }; + * + * @return void + */ + function putAuthAttributes() + { + if (isset($this->username)) { + $this->putAttribute(RADIUS_USER_NAME, $this->username); + } + if (isset($this->response) || isset($this->lmResponse)) { + $lmResp = isset($this->lmResponse) ? $this->lmResponse : str_repeat ("\0", 24); + $ntResp = isset($this->response) ? $this->response : str_repeat ("\0", 24); + $resp = pack('CC', $this->chapid, $this->flags) . $lmResp . $ntResp; + $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_RESPONSE, $resp); + } + if (isset($this->challenge)) { + $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_CHALLENGE, $this->challenge); + } + } +} + +/** + * class Auth_RADIUS_MSCHAPv2 + * + * Class for authenticating using MS-CHAPv2 see RFC2759 + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_MSCHAPv2 extends Auth_RADIUS_MSCHAPv1 +{ + /** + * 16 Bytes binary challenge + * @var string + */ + var $challenge = null; + + /** + * 16 Bytes binary Peer Challenge + * @var string + */ + var $peerChallenge = null; + + /** + * Put MS-CHAPv2 specific attributes + * + * For authenticating using MS-CHAPv1 via RADIUS you have to put the challenge + * and the response. The response has this structure: + * struct rad_mschapv2value { + * u_char ident; + * u_char flags; + * u_char pchallenge[16]; + * u_char reserved[8]; + * u_char response[24]; + * }; + * where pchallenge is the peer challenge. Like for MS-CHAPv1 we set the flags field to 1. + * @return void + */ + function putAuthAttributes() + { + if (isset($this->username)) { + $this->putAttribute(RADIUS_USER_NAME, $this->username); + } + if (isset($this->response) && isset($this->peerChallenge)) { + // Response: chapid, flags (1 = use NT Response), Peer challenge, reserved, Response + $resp = pack('CCa16a8a24',$this->chapid , 1, $this->peerChallenge, str_repeat("\0", 8), $this->response); + $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP2_RESPONSE, $resp); + } + if (isset($this->challenge)) { + $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_CHALLENGE, $this->challenge); + } + } + + /** + * Frees resources. + * + * Calling this method is always a good idea, because all security relevant + * attributes are filled with Nullbytes to leave nothing in the mem. + * + * @access public + */ + function close() + { + Auth_RADIUS_MSCHAPv1::close(); + $this->peerChallenge = str_repeat("\0", strlen($this->peerChallenge)); + } +} + +/** + * class Auth_RADIUS_Acct + * + * Class for RADIUS accounting + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_Acct extends Auth_RADIUS +{ + /** + * Defines where the Authentication was made, possible values are: + * RADIUS_AUTH_RADIUS, RADIUS_AUTH_LOCAL, RADIUS_AUTH_REMOTE + * @var integer + */ + var $authentic = null; + + /** + * Defines the type of the accounting request, on of: + * RADIUS_START, RADIUS_STOP, RADIUS_ACCOUNTING_ON, RADIUS_ACCOUNTING_OFF + * @var integer + */ + var $status_type = null; + + /** + * The time the user was logged in in seconds + * @var integer + */ + var $session_time = null; + + /** + * A uniq identifier for the session of the user, maybe the PHP-Session-Id + * @var string + */ + var $session_id = null; + + /** + * Constructor + * + * Generates a predefined session_id. We use the Remote-Address, the PID, and the Current user. + * @return void + */ + function Auth_RADIUS_Acct() + { + $this->Auth_RADIUS(); + + if (isset($_SERVER)) { + $var = &$_SERVER; + } else { + $var = &$GLOBALS['HTTP_SERVER_VARS']; + } + + $this->session_id = sprintf("%s:%d-%s", isset($var['REMOTE_ADDR']) ? $var['REMOTE_ADDR'] : '127.0.0.1' , getmypid(), get_current_user()); + } + + /** + * Creates a RADIUS resource + * + * Creates a RADIUS resource for accounting. This should be the first + * call before you make any other things with the library. + * + * @return bool true on success, false on error + */ + function open() + { + $this->res = radius_acct_open(); + if (!$this->res) { + return false; + } + return true; + } + + /** + * Creates an accounting request + * + * Creates an accounting request. + * You MUST call this method before you can put any attribute. + * + * @return bool true on success, false on error + */ + function createRequest() + { + if (!radius_create_request($this->res, RADIUS_ACCOUNTING_REQUEST)) { + return false; + } + return true; + } + + /** + * Put attributes for accounting. + * + * Here we put some accounting values. There many more attributes for accounting, + * but for web-applications only certain attributes make sense. + * @return void + */ + function putAuthAttributes() + { + $this->putAttribute(RADIUS_ACCT_SESSION_ID, $this->session_id); + $this->putAttribute(RADIUS_ACCT_STATUS_TYPE, $this->status_type); + if (isset($this->session_time) && $this->status_type == RADIUS_STOP) { + $this->putAttribute(RADIUS_ACCT_SESSION_TIME, $this->session_time); + } + if (isset($this->authentic)) { + $this->putAttribute(RADIUS_ACCT_AUTHENTIC, $this->authentic); + } + + } + +} + +/** + * class Auth_RADIUS_Acct_Start + * + * Class for RADIUS accounting. Its usualy used, after the user has logged in. + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_Acct_Start extends Auth_RADIUS_Acct +{ + /** + * Defines the type of the accounting request. + * It is set to RADIUS_START by default in this class. + * @var integer + */ + var $status_type = RADIUS_START; +} + +/** + * class Auth_RADIUS_Acct_Start + * + * Class for RADIUS accounting. Its usualy used, after the user has logged out. + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_Acct_Stop extends Auth_RADIUS_Acct +{ + /** + * Defines the type of the accounting request. + * It is set to RADIUS_STOP by default in this class. + * @var integer + */ + var $status_type = RADIUS_STOP; +} + +if (!defined('RADIUS_UPDATE')) + define('RADIUS_UPDATE', 3); + +/** + * class Auth_RADIUS_Acct_Update + * + * Class for interim RADIUS accounting updates. + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_Acct_Update extends Auth_RADIUS_Acct +{ + /** + * Defines the type of the accounting request. + * It is set to RADIUS_UPDATE by default in this class. + * @var integer + */ + var $status_type = RADIUS_UPDATE; +} + +?> diff --git a/lib/pear/Crypt/CHAP.php b/lib/pear/Crypt/CHAP.php new file mode 100644 index 0000000000..db33e987b1 --- /dev/null +++ b/lib/pear/Crypt/CHAP.php @@ -0,0 +1,464 @@ + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This code cannot simply be copied and put under the GNU Public License or +any other GPL-like (LGPL, GPL2) License. + + $Id$ +*/ + +require_once 'PEAR.php'; + +/** +* Classes for generating packets for various CHAP Protocols: +* CHAP-MD5: RFC1994 +* MS-CHAPv1: RFC2433 +* MS-CHAPv2: RFC2759 +* +* @package Crypt_CHAP +* @author Michael Bretterklieber +* @access public +* @version $Revision$ +*/ + +/** + * class Crypt_CHAP + * + * Abstract base class for CHAP + * + * @package Crypt_CHAP + */ +class Crypt_CHAP extends PEAR +{ + /** + * Random binary challenge + * @var string + */ + var $challenge = null; + + /** + * Binary response + * @var string + */ + var $response = null; + + /** + * User password + * @var string + */ + var $password = null; + + /** + * Id of the authentication request. Should incremented after every request. + * @var integer + */ + var $chapid = 1; + + /** + * Constructor + * + * Generates a random challenge + * @return void + */ + function Crypt_CHAP() + { + $this->PEAR(); + $this->generateChallenge(); + } + + /** + * Generates a random binary challenge + * + * @param string $varname Name of the property + * @param integer $size Size of the challenge in Bytes + * @return void + */ + function generateChallenge($varname = 'challenge', $size = 8) + { + $this->$varname = ''; + mt_srand(hexdec(substr(md5(microtime()), -8)) & 0x7fffffff); + for ($i = 0; $i < $size; $i++) { + $this->$varname .= pack('C', 1 + mt_rand() % 255); + } + return $this->$varname; + } + + /** + * Generates the response. Overwrite this. + * + * @return void + */ + function challengeResponse() + { + } + +} + +/** + * class Crypt_CHAP_MD5 + * + * Generate CHAP-MD5 Packets + * + * @package Crypt_CHAP + */ +class Crypt_CHAP_MD5 extends Crypt_CHAP +{ + + /** + * Generates the response. + * + * CHAP-MD5 uses MD5-Hash for generating the response. The Hash consists + * of the chapid, the plaintext password and the challenge. + * + * @return string + */ + function challengeResponse() + { + return pack('H*', md5(pack('C', $this->chapid) . $this->password . $this->challenge)); + } +} + +/** + * class Crypt_CHAP_MSv1 + * + * Generate MS-CHAPv1 Packets. MS-CHAP doesen't use the plaintext password, it uses the + * NT-HASH wich is stored in the SAM-Database or in the smbpasswd, if you are using samba. + * The NT-HASH is MD4(str2unicode(plaintextpass)). + * You need the mhash extension for this class. + * + * @package Crypt_CHAP + */ +class Crypt_CHAP_MSv1 extends Crypt_CHAP +{ + /** + * Wether using deprecated LM-Responses or not. + * 0 = use LM-Response, 1 = use NT-Response + * @var bool + */ + var $flags = 1; + + /** + * Constructor + * + * Loads the mhash extension + * @return void + */ + function Crypt_CHAP_MSv1() + { + $this->Crypt_CHAP(); + $this->loadExtension('mhash'); + } + + /** + * Generates the NT-HASH from the given plaintext password. + * + * @access public + * @return string + */ + function ntPasswordHash($password = null) + { + if (isset($password)) { + return mhash(MHASH_MD4, $this->str2unicode($password)); + } else { + return mhash(MHASH_MD4, $this->str2unicode($this->password)); + } + } + + /** + * Converts ascii to unicode. + * + * @access public + * @return string + */ + function str2unicode($str) + { + $uni = ''; + $str = (string) $str; + for ($i = 0; $i < strlen($str); $i++) { + $a = ord($str{$i}) << 8; + $uni .= sprintf("%X", $a); + } + return pack('H*', $uni); + } + + /** + * Generates the NT-Response. + * + * @access public + * @return string + */ + function challengeResponse() + { + return $this->_challengeResponse(); + } + + /** + * Generates the NT-Response. + * + * @access public + * @return string + */ + function ntChallengeResponse() + { + return $this->_challengeResponse(false); + } + + /** + * Generates the LAN-Manager-Response. + * + * @access public + * @return string + */ + function lmChallengeResponse() + { + return $this->_challengeResponse(true); + } + + /** + * Generates the response. + * + * Generates the response using DES. + * + * @param bool $lm wether generating LAN-Manager-Response + * @access private + * @return string + */ + function _challengeResponse($lm = false) + { + if ($lm) { + $hash = $this->lmPasswordHash(); + } else { + $hash = $this->ntPasswordHash(); + } + + while (strlen($hash) < 21) { + $hash .= "\0"; + } + + $td = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, ''); + $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND); + $key = $this->_desAddParity(substr($hash, 0, 7)); + mcrypt_generic_init($td, $key, $iv); + $resp1 = mcrypt_generic($td, $this->challenge); + mcrypt_generic_deinit($td); + + $key = $this->_desAddParity(substr($hash, 7, 7)); + mcrypt_generic_init($td, $key, $iv); + $resp2 = mcrypt_generic($td, $this->challenge); + mcrypt_generic_deinit($td); + + $key = $this->_desAddParity(substr($hash, 14, 7)); + mcrypt_generic_init($td, $key, $iv); + $resp3 = mcrypt_generic($td, $this->challenge); + mcrypt_generic_deinit($td); + mcrypt_module_close($td); + + return $resp1 . $resp2 . $resp3; + } + + /** + * Generates the LAN-Manager-HASH from the given plaintext password. + * + * @access public + * @return string + */ + function lmPasswordHash($password = null) + { + $plain = isset($password) ? $password : $this->password; + + $plain = substr(strtoupper($plain), 0, 14); + while (strlen($plain) < 14) { + $plain .= "\0"; + } + + return $this->_desHash(substr($plain, 0, 7)) . $this->_desHash(substr($plain, 7, 7)); + } + + /** + * Generates an irreversible HASH. + * + * @access private + * @return string + */ + function _desHash($plain) + { + $key = $this->_desAddParity($plain); + $td = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, ''); + $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND); + mcrypt_generic_init($td, $key, $iv); + $hash = mcrypt_generic($td, 'KGS!@#$%'); + mcrypt_generic_deinit($td); + mcrypt_module_close($td); + return $hash; + } + + /** + * Adds the parity bit to the given DES key. + * + * @access private + * @param string $key 7-Bytes Key without parity + * @return string + */ + function _desAddParity($key) + { + static $odd_parity = array( + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, + 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, + 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, + 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, + 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, + 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, + 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, + 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, + 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, + 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254); + + $bin = ''; + for ($i = 0; $i < strlen($key); $i++) { + $bin .= sprintf('%08s', decbin(ord($key{$i}))); + } + + $str1 = explode('-', substr(chunk_split($bin, 7, '-'), 0, -1)); + $x = ''; + foreach($str1 as $s) { + $x .= sprintf('%02s', dechex($odd_parity[bindec($s . '0')])); + } + + return pack('H*', $x); + + } + + /** + * Generates the response-packet. + * + * @param bool $lm wether including LAN-Manager-Response + * @access private + * @return string + */ + function response($lm = false) + { + $ntresp = $this->ntChallengeResponse(); + if ($lm) { + $lmresp = $this->lmChallengeResponse(); + } else { + $lmresp = str_repeat ("\0", 24); + } + + // Response: LM Response, NT Response, flags (0 = use LM Response, 1 = use NT Response) + return $lmresp . $ntresp . pack('C', !$lm); + } +} + +/** + * class Crypt_CHAP_MSv2 + * + * Generate MS-CHAPv2 Packets. This version of MS-CHAP uses a 16 Bytes authenticator + * challenge and a 16 Bytes peer Challenge. LAN-Manager responses no longer exists + * in this version. The challenge is already a SHA1 challenge hash of both challenges + * and of the username. + * + * @package Crypt_CHAP + */ +class Crypt_CHAP_MSv2 extends Crypt_CHAP_MSv1 +{ + /** + * The username + * @var string + */ + var $username = null; + + /** + * The 16 Bytes random binary peer challenge + * @var string + */ + var $peerChallenge = null; + + /** + * The 16 Bytes random binary authenticator challenge + * @var string + */ + var $authChallenge = null; + + /** + * Constructor + * + * Generates the 16 Bytes peer and authentication challenge + * @return void + */ + function Crypt_CHAP_MSv2() + { + $this->Crypt_CHAP_MSv1(); + $this->generateChallenge('peerChallenge', 16); + $this->generateChallenge('authChallenge', 16); + } + + /** + * Generates a hash from the NT-HASH. + * + * @access public + * @param string $nthash The NT-HASH + * @return string + */ + function ntPasswordHashHash($nthash) + { + return mhash(MHASH_MD4, $nthash); + } + + /** + * Generates the challenge hash from the peer and the authenticator challenge and + * the username. SHA1 is used for this, but only the first 8 Bytes are used. + * + * @access public + * @return string + */ + function challengeHash() + { + return substr(mhash(MHASH_SHA1, $this->peerChallenge . $this->authChallenge . $this->username), 0, 8); + } + + /** + * Generates the response. + * + * @access public + * @return string + */ + function challengeResponse() + { + $this->challenge = $this->challengeHash(); + return $this->_challengeResponse(); + } +} + + +?> diff --git a/lib/pear/README.txt b/lib/pear/README.txt index 3b1f4a477c..dcf82d65b2 100644 --- a/lib/pear/README.txt +++ b/lib/pear/README.txt @@ -65,6 +65,17 @@ In detail, the libraries added here are: - by Elizabeth Smith, Arpad Ray, Joshua Eichorn, David Coallier and Laurent Yaish - License: LGPL - http://pear.php.net/package/HTML_AJAX/ +- PEAR Auth_RADIUS: + - Current version: 1.0.6 (2008-04-13) + - by Michael Bretterklieber + - License: BSD + - http://pear.php.net/package/Auth_RADIUS +- PEAR Crypt_CHAP: + - Current Version: 1.0.1 (2007-03-14) + - by Michael Bretterklieber + - License: BSD + - http://pear.php.net/package/Crypt_CHAP + ---------------------------------------------------------------- -- 2.39.5