NTLM Authentication - Get Windows login, domain and host in PHP

Maxime Mettey picture Maxime Mettey · May 6, 2015 · Viewed 15.9k times · Source

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

Answer

ThaDafinser picture ThaDafinser · May 12, 2015

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.

You can remove the prompt, by changing the Firefox settings:

  • Type: "about:config" in the addressbar
  • Check for network.automatic-ntlm-auth.trusted-uris
  • Set the value to your domain, or part of the domain e.g mycompany.com (seperate with comma multiple values)

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