17

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

Maxime Mettey
  • 208
  • 1
  • 2
  • 13
  • 1
    Are you accessing the script using a browser? The reason `ord($chained64[13])` value is 130 is because the headers of the request the client sent to the script are missing these two flags: `Negotiate Domain Supplied (0x00001000)` and `Negotiate Workstation Supplied (0x00002000)`. – blubear May 11 '15 at 08:03
  • @blubear Yes I am accessing the script using Internet Explorer 8. Am I missing something to send these two headers in the request ? – Maxime Mettey May 11 '15 at 08:57
  • Are you logged in to the machine as a domain user or machine user? Did you get the prompt from IE to enter your domain username and password? – blubear May 12 '15 at 04:09
  • @blubear I'm logged as a domain user on the machine. But I never get a prompt from IE.When I try with Firefox, I get a prompt and then I get the login that I wrote in the prompt, but this is not single sign-on, and I have to get it to work with IE. – Maxime Mettey May 12 '15 at 06:34
  • As you are running a Windows server is there any reason you can't use IIS instead of Apache? IIS has built-in Windows Integrated Authentication that is trivially easy to enable. – timclutton May 15 '15 at 08:55
  • And, now that I think about it, your entire PHP authentication script is unnecessary. `mod_authnz_sspi` transparently handles the authentication and fills the `REMOTE_USER` variable. By using the script you posted users are authenticating _twice_. See [this answer](http://stackoverflow.com/a/20996238/3775731) for details. – timclutton May 15 '15 at 08:58
  • @timclutton Unfortunately I can not use IIS, my customer is a big company and they have some rules for hosting, machines configuration... The application is running since many years and it would be to heavy to change for IIS now... For the REMOTE_USER variable, I don't have it (neither REMOTE_USER, nor PHP_AUTH_USER). – Maxime Mettey May 15 '15 at 09:11
  • Understood - it's difficult to argue with customer requirements :) If you don't get the `REMOTE_USER` variable then the module must not be working correctly. There are errors in your `httpd.conf`. I'll write a full answer that details my similar setup - hopefully this can help. – timclutton May 15 '15 at 09:16
  • Thank you for your time :) I just added two parts in http.conf : `LoadModule authnz_sspi_module modules/mod_authnz_sspi.so` after the loading of the other modules, and the tag that I wrote in my question edit. – Maxime Mettey May 15 '15 at 09:22

2 Answers2

4

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

Community
  • 1
  • 1
ThaDafinser
  • 469
  • 3
  • 13
  • Thank you for your help, it is now working with Firefox without prompt, I get the session informations (login, host, domain). But I can't make it work with IE, I set Intranet security at the lowest and nothing changed. I tried to set all security at the lowest to test and the result is the same... – Maxime Mettey May 12 '15 at 12:18
  • You need to set "Automatic logon with current user name and password". Check the new link in my post – ThaDafinser May 12 '15 at 12:45
  • I already set this parameter in IE, but it is not working. IE never asks the credentials. I checked your link and I went in Local Policies / Security Options to set "Network Security: LAN Manager authentication level" to "Send LM & NTLM responses", but nothing changed, – Maxime Mettey May 12 '15 at 13:18
  • When I use the prompt of the IE by forcing it to ask credentials, IE does not return the credentials, contrary to Firefox. Maybe I omitted another IE config ? – Maxime Mettey May 12 '15 at 13:25
  • If the seamless authentification (no popup) works in Firefox, your server (Apache/PHP) configuration is correct. It must be a setting in IE, if your website is really detected as an intranet site and the intranet settings is low enough, there shouldnt be problems – ThaDafinser May 18 '15 at 09:06
  • Yes I really do not get why this does not work in IE. In IE Security config, my local Intranet security is set to Low. In the `Sites` section, `Automatically detect intranet network` is checked and in `Advanced` section, I have my URL in the websites list (I added `*.mycompany.com` and `https://subdomain.mycompany.com`) and `Require server verification (https:) for all sites in this zone` is unchecked. When I am on the page, I can see in the IE status bar `Local intranet | protected mode : Off`. – Maxime Mettey May 18 '15 at 09:21
2

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 Apache 403 - Forbidden error.

I suspect the problem is with your httpd.conf since you are using the Apache 2.2 allow/deny syntax and this won't work in Apache 2.4 (unless you have the mod_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:

<Directory "/path/to/webroot">
    AllowOverride     All
    Options           ExecCGI
    # since I run PHP via mod_fcgi; should also work as 'Options none'.

    AuthName          "SSPI Authentication"
    AuthType          SSPI
    SSPIAuth          On
    SSPIAuthoritative On
    SSPIOmitDomain    On
    Require           valid-user
    Require           user "NT AUTHORITY\ANONYMOUS LOGON" denied
</Directory>
timclutton
  • 11,269
  • 3
  • 26
  • 40
  • I changed the config and restarted Apache, but I did not get the REMOTE_USER. The only things that changed in $_SERVER are : `[HTTP_CACHE_CONTROL] => no-cache` became `[HTTP_CACHE_CONTROL] => max-age=0` - `[HTTP_PRAGMA]` disappeared - `[REMOTE_PORT]` changed. – Maxime Mettey May 15 '15 at 09:51
  • To ensure that `E:\_PATH_` is the exact folder, I copied it and pasted it in the windows explorer URL, and it showed me my files so the path is good. – Maxime Mettey May 15 '15 at 09:54
  • I just tried your exact config (except path to the files and options) but I do not get the REMOTE_USER. I think I have to use the PHP script, it works with it on Firefox, but I can not find why it does not work with IE... – Maxime Mettey May 15 '15 at 11:46
  • @MaximeMettey Check that you don't have the line `Require all granted` anywhere as this will bypass the `SSPI` requirement. Also confirm that your apache server is running as a service under the `SYSTEM` account (the module readme says this is required). – timclutton May 15 '15 at 14:05
  • I had 10 lines of `Require all granted` in my httpd.conf, but they were set on other folders than my application. I commented them but nothing changed. My Apache service was running as a user of the domain, I changed it for the Local System account, but nothing changed. – Maxime Mettey May 15 '15 at 14:25
  • You could try adding `Require all denied` as the first line of the `Directory` block to ensure that inherited rights aren't causing this. – timclutton May 15 '15 at 14:26
  • Added the line, still the same result :(. – Maxime Mettey May 15 '15 at 14:39
  • Hmmm. It seems like the module isn't working at all. Maybe try to force an error to see if the config is being read. Change `Require valid-user` to `Require valid-use`. This should stop Apache from starting... – timclutton May 15 '15 at 14:44
  • The config is being read, Apache can not start if I write 'valid-use'. – Maxime Mettey May 15 '15 at 14:54
  • For information, I tried the mod_authnz_sspi for x64 versions but Apache can not start with it. – Maxime Mettey May 15 '15 at 14:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/77915/discussion-between-timclutton-and-maxime-mettey). – timclutton May 15 '15 at 15:40
  • Not sure if you received chat notification ? – Maxime Mettey May 19 '15 at 09:32
  • @MaximeMettey I'm in chat now. – timclutton May 19 '15 at 09:48