Accessing NTLM secured resources with PHP

Sometimes you need to do strange things – and then PHP is the language of choice if you need a solution for web applications in really special environments like using a Windows based authentication method on a Linux server. Here at Mayflower GmbH / ThinkPHP we wrote a proxy server for the authentication with NTLM (NT LAN Manager). This piece of software is used by one of our costumers for two years now without any bigger problems. It does silently its job for hundreds of users each day and they never know what’s happening in the background.

NTLM is an authentication protocol which is widely used in Microsoft based network environments. By using it with HTTP you can use that method for single-sign-on (SSO) authentication within your web browser. NTLM is supported by Microsoft Internet Explorer and Mozilla based browsers like Firefox or Seamonkey. There’s also a module of the Apache webserver called mod_ntlm which can be used for the authentication in Windows based networks.

How NTLM works

NTLM is a so called challenge-response method between a client (which wants to be authenticated) and a server (which needs an authentication by the client). The handshake is done by three messages, the type 1 (negotiation), the type 2 (challenge) and the type 3 (authentication). Every message begins with the hexadecimal encoded string „NTLMSSP“. The process is working like the following steps:

  1. The client sends a GET request to the server or proxy, the server answers with a 401 header and calls for a NTLM authentication.
  2. Now the client sends the type 1 message with the required information (Windows user name, name of the domain, the password hash, the size of the key for the encryption, and the request of the shared authentication) to the server. The server then answers with the type 2 message and a 401 header. Additionally the server also sends the same information and permits the client to authenticate with these parameters. Secluding the server also sends a random hash string to the string.
  3. In the third message the client uses this random hash string received from the server and the Windows authorization information of the user for the evaluation of the answer. This method is based on MD4/MD5 hasing algorithms and a DES encryption. Now the client sends the answer to the server. The server checks the password against the „Security Account Manager“ (SAM) database of the domain controller. If everything is okay, the client is authenicated.

You can describe the authentication like that:

    1: C  --> S	GET ...
    
    2: C <--  S	401 Unauthorized
			WWW-Authenticate: NTLM
    
    3: C  --> S  	GET ...
                  Authorization: NTLM <base64-encoded type-1-message>
    
    4: C <--  S   401 Unauthorized
                  WWW-Authenticate: NTLM <base64-encoded type-2-message>
    
    5: C  --> S   GET ...
                  Authorization: NTLM <base64-encoded type-3-message>
    
    6: C <--  S   200 Ok

If you’re using a Windows 2000 Server or a Windows Server 2003 instead of a Windows NT Server, the Internet Information Server will send also a WWW-Authenticate: Negotiate. Then the method will use the Kerberos authentiction over Active Directory instead of the authentication with a domain controller.

Now a short example with the encoded messages during the NTLM HTTP authentication:


    1: C  --> S   GET /index.html HTTP/1.1
    
    2: C <--  S   HTTP/1.1 401 Unauthorized
    			WWW-Authenticate: NTLM
    			Connection: close
    
    3: C  --> S   GET /index.html HTTP/1.1
    			Authorization: NTLM TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==    

    4: C <--  S   HTTP/1.1 401 Unauthorized
    			WWW-Authenticate: NTLM TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARA
BPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZAB
vAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAb
wBtAAAAAAA=
    
    5: C  --> S   GET /index.html HTTP/1.1
    			Authorization: NTLM TlRMTVNTUAADAAAAGAAYAGoAAAAYABgAggAAAAwADABAAAAACAAIAEwAAAAWABYAV
AAAAAAAAACaAAAAAQIAAEQATwBNAEEASQBOAHUAcwBlAHIAVwBPAFIASwBTAFQAQQB
UAEkATwBOAMM3zVy9RPyXgqZnr21CfG3mfCDC0+d8ViWpjBwx6BhHRmspst9GgPOZWPuM
ITqcxg==
    
    6: C <--  S   HTTP/1.1 200 OK

How we implemented it

One of the problems during the implementation was a little browser comunication detail. If the web application sends multiple requests to the server and then the connection breaks after the next requests, the browser needs feedback from the server before getting a new 401 header. If no feedback is send back, the browser closes the authenticated connection to the server.

To avoid this problem the NTLMProxy script sends a simple XHTML page to the browser which will be reloaded for every new server request. In the meantime the $_GET, $_POST, $_REQUEST and $_SERVER variables have to be saved and have to make available for the browser after the reload. So the NTLMProxy administrates the connections to the server and the client at the same time.

During the NTLM handshake the proxy sends multiple 401 headers (Access denied) to the browser and the page will be reloaded. During the requests the proxy have to change his state and have to recognize which steps in the handshake must be done next. Because of that the NTLMProxy have to registered in the PHP session. The successful termination of the handshake can be discovered when the proxy delivers a string.

The initialization of the NTLMProxy is very simple because we only need a valid session:

session_start();
if (!isset($_SESSION['proxy'])) {
	$_SESSION['proxy'] = new NTLMProxy();
}
$_SESSION['proxy']->handleHandshake();

You can get the secret content with just a few lines of code:

$content = $_SESSION['proxy']->get('192.168.0.1', 'www.example.com', '/');
if (is_string($content)) {
	print htmlspecialchars($content);
} elseif (false === $content) {
	die("Error!");
}

But how we’re using this in the real life? We’re using the NTLMProxy since two years now at a customer where it requests secret informations from a NTLM secured XML interface. Currently several hundreds users with various permissions can access to several millions of datasets with this appalication. It have to be guaranteed that only an authorized user can read and write data for which he has enough rights. The problem is that the PHP web application is running on a LAMP server and the secret data is provided from an IIS webserver.

Problems

There are some minor problems that we recognized in the production. The Internet Explorer 6 on WindowsXP with Service Pack 1 sporadically breaks the connection and the authentication fails. This problem is solved with SP2, but we also found a funny workaround. If the 401 header is bigger than 1460 bytes the issue is solved with SP1. 1460 bytes is the size of one TCP/IP package….

You also need a domain controller which can handle more traffic than a certain DC because the NTLMProxy needs for every request a new authentication from the domain controller. Normally a single user needs the authentication from the domain controller only at the morning beginning with work.

PEARified

I rewrote the whole class the last months and submitted it to PEAR. The package was accepted last week and now I’m preparing the first release of it. The PEAR::NTLMProxy package also uses the PEAR error handling and the PEAR::Log package for logging all stages to the console.

Für neue Blogupdates anmelden:


13 Gedanken zu “Accessing NTLM secured resources with PHP

  1. Hi I found your blog post very interesting. Particularly, the „problems“ section.

    I ran in the same problems in an environnement where I try to single-sign -on a Windows 2000/IE6 client to an Ubuntu LTS6/PHP server box.

    I used an adapted version of this script :
    http://ng.secusquad.com/wiki/index.php/PHP_NTLM_GET_LOGIN

    IE6 is answering a very strange „Server Not Found“.

    Using Ethereal, I managed to discover that IE was closing the connexion after sending the 5th packet, without waiting HTTP1.1 200 OK from the server.

    I managed to simulate the dialog with a telnet session from the W2K client, to check it was an IE6 bug.

    So, I’ve not tried your workaround yet. What is the trick ? You initialize a big cookie just to fill the headers ?

    By the way, I couldn’t find your sources for NTLMProxy …
    http://sourceforge.net/project/showfiles.php?group_id=66818


    Laurent

    • Hi,

      the trick is just to send a 401 page which has a size more than 1460 bytes. The source code will be available shortly on PEAR.

      Regards,
      Thorsten

  2. I have problems getting this to run. Where are the following functions defined?

    saveRequestVariables();
    createServerSocket();

    Thanks,

    Carsten

    • I’m trying it to get it running too. Is the file for the NTLMProxy class on pear incomplete?

      I can’t find the createServerSocket method anywhere.

      This class will help me a lot once I get it working….

      Thanks Thorsten!

  3. Hi,
    I’m facing the same problem.
    Call to undefined method NTLMProxy::createServerSocket()
    Do I have to create my own class using socket_create function ?

    Txs in advance
    Ettore Labardi

  4. I have a couple of question to you, according to your class NTLMProxy.

    First of all it looks strange, that this package could not be found via package search at pear.php.net. Single way to find it is to follow upper link.

    Second – the example, provided in comments is wrong. Considering, that this package has no any other docs – it is not possible to run this NTLM Proxy…

    Third is errors in code: class doesn’t have method createServerSocket, but only _createNewSocket, that sounds similar to.

    Besides, _createNewSocket method has missed parameter ($ip) in _createSocket call.

    So, I ask you to help! Could you provide some support helping to write NTLM auth, using your class?

    Thanx forward

  5. I have the same problem:
    Call to undefined method NTLMProxy::createServerSocket()
    I seem to have everything else starting to work.

    Thanks,
    Mark Felton

  6. In my opinion this code should never become a pear package. few class members and methods are simply not defined. the workflow is arguable and in fact, the code doesnt work at all.

    i.e. the hostname as member is called in the code as $this->host….wtf? and this becomes a pear package…well: u need good, efficient code? write yourself

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.