I am working on a Single Sign-On (SSO) PHP application.
Users log in their Windows session, and they want to be automatically logged in the application with their Windows account (connected with LDAP Active Directory).
I tried this script :
<?php
$headers = apache_request_headers(); // Récupération des l'entêtes client
if (@$_SERVER['HTTP_VIA'] != NULL){ // nous verifions si un proxy est utilisé : parceque l'identification par ntlm ne peut pas passer par un proxy
echo "Proxy bypass!";
} elseif(!isset($headers['Authorization'])) { //si l'entete autorisation est inexistante
header( "HTTP/1.0 401 Unauthorized" ); //envoi au client le mode d'identification
header( "WWW-Authenticate: NTLM" ); //dans notre cas le NTLM
exit; //on quitte
}
if(isset($headers['Authorization'])) //dans le cas d'une authorisation (identification)
{
if(substr($headers['Authorization'],0,5) == 'NTLM '){ // on vérifit que le client soit en NTLM
$chaine=$headers['Authorization'];
$chaine=substr($chaine, 5); // recuperation du base64-encoded type1 message
$chained64=base64_decode($chaine); // decodage base64 dans $chained64
if(ord($chained64{8}) == 1){
// |_ byte signifiant l'etape du processus d'identification (etape 3)
// verification du drapeau NTLM "0xb2" à l'offset 13 dans le message type-1-message (comp ie 5.5+) :
if (ord($chained64[13]) != 178){
echo "NTLM Flag error!";
exit;
}
$retAuth = "NTLMSSP".chr(000).chr(002).chr(000).chr(000).chr(000).chr(000).chr(000).chr(000);
$retAuth .= chr(000).chr(040).chr(000).chr(000).chr(000).chr(001).chr(130).chr(000).chr(000);
$retAuth .= chr(000).chr(002).chr(002).chr(002).chr(000).chr(000).chr(000).chr(000).chr(000);
$retAuth .= chr(000).chr(000).chr(000).chr(000).chr(000).chr(000).chr(000);
$retAuth64 =base64_encode($retAuth); // encode en base64
$retAuth64 = trim($retAuth64); // enleve les espaces de debut et de fin
header( "HTTP/1.0 401 Unauthorized" ); // envoi le nouveau header
header( "WWW-Authenticate: NTLM $retAuth64" ); // avec l'identification supplémentaire
exit;
} else if(ord($chained64{8}) == 3) {
// |_ byte signifiant l'etape du processus d'identification (etape 5)
// on recupere le domaine
$lenght_domain = (ord($chained64[31])*256 + ord($chained64[30])); // longueur du domain
$offset_domain = (ord($chained64[33])*256 + ord($chained64[32])); // position du domain.
$domain = str_replace("\0","",substr($chained64, $offset_domain, $lenght_domain)); // decoupage du du domain
//le login
$lenght_login = (ord($chained64[39])*256 + ord($chained64[38])); // longueur du login.
$offset_login = (ord($chained64[41])*256 + ord($chained64[40])); // position du login.
$login = str_replace("\0","",substr($chained64, $offset_login, $lenght_login)); // decoupage du login
$lenght_host = (ord($chained64[47])*256 + ord($chained64[46]));
$offset_host = (ord($chained64[49])*256 + ord($chained64[48]));
$host = str_replace("\0","",substr($chained64, $offset_host, $lenght_host));
if ( $login != NULL){
echo $login;
} else {
echo "NT Login empty!";
}
}
}
}
?>
This script is working on this configuration :
- Windows server 2003
- Apache 2.2 with module mod_auth_sspi
But now I need to implement this on this configuration and it does not work :
- Windows server 2008
- Apache 2.4.6 with module mod_authnz_sspi
I keep getting "NTLM Flag error!", because of this condition :
if (ord($chained64[13]) != 178){
echo "NTLM Flag error!";
exit;
}
I tried :
if (ord($chained64[13]) != 130){
because ord($chained64[13]) returns 130, but I can not go in this condition :
} else if(ord($chained64{8}) == 3) {
$lenght_domain = (ord($chained64[31])*256 + ord($chained64[30])); // longueur du domain
$offset_domain = (ord($chained64[33])*256 + ord($chained64[32])); // position du domain.
$domain = str_replace("\0","",substr($chained64, $offset_domain, $lenght_domain)); // decoupage du du domain
//le login
$lenght_login = (ord($chained64[39])*256 + ord($chained64[38])); // longueur du login.
$offset_login = (ord($chained64[41])*256 + ord($chained64[40])); // position du login.
$login = str_replace("\0","",substr($chained64, $offset_login, $lenght_login)); // decoupage du login
$lenght_host = (ord($chained64[47])*256 + ord($chained64[46]));
$offset_host = (ord($chained64[49])*256 + ord($chained64[48]));
$host = str_replace("\0","",substr($chained64, $offset_host, $lenght_host));
if ( $login != NULL){
echo $login;
} else {
echo "NT Login empty!";
}
}
Because ord($chained64{8})
always returns 1.
Edit 2015-05-11 :
I tried executing the 'whoami' command in php, like this :
echo exec('whoami');
-> when I execute this command in cmd.exe, I get the current logged user, but when I execute it in PHP, I get nt_authority/system.I supposed that when PHP executes the 'whoami' command, Windows checks the login of Apache service. I went into Apache properties, in the 'Log On' tab, to log on as a valid user of the Active Directory. But then, when PHP executes
echo exec('whoami');
, I only get the login used for Apache, and not the current user.I am using Internet Explorer 8 to execute the PHP script.
I have this in my Apache httpd.conf (
_PATH_
is the path to my php files, maybe this is wrong ?) :<Directory "E:/_PATH_"> Options None AllowOverride All Order allow,deny Allow from all AuthName "SSPI Protected Place" AuthType SSPI SSPIAuth On SSPIAuthoritative On SSPIOfferBasic On SSPIOmitDomain On Require valid-user </Directory>
Edit 2015-05-12 :
I am logged as a domain user on the machine
When I try with Firefox, I get a prompt for a login and a password. When I post the prompt, the script gets the login from the prompt, but this is not what I want to do : I have to get this to work with IE, and I don't want to type again login and password. I want the login of the current Windows session.
In Firefox, I went into about:config to set network.automatic-ntlm-auth.trusted-uris to my domain, thanks to @ThaDafinser. Now I do not get a prompt anymore in Firefox and everything works, but I always need to make it work on IE.
In IE, I set Local Intranet Security to the lowest, but nothing changed.
In IE, "Automatic logon with current user name and password" is checked for Local Intranet & Trusted Sites.
When I force IE to ask credentials in a prompt, if I post the prompt, IE does not return the credentials, contrary to Firefox.
Edit 2015-05-13 :
I added the URL to trusted sites in IE, nothing changed.
I set security to low for trusted sites, nothing changed.
I unchecked "Use HTTP 1.1 through proxy connections" in IE > Internet Options > Advanced, I still can not have session informations on Internet Explorer, even if I use the prompt.
I added the full URL in Internet Explorer > Internet Options > Security > Local Intranet > Sites > Advanced
In Internet Explorer > Internet Options > Security > Local Intranet > Sites > Advanced, I also added the same part of the domain (mycompany.com) than I have added in Firefox to make it work, but this did not help.
Edit 2015-05-18 :
Changed my httpd.conf to be compatible with Apache 2.4, according to what @timclutton said in his answer :
<Directory "E:/_PATH_">
Require all denied
AllowOverride All
Options None
AuthName "SSPI Authentication"
AuthType SSPI
SSPIAuth On
SSPIAuthoritative On
SSPIOmitDomain On
Require valid-user
Require user "NT AUTHORITY\ANONYMOUS LOGON" denied
</Directory>
Edit 2015-05-19 :
I tried to set a basic authentication intead of SSPI and it does not work.
AuthType Basic AuthName "Authentication Required" AuthUserFile "E:/PATH/.htpasswd" Require valid-user
Order allow,deny Allow from all
You can remove the prompt, by changing the Firefox settings:
For IE you need to set the security settings for your page (intranet) lower than for the rest of the internet. Please see https://superuser.com/questions/148063/why-does-internet-explorer-keep-asking-me-for-ntlm-credentials-in-an-intranet-zo
The
mod_authnz_sspi
module handles all aspects of the authentication process transparently, meaning that there is no need for your PHP authentication script. If the module is configured correctly you should simply be able to reference$_SERVER['REMOTE_USER']
in your script. Any user that cannot be authenticated will receive a standard Apache403 - Forbidden
error.I suspect the problem is with your
httpd.conf
since you are using the Apache 2.2allow/deny
syntax and this won't work in Apache 2.4 (unless you have themod_access_compat
module enabled). You should read Upgrading to 2.4 from 2.2 in the Apache documentation.Ensure that
E:/_PATH_
is the exact folder from which your PHP script is running and every request to that path will require authentication.The following works for me on Apache 2.4: