5

I have a nginx web server, using php-fpm for script execution, and I want to get the NTLMv2 credentials of the client browsing the the server. I have a proxy server within my local network to authenticate my users. The question is, how to do I make the nginx server authenticate, or PHP get the credentials of my users using NTLMv2 and pass me back information? I would obviously need to know their username at least to make sure that the client gets the correct credentials within the system.

I'm fine with making a upstream connection to the proxy server when for example I go to /login.php, so long as it passes the information about the client back to the server about the client, for example the user name found in the Type-3 message, I could then save this information within their session and use it from that point on.


I have a Linux server running nginx, PHP and SQLite within a local area network. The computers that are connecting to this server are all Windows based using Windows Login to the network. The login uses NTLMv2 authentication and to get to websites outside of the network via a proxy that all clients must go through to make a connection to the outside web. What I want to do, is use the NTLMv2 authentication information for the login into the LAN web server. Any suggestions on how I could do this?

Mark Tomlin
  • 8,593
  • 11
  • 57
  • 72
  • 1
    Wow, doing a Google search for "nginx NTLMv2" already has this on the front page, and I only asked this question 7 minutes ago. – Mark Tomlin Apr 15 '13 at 06:26

1 Answers1

5

I think the easiest way to accomplish something like that, is to simulate a NTLMv2 authentication at the nginx server, redirect the requests to the proxy and check the answer. I can't reproduce your setup, so the code below is not tested but it should work or it should give you a bit of help.

<?php
$headers = getallheaders() //Equivalent to apache_request_headers() to get the headers of the request.

if(!isset($headers['Authorization'])) //Check Authorization Header
{
    header('HTTP/1.1 401 Unauthorized'); //Return Unauthorized Http-Header (NTLM protocol)
    header('WWW-Authenticate: NTLM'); //Authenticcation Information (NTLM protocol)
}
else
{
    if(substr($headers['Authorization'],0,4) == 'NTLM') //Check whether Authorization Header is valid
    {
        $message = base64_decode(substr($headers['Authorization'], 5)) //Get NTLM Message from Authrization header
        if(substr($message, 0, 8) == "NTLMSSP\x00") //Check whether NTLM Message is valid
        {
            if($message[8] == "\x01") //Check whether it's type-1-NTLM Message
            {
                //$message holds the base64 encoded type-1-NTLM message
                $ch = curl_init(); //Use cURL to connect to web via proxy
                curl_setopt($ch, CURLOPT_URL, "http://www.google.com");
                curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: '.$headers['Authorization']));
                curl_setopt($ch, CURLOPT_PROXY, <Your Proxy Adress>);
                curl_setopt($ch, CURLOPT_PROXYPORT, <Your Proxy Port>);
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                $result = curl_exec($ch);
                $info = curl_getinfo($ch);
                curl_close($ch);
                $header = substr($result, 0, $info['header_size']);
                $body = substr($result, $info['header_size'], $info['download_content_length']-$info['header_size']);
                $c_headers = explode("\r\n", $header);
                for($i = 0; $i < (count($c_headers) - 2); $i++)
                {
                    header($c_headers[$i]);
                    if(substr($c_headers[$i], 0, 16) == "WWW-Authenticate")
                    {
                        //Thats your type-2-message header Format: WWW-Authenticate: NTLM <base64-type-2-message>
                    }
                }
            }
            else if ($message[8] == "\x03") //Check whether it's type-3-NTLM Message
            {
                $ch = curl_init(); //Use cURL to connect to web via proxy 
                curl_setopt($ch, CURLOPT_URL, "http://www.google.com");
                curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: '.$headers['Authorization']));
                curl_setopt($ch, CURLOPT_PROXY, <Your Proxy Adress>);
                curl_setopt($ch, CURLOPT_PROXYPORT, <Your Proxy Port>);
                $result = curl_exec($ch);
                $info = curl_getinfo($ch);
                curl_close($ch);
                if($info['CURLINFO_HTTP_CODE'] == 200)
                {
                    //Authenticated
                    //$msg holds the base64 encoded type-3-NTLM message (which includes username, domain, workstation)
                }
            }
        }
    }
}?>

I used this reference of the NTLM protocol: http://davenport.sourceforge.net/ntlm.html

I hope it will help you. Feel free to comment.

Max
  • 292
  • 2
  • 7
  • Thanks for the help, I'll try out your code a little later on! – Mark Tomlin Aug 21 '13 at 01:54
  • Within the type-1 block, `$response` is never set anywhere, but it's used. So I set the `curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);` and changed every `$response` to `$result`. Although, that's not working out well so far ... – Mark Tomlin Aug 21 '13 at 06:45
  • @MarkTomlin Oh sorry my fault. Have you tried to set e_reporting to E_ALL? Maybe you can use the Method syslog() to print some debug stuff on your log like $message or $result. – Max Aug 21 '13 at 06:59
  • Currently, I'm printing this information out with echo, and it's working ok. This is what I have right now. https://gist.github.com/Dygear/6291550 The other thing, that I did not mention in my previous note was that `substr($headers['Authorization'],0,5) == 'NTLM'` will never equal 'NTLM' as it would also include the space, so I made it `,0,4)` and that worked. – Mark Tomlin Aug 21 '13 at 08:00
  • @MarkTomlin I'm glad to hear that it is working. It's hard to find easy mistakes without the ability to test the code, but I hope my answer helped properly. – Max Aug 21 '13 at 09:01
  • I'm stuck at the Type 1 message, sorry. I did not make that clear in my last message. That's currently where I am stuck. But I think I might be a little too tired to continue this right now as I've been working for 14 hours straight. – Mark Tomlin Aug 21 '13 at 10:08
  • I don't understand, why do you need to separate those cases, why don't you simply redirect with curl all requests? – Somnium Mar 27 '17 at 14:55