version = 1;
// $auth = $nh->run();
// if ($auth) {
// print "You are authenticated
";
// } else {
// print "You are not authenticated
";
// }
// highlight_string(print_r($nh->auth,true));
class ntlm_handshake {
public $targetname = 'testwebsite';
public $domain = 'testdomain';
public $computer = 'mycomputer';
public $dnsdomain = 'testdomain.local';
public $dnscomputer = 'mycomputer.local';
public $workstation = '';
public $version = 1;
public $v2_only = true;
public $headers = array();
public $auth_header = null;
public $msg = '';
public $msg2 = '';
public $fail_msg = '
Authentication Required
';
public $auth = array();
public $clientblob = '';
public $clientblobhash = '';
public $ntlm_hosts = array(); //plages d'adresses IP pour lesquelles une authentification NTLM est possible
public $http_proxies = array(); //proxies http
public $ntlm_check=true; //vérification NTLM ?
public $ntlm_check_ip = false; //vérification de l'adresse IP
public $log = false;
public function __construct () {
}
public function run() {
$this->ntlm_prompt();
return $this->auth['authenticated'];
}
//définition d'un log.
public function set_log($log=false, $log_file='', $log_format='text', $log_now=false, $log_reset=true) {
$this->log = $log;
if ($this->log) {
log::$log_file=$log_file;
log::$log_format=$log_format;
log::$log_now=$log_now;
if ($log_reset) log::reset();
}
}
public function set_ntlm_hosts($ntlm_hosts=array(), $http_proxies=array()) {
$this->ntlm_hosts = $ntlm_hosts;
$this->http_proxies = $http_proxies;
$this->ntlm_check_ip = true;
}
public function check_ip() {
if ($this->ntlm_check_ip) {
$remote_addr = $_SERVER['REMOTE_ADDR'];
if (in_array($remote_addr,$this->http_proxies)) {
$remote_addr = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
$this->ntlm_check = false;
foreach($this->ntlm_hosts as $ntlm_host) {
if(stripos($remote_addr,$ntlm_host)===0) {
$this->ntlm_check = true;
break;
}
}
}
}
public function ntlm_prompt() {
$this->check_ip();
if (!$this->ntlm_check) return;
$this->auth_header = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : null;
if ($this->log) {
if ($this->auth_header == null) {
log::print_message('HTTP_AUTHORIZATION non défini.');
} else {
log::print_message('HTTP_AUTHORIZATION = ');
log::print_message($this->auth_header);
}
}
if ($this->auth_header == null && function_exists('getallheaders')) {
$this->headers = getallheaders();
$this->auth_header = isset($this->headers['Authorization']) ? $this->headers['Authorization'] : null;
}
if ($this->log) {
if ($this->auth_header == null) {
log::print_message('Apache headers non définis.');
} else {
log::print_message('Apache headers = ');
log::print_message($this->headers);
log::print_message('auth_header = ');
log::print_message($this->auth_header);
log::print_message(bin2hex(base64_decode($this->auth_header)));
}
}
if (isset($_SESSION['_ntlm_auth'])) {
if ($this->log) {
log::print_message('_ntlm_auth = ');
log::print_message($_SESSION['_ntlm_auth']);
}
$this->auth = $_SESSION['_ntlm_auth'];
//return ;
}
if (!$this->auth_header) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: NTLM');
print $this->fail_msg;
if ($this->log) {
log::print_message("Pas de headers.");
log::print_message($this->fail_msg);
log::print_message("Envoi header 'HTTP/1.1 401 Unauthorized'");
log::print_message("Envoi header 'WWW-Authenticate: NTLM'");
}
exit;
}
if (substr($this->auth_header,0,5) == 'NTLM ') {
$this->msg = base64_decode(substr($this->auth_header, 5));
if (substr($this->msg, 0, 8) != "NTLMSSP\x00") {
if ($this->log) {
log::print_message("Header NTLM non reconnus.");
}
die();
}
if (substr($this->msg, 8, 1) == "\x01") {
if ($this->version==1) {
$this->msg2 = "NTLMSSP\x00\x02\x00\x00\x00\x00\x00\x00";
$this->msg2.= "\x00\x28\x00\x00\x00\x01\x82\x00\x00";
$this->msg2.= "\x00\x02\x02\x02\x00\x00\x00\x00\x00";
$this->msg2.= "\x00\x00\x00\x00\x00\x00\x00";
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: NTLM '.trim(base64_encode($this->msg2)));
if ($this->log) {
log::print_message("Envoi header 'HTTP/1.1 401 Unauthorized'");
log::print_message("Envoi header 'WWW-Authenticate: NTLM'");
log::print_message(bin2hex($this->msg2));
}
} else if ($this->version==2) {
$_SESSION['_ntlm_server_challenge'] = $this->ntlm_get_random_bytes(8);
$this->msg2 = $this->ntlm_get_challenge_msg($_SESSION['_ntlm_server_challenge']);
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: NTLM '.trim(base64_encode($this->msg2)));
if ($this->log) {
log::print_message("Envoi header 'HTTP/1.1 401 Unauthorized'");
log::print_message("Envoi header 'WWW-Authenticate: NTLM'");
log::print_message("Envoi challenge NTLM");
log::print_message(bin2hex($this->msg2));
}
}
exit;
} else if (substr($this->msg, 8, 1) == "\x03") {
if ($this->version==1) {
$this->auth = $this->ntlm_parse_response_msg();
} else if ($this->version==2) {
$this->auth = $this->ntlm_parse_response_msg($_SESSION['_ntlm_server_challenge']);
unset($_SESSION['_ntlm_server_challenge']);
if (!$this->auth['authenticated']) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: NTLM');
print $this->fail_msg;
print $this->auth['error'];
if ($this->log) {
log::print_message("Envoi header 'HTTP/1.1 401 Unauthorized'");
log::print_message("Envoi header 'WWW-Authenticate: NTLM'");
log::print_message($this->fail_msg);
log::print_message($this->auth['error']);
}
exit;
}
}
$_SESSION['_ntlm_auth'] = $this->auth;
return ;
}
} else {
if($this->log) {
log::print_message("Pas de header NTLM.");
}
}
}
public function ntlm_utf8_to_utf16le($str) {
return iconv('UTF-8', 'UTF-16LE', $str);
}
public function ntlm_md4($s) {
if (function_exists('mhash')) {
return mhash(MHASH_MD4, $s);
}
return pack('H*', hash('md4', $s));
}
public function ntlm_av_pair($type, $utf16) {
return pack('v', $type).pack('v', strlen($utf16)).$utf16;
}
public function ntlm_field_value($start, $decode_utf16 = true) {
$len = (ord(substr($this->msg, $start+1, 1)) * 256) + ord(substr($this->msg, $start, 1));
$off = (ord(substr($this->msg, $start+5, 1)) * 256) + ord(substr($this->msg, $start+4, 1));
$result = substr($this->msg, $off, $len);
if ($decode_utf16) {
$result = iconv('UTF-16LE', 'UTF-8', $result);
}
return $result;
}
public function ntlm_hmac_md5($key) {
$blocksize = 64;
if (strlen($key) > $blocksize) {
$key = pack('H*', md5($key));
}
$key = str_pad($key, $blocksize, "\0");
$ipadk = $key ^ str_repeat("\x36", $blocksize);
$opadk = $key ^ str_repeat("\x5c", $blocksize);
return pack('H*', md5($opadk.pack('H*', md5($ipadk.$this->msg))));
}
public function ntlm_get_random_bytes($length) {
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= chr(rand(0, 255));
}
return $result;
}
public function ntlm_get_challenge_msg($challenge='') {
$this->domain = $this->ntlm_field_value(16);
$ws = $this->ntlm_field_value(24);
$tdata = $this->ntlm_av_pair(2, $this->ntlm_utf8_to_utf16le($this->domain)).$this->ntlm_av_pair(1, $this->ntlm_utf8_to_utf16le($this->computer)).$this->ntlm_av_pair(4, $this->ntlm_utf8_to_utf16le($this->dnsdomain)).$this->ntlm_av_pair(3, $this->ntlm_utf8_to_utf16le($this->dnscomputer))."\0\0\0\0\0\0\0\0";
$tname = $this->ntlm_utf8_to_utf16le($this->targetname);
$this->msg2 = "NTLMSSP\x00\x02\x00\x00\x00".
pack('vvV', strlen($tname), strlen($tname), 48). // target name len/alloc/offset
"\x01\x02\x81\x00". // flags
$challenge. // challenge
"\x00\x00\x00\x00\x00\x00\x00\x00". // context
pack('vvV', strlen($tdata), strlen($tdata), 48 + strlen($tname)). // target info len/alloc/offset
$tname.$tdata;
return $this->msg2;
}
public function ntlm_verify_hash($challenge) {
// $md4hash = $this->get_ntlm_user_hash($this->user);
// if (!$md4hash) {
// return false;
// }
// $ntlmv2hash = ntlm_hmac_md5($md4hash, ntlm_utf8_to_utf16le(strtoupper($this->user).$this->domain));
// $blobhash = ntlm_hmac_md5($ntlmv2hash, $challenge.$this->clientblob);
//
// echo
// 'domain = '.$this->domain."\r\n".
// 'user = '.$this->user."\r\n".
// 'challenge = '.bin2hex($challenge )."\r\n".
// 'clientblob = '.bin2hex($this->clientblob )."\r\n".
// 'clientblobhash = '.bin2hex($this->clientblobhash )."\r\n".
// 'md4hash = '.bin2hex($md4hash )."\r\n".
// 'ntlmv2hash = '.bin2hex($ntlmv2hash)."\r\n".
// 'blobhash = '.bin2hex($blobhash)."\r\n";
//
// return ($blobhash == $this->clientblobhash);
//return ntlm_md4(ntlm_utf8_to_utf16le('test'));
return true;
}
public function ntlm_parse_response_msg($challenge='') {
if ($this->version==1) {
$this->user = $this->ntlm_field_value(36);
$this->domain = $this->ntlm_field_value(28);
$this->workstation = $this->ntlm_field_value(44);
} else if ($this->version==2) {
$this->user = $this->ntlm_field_value(36);
$this->domain = $this->ntlm_field_value(28);
$this->workstation = $this->ntlm_field_value(44);
$ntlmresponse = $this->ntlm_field_value(20, false);
//$blob = "\x01\x01\x00\x00\x00\x00\x00\x00".$timestamp.$nonce."\x00\x00\x00\x00".$tdata;
$this->clientblob = substr($ntlmresponse, 16);
$this->clientblobhash = substr($ntlmresponse, 0, 16);
if (substr($this->clientblob, 0, 8) != "\x01\x01\x00\x00\x00\x00\x00\x00") {
//return array('authenticated' => true, 'username' => $this->user, 'domain' => $this->domain, 'workstation' => $this->workstation);
return array('authenticated' => false, 'error' => 'NTLMv2 response required. Please force your client to use NTLMv2.');
}
if (!$this->ntlm_verify_hash($challenge)) {
return array('authenticated' => false, 'error' => 'Incorrect username or password.', 'username' => $this->user, 'domain' => $this->domain, 'workstation' => $this->workstation);
}
}
return array('authenticated' => true, 'username' => $this->user, 'domain' => $this->domain, 'workstation' => $this->workstation);
}
public function ntlm_unset_auth() {
unset ($_SESSION['_ntlm_auth']);
}
public function get_ntlm_user_hash() {
//$userdb = array('loune'=>'test', 'user1'=>'password');
//if (!isset($userdb[strtolower($this->user)]))
//return false;
//return ntlm_md4(ntlm_utf8_to_utf16le('test'));
return true;
}
}