/** * Returns true if the username and password work and false if they are * wrong or don't exist. * * @param string $username The username * @param string $password The password * @return bool Authentication success or failure. */ 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 <br/>"); // printf("Password: $password <br/>"); // printf("host: $this->config->host <br/>"); // printf("nasport: $this->config->nasport <br/>"); // printf("secret: $this->config->secret <br/>"); // 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<br/>\n", $rauth->getError()); exit; } $result = $rauth->send(); if ($rauth->isError($result)) { printf("Radius send failed: %s<br/>\n", $result->getMessage()); exit; } else { if ($result === true) { // printf("Radius Auth succeeded<br/>\n"); return true; } else { // printf("Radius Auth rejected<br/>\n"); return false; } } // get attributes, even if auth failed if (!$rauth->getAttributes()) { printf("Radius getAttributes: %s<br/>\n", $rauth->getError()); } else { $rauth->dumpAttributes(); } $rauth->close(); }
printf("ChallResp : %s\nexpected : d39bfaf5d6855a948c8c81a85947502c\n", bin2hex($crpt->challengeResponse())); echo "\n"; echo "MS-CHAPv1 str2unicode\n"; $crpt = new Crypt_CHAP_MSv1(); printf("Passed 123 as Number:%s\n", bin2hex($crpt->str2unicode(123))); printf("Passed 123 as String:%s\n", bin2hex($crpt->str2unicode('123'))); echo "MS-CHAPv1 TEST\n"; $crpt->password = '******'; $crpt->challenge = pack('H*', '102DB5DF085D3041'); $unipw = $crpt->str2unicode($pass); printf("Unicode PW: %s\nexpected : 4d00790050007700\n", bin2hex($unipw)); printf("NT HASH : %s\nexpected : fc156af7edcd6c0edde3337d427f4eac\n", bin2hex($crpt->ntPasswordHash())); printf("NT Resp : %s\nexpected : 4e9d3c8f9cfd385d5bf4d3246791956ca4c351ab409a3d61\n", bin2hex($crpt->challengeResponse())); printf("LM HASH : %s\nexpected : 75ba30198e6d1975aad3b435b51404ee\n", bin2hex($crpt->lmPasswordHash())); printf("LM Resp : %s\nexpected : 91881d0152ab0c33c524135ec24a95ee64e23cdc2d33347d\n", bin2hex($crpt->lmChallengeResponse())); //printf ("Response : %s\nexpected : unknown\n", bin2hex($crpt->response())); echo "\n"; echo "MS-CHAPv2 TEST\n"; $crpt = new Crypt_CHAP_MSv2(); $crpt->username = '******'; $crpt->password = '******'; printf("Username : %s\nexpected : 55736572\n", bin2hex($crpt->username)); $crpt->authChallenge = pack('H*', '5b5d7c7d7b3f2f3e3c2c602132262628'); $crpt->peerChallenge = pack('H*', '21402324255E262A28295F2B3A337C7E'); $nthash = $crpt->ntPasswordHash(); printf("NT HASH : %s\nexpected : 44ebba8d5312b8d611474411f56989ae\n", bin2hex($nthash)); $nthashhash = $crpt->ntPasswordHashHash($nthash); printf("NT HASH-HASH : %s\nexpected : 41c00c584bd2d91c4017a2a12fa59f3f\n", bin2hex($nthashhash)); printf("ChallResp : %s\nexpected : 82309ecd8d708b5ea08faa3981cd83544233114a3d85d6df\n", bin2hex($crpt->challengeResponse())); printf("Challenge : %s\nexpected : d02e4386bce91226\n", bin2hex($crpt->challenge)); echo "\n";
/** * Check username and password against RADIUS authentication backend. * * @param string $username User name to check * @param string $password User password to check * @return int Authentication success (0 = fail, 1 = success) FIXME bool */ function radius_authenticate($username, $password) { global $config, $rad; radius_init(); if ($username && $rad) { //print_vars(radius_server_secret($rad)); radius_create_request($rad, RADIUS_ACCESS_REQUEST); radius_put_attr($rad, RADIUS_USER_NAME, $username); switch (strtolower($config['auth_radius_method'])) { // CHAP-MD5 see RFC1994 case 'chap': case 'chap_md5': $chapid = 1; // Specify a CHAP identifier //$challenge = mt_rand(); // Generate a challenge //$cresponse = md5(pack('Ca*', $chapid, $password.$challenge), TRUE); new Crypt_CHAP(); // Pre load class $crpt = new Crypt_CHAP_MD5(); $crpt->password = $password; $challenge = $crpt->challenge; $resp_md5 = $crpt->challengeResponse(); $resp = pack('C', $chapid) . $resp_md5; radius_put_attr($rad, RADIUS_CHAP_PASSWORD, $resp); // Add the Chap-Password attribute radius_put_attr($rad, RADIUS_CHAP_CHALLENGE, $challenge); // Add the Chap-Challenge attribute. break; // MS-CHAPv1 see RFC2433 // MS-CHAPv1 see RFC2433 case 'mschapv1': $chapid = 1; // Specify a CHAP identifier $flags = 1; // 0 = use LM-Response, 1 = use NT-Response (we not use old LM) new Crypt_CHAP(); // Pre load class $crpt = new Crypt_CHAP_MSv1(); $crpt->password = $password; $challenge = $crpt->challenge; $resp_lm = str_repeat("", 24); $resp_nt = $crpt->challengeResponse(); $resp = pack('CC', $chapid, $flags) . $resp_lm . $resp_nt; radius_put_vendor_attr($rad, RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_RESPONSE, $resp); radius_put_vendor_attr($rad, RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_CHALLENGE, $challenge); break; // MS-CHAPv2 see RFC2759 // MS-CHAPv2 see RFC2759 case 'mschapv2': $chapid = 1; // Specify a CHAP identifier $flags = 1; // 0 = use LM-Response, 1 = use NT-Response (we not use old LM) new Crypt_CHAP(); // Pre load class $crpt = new Crypt_CHAP_MSv2(); $crpt->username = $username; $crpt->password = $password; $challenge = $crpt->authChallenge; $challenge_p = $crpt->peerChallenge; $resp_nt = $crpt->challengeResponse(); // Response: chapid, flags (1 = use NT Response), Peer challenge, reserved, Response $resp = pack('CCa16a8a24', $chapid, $flags, $challenge_p, str_repeat("", 8), $resp_nt); radius_put_vendor_attr($rad, RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP2_RESPONSE, $resp); radius_put_vendor_attr($rad, RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_CHALLENGE, $challenge); break; // PAP (Plaintext) // PAP (Plaintext) default: radius_put_attr($rad, RADIUS_USER_PASSWORD, $password); } // Puts standard attributes $radius_ip = get_ip_version($config['auth_radius_nas_address']) ? $config['auth_radius_nas_address'] : $_SERVER['SERVER_ADDR']; if (get_ip_version($radius_ip) == 6) { // FIXME, not sure that this work correctly radius_put_attr($rad, RADIUS_NAS_IPV6_ADDRESS, $radius_ip); } else { radius_put_addr($rad, RADIUS_NAS_IP_ADDRESS, $radius_ip); } $radius_id = empty($config['auth_radius_id']) ? get_localhost() : $config['auth_radius_id']; radius_put_attr($rad, RADIUS_NAS_IDENTIFIER, $radius_id); //radius_put_attr($rad, RADIUS_NAS_PORT_TYPE, RADIUS_VIRTUAL); //radius_put_attr($rad, RADIUS_SERVICE_TYPE, RADIUS_FRAMED); //radius_put_attr($rad, RADIUS_FRAMED_PROTOCOL, RADIUS_PPP); radius_put_attr($rad, RADIUS_CALLING_STATION_ID, isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1'); $response = radius_send_request($rad); //print_vars($response); switch ($response) { case RADIUS_ACCESS_ACCEPT: // An Access-Accept response to an Access-Request indicating that the RADIUS server authenticated the user successfully. //echo 'Authentication successful'; return 1; break; case RADIUS_ACCESS_REJECT: // An Access-Reject response to an Access-Request indicating that the RADIUS server could not authenticate the user. //echo 'Authentication failed'; break; case RADIUS_ACCESS_CHALLENGE: // An Access-Challenge response to an Access-Request indicating that the RADIUS server requires further information // in another Access-Request before authenticating the user. //echo 'Challenge required'; break; default: print_error('A RADIUS error has occurred: ' . radius_strerror($rad)); } } //session_logout(); return 0; }
/** * Attempts to login a user against the authentication source. * * If successfull, returns a User object * * @param string $username A valid identifying token for the source. * Not necessarily unique. * @param string $password Clear text password. * @param string $errmsg Reference of error message * * @return object The actual User object if login was successfull, * false otherwise. */ public function login($username, $password, &$errmsg = null) { $db = AbstractDb::getObject(); User::setCurrentUser(null); //This should fix a security hole if using an empty username. I didn't have time to audit the radius code to see if it really was vulnerable, and code a better fix. // Init values $retval = false; $username = $db->escapeString($username); $password = $db->escapeString($password); if (Dependency::check("Auth_RADIUS", $errmsg)) { /* * Supported encryption methods are : * * CHAP_MD5 :Challenge-Handshake Authentication Protocol with MD5 * MSCHAPv1 and MSCHAPv2: Microsoft's CHAP implementation */ switch ($this->mRadius_encryption_method) { case "PAP": case "CHAP_MD5": case "MSCHAPv1": case "MSCHAPv2": // Instanciate PEAR class $classname = 'Auth_RADIUS_' . $this->mRadius_encryption_method; $radius_server = new $classname($username, $password); $radius_server->addServer($this->mRadius_hostname, $this->mRadius_auth_port, $this->mRadius_secret_key); break; default: // Invalid encryption method $errmsg = _("Invalid RADIUS encryption method."); return false; } // Instructing PEAR RADIUS class auth parameters $radius_server->username = $username; // Depending on the auth method, generate challenge response switch ($this->mRadius_encryption_method) { case 'CHAP_MD5': case 'MSCHAPv1': $classname = $this->mRadius_encryption_method == 'MSCHAPv1' ? 'Crypt_CHAP_MSv1' : 'Crypt_CHAP_MD5'; $crypt_class = new $classname(); $crypt_class->password = $password; $radius_server->challenge = $crypt_class->challenge; $radius_server->chapid = $crypt_class->chapid; $radius_server->response = $crypt_class->challengeResponse(); $radius_server->flags = 1; break; case 'MSCHAPv2': $crypt_class = new Crypt_CHAP_MSv2(); $crypt_class->username = $username; $crypt_class->password = $password; $radius_server->challenge = $crypt_class->authChallenge; $radius_server->peerChallenge = $crypt_class->peerChallenge; $radius_server->chapid = $crypt_class->chapid; $radius_server->response = $crypt_class->challengeResponse(); break; default: $radius_server->password = $password; break; } if (!$radius_server->start()) { $errmsg = _("Could not initiate PEAR RADIUS Auth class : " . $radius_server->getError()); return false; } // Send the authentication request to the RADIUS server $result = $radius_server->send(); if (PEAR::isError($result)) { $errmsg = _("Failed to send authentication request to the RADIUS server. : " . $result->getMessage()); return false; } else { if ($result === true) { // RADIUS authentication succeeded! // Now checking for local copy of this user $user_info = null; $sql = "SELECT user_id, pass FROM users WHERE (username='******') AND account_origin='" . $this->getNetwork()->getId() . "'"; $db->execSqlUniqueRes($sql, $user_info, false); if ($user_info != null) { $user = User::getObject($user_info['user_id']); if ($user->isUserValid($errmsg)) { $retval =& $user; User::setCurrentUser($user); $errmsg = _("Login successfull"); } else { $retval = false; //Reason for refusal is already in $errmsg } } else { /* * This user has been succcessfully authenticated through * remote RADIUS, but it's not yet in our local database. * Creating the user with a Global Unique ID, empty email * and password. * Local database password hashing is based on an empty * string (we do not store remote passwords). */ $user = User::createUser(get_guid(), $username, $this->getNetwork(), "", ""); $retval =& $user; // Validate the user right away ! $user->setAccountStatus(ACCOUNT_STATUS_ALLOWED); User::setCurrentUser($user); $errmsg = _("Login successfull"); } return $retval; } else { $errmsg = _("The RADIUS server rejected this username/password combination."); return false; } } $radius_server->close(); } else { return false; } }
public function accessRequestEapMsChapV2($username, $password) { /* * RADIUS EAP MSCHAPv2 Process: * > RADIUS ACCESS_REQUEST w/ EAP identity packet * < ACCESS_CHALLENGE w/ MSCHAP challenge encapsulated in EAP request * CHAP packet contains auth_challenge value * Calculate encrypted password based on challenge for response * > ACCESS_REQUEST w/ MSCHAP challenge response, peer_challenge & * encrypted password encapsulated in an EAP response packet * < ACCESS_CHALLENGE w/ MSCHAP success or failure in EAP packet. * > ACCESS_REQUEST w/ EAP success packet if challenge was accepted * */ $attributes = $this->getAttributesToSend(); $this->clearDataToSend()->clearError()->setPacketType(self::TYPE_ACCESS_REQUEST); $this->attributesToSend = $attributes; $eapPacket = EAPPacket::identity($username); $this->setUsername($username)->setAttribute(79, $eapPacket)->setIncludeMessageAuthenticator(); $this->accessRequest(); if ($this->errorCode) { return false; } elseif ($this->radiusPacketReceived != self::TYPE_ACCESS_CHALLENGE) { $this->errorCode = 102; $this->errorMessage = 'Access-Request did not get Access-Challenge response'; return false; } $state = $this->getReceivedAttribute(24); $eap = $this->getReceivedAttribute(79); if ($eap == null) { $this->errorCode = 102; $this->errorMessage = 'EAP packet missing from MSCHAP v2 access response'; return false; } $eap = EAPPacket::fromString($eap); if ($eap->type != EAPPacket::TYPE_EAP_MS_AUTH) { $this->errorCode = 102; $this->errorMessage = 'EAP type is not EAP_MS_AUTH in access response'; return false; } $chapPacket = MsChapV2Packet::fromString($eap->data); if (!$chapPacket || $chapPacket->opcode != MsChapV2Packet::OPCODE_CHALLENGE) { $this->errorCode = 102; $this->errorMessage = 'MSCHAP v2 access response packet missing challenge'; return false; } $challenge = $chapPacket->challenge; $chapId = $chapPacket->msChapId; $msChapV2 = new \Crypt_CHAP_MSv2(); $msChapV2->username = $username; $msChapV2->password = $password; $msChapV2->chapid = $chapPacket->msChapId; $msChapV2->authChallenge = $challenge; $response = $msChapV2->challengeResponse(); $chapPacket->opcode = MsChapV2Packet::OPCODE_RESPONSE; $chapPacket->response = $response; $chapPacket->name = $username; $chapPacket->challenge = $msChapV2->peerChallenge; $eapPacket = EAPPacket::mschapv2($chapPacket, $chapId); $this->clearDataToSend()->setPacketType(self::TYPE_ACCESS_REQUEST)->setUsername($username)->setAttribute(79, $eapPacket)->setIncludeMessageAuthenticator(); $resp = $this->accessRequest(null, null, 0, $state); if ($this->errorCode) { return false; } $eap = $this->getReceivedAttribute(79); if ($eap == null) { $this->errorCode = 102; $this->errorMessage = 'EAP packet missing from MSCHAP v2 challenge response'; return false; } $eap = EAPPacket::fromString($eap); if ($eap->type != EAPPacket::TYPE_EAP_MS_AUTH) { $this->errorCode = 102; $this->errorMessage = 'EAP type is not EAP_MS_AUTH in access response'; return false; } $chapPacket = MsChapV2Packet::fromString($eap->data); if ($chapPacket->opcode != MsChapV2Packet::OPCODE_SUCCESS) { $this->errorCode = 3; $err = !empty($chapPacket->response) ? $chapPacket->response : 'General authentication failure'; if (preg_match('/E=(\\d+)/', $chapPacket->response, $err)) { switch ($err[1]) { case '691': $err = 'Authentication failure, username or password incorrect.'; break; case '646': $err = 'Authentication failure, restricted logon hours.'; break; case '647': $err = 'Account disabled'; break; case '648': $err = 'Password expired'; break; case '649': $err = 'No dial in permission'; break; } } $this->errorMessage = $err; return false; } // got a success response - send success acknowledgement $state = $this->getReceivedAttribute(24); $chapPacket = new MsChapV2Packet(); $chapPacket->opcode = MsChapV2Packet::OPCODE_SUCCESS; $eapPacket = EAPPacket::mschapv2($chapPacket, $chapId + 1); $this->clearDataToSend()->setPacketType(self::TYPE_ACCESS_REQUEST)->setUsername($username)->setAttribute(79, $eapPacket)->setIncludeMessageAuthenticator(); $resp = $this->accessRequest(null, null, 0, $state); if ($resp !== true) { return false; } else { return true; } }