NTLM / NTLMv2
Server / Proxy
challenge / response
solution

      czech version / česká verze

16.2.2003 NTLMv2 library was released
     Testovací aplikaci si můžete stáhnout test application ZDE (klikni).
     Tato knihovna podporuje NTLMv1 a NTLMv2 challenge/response schéma.
     Pro bližší informace o NTLM knihovně a jejím užití se podívejte níže.
     Knihovna je zkompilována jako dynamická (ntlmv2.dll) pro nahrátí
     z jiných aplikací než VC++ (jako například Delphi, VisualBasic, Oracle atd.)
     nebo jako statická (ntlmv2lib.lib) pro přímé zalinkovýní do vašeho Visual Studio C++ projektu.
     Kontakt geniuz@geniuz.cz
     Následuje hlavička knihovny NTLMv2 s přehlednými instrukcemi užití:

// Structures
struct _auth_ctxt_struct
{
      BOOL success;
      BYTE context[0x40];
};

typedef enum {
      NAM_AUTO = 0,
      NAM_NTLMv1 = 0x40000,
      NAM_NTLMv2 = 0x80000
} NTLM_AUTH_METHOD;

// Functions description:
// - allocate "ntlm_out" variable as 2000 characters length buffer
// char ntlm_out[2000];
// - allocate _auth_ctxt as _auth_ctxt_struct structure
// _auth_ctxt_struct _auth_ctxt
// - call NTLMv2ClientStep1 function before sending request to proxy or server
// - if you need suppress NTLMv2 and you want use NTLMv1 solution use NAM_NTLMv1 constant as last input parameter

void _stdcall NTLMv2ClientStep1(
      _auth_ctxt_struct* _auth_ctxt, //in
      const char* user, //in
      const char* domain, //in
      const char* password, //in
      char* ntlm_out, //out (2000 bytes)
      NTLM_AUTH_METHOD ntlm_auth_mehotd = NAM_AUTO
);

// check _auth_ctxt.success BOOL value for all is o.k.

// if you connect over proxy:
// - add "Proxy-Authorization: NTLM %ntlm_out%\r\n" to header
// - add "Proxy-Connection: Keep-Alive\r\n" to header
// else
// if you connect directly to server
// - add "Authorization: NTLM %ntlm_out%\r\n" to header
// - add "Connection: Keep-Alive\r\n" to header
// end if

// - send request to proxy or server and receive data from proxy or server
// if you connect over proxy:
// - get "ntlm_in" variable as "Proxy-Authenticate: NTLM %ntlm_in%"\r\n" from returned header
// else
// if you connect directly to server
// - get "ntlm_in" variable as "WWW-Authenticate: NTLM %ntlm_in%"\r\n" from returned header
// end if

// - call NTLMv2ClientStep2 function with obtained Authenticate value as ntlm_in

void _stdcall NTLMv2ClientStep2(
      _auth_ctxt_struct* _auth_ctxt, //in
      const char* ntlm_in, //in
      char* ntlm_out //out (2000 bytes)
);

// check _auth_ctxt.success BOOL value for all is o.k.

// - add "Host: %url%\r\n" to header
// if you connect over proxy:
// - add "Proxy-Authorization: NTLM %ntlm_out%\r\n" to header
// - add "Proxy-Connection: Keep-Alive\r\n" to header
// else
// if you connect directly to server
// - add "Authorization: NTLM %ntlm_out%\r\n" to header
// - add "Connection: Keep-Alive\r\n" to header
// end if
// - send request to server/proxy and receive data from server/proxy


// - call NTLMv2Destroy if you are leaving your application

BOOL _stdcall NTLMv2Destroy();

//future functions support, you can build server side NTLM application with this
//void _stdcall NTLMv2ServerStep1
//void _stdcall NTLMv2ServerStep2





12.1.2002 NTLM library was released

      Pokud ve vašich aplikacích využíváte spojení na základě socketů (v MFC CSocket a CAsyncSocket třídy) a pokud se do cesty vaší aplikaci postaví Microsoft Proxy Server se zapnutou Windows NT Challenge/Response autentizací, vaše aplikace se pravděpodobně nebude schopna autorizovat, jelikož NTLM autorizace není od společnosti Microsoft dokumentována.

      V této situaci můžete využít knihovnu ntlm.dll, která tuto službu provádí. Knihovna exportuje dvě funkce:

      int AuthOne( char* domain, char* host, char* reply );
      int AuthTwo( const char* authcode, char* domain, char* host, char* user, char* passwd, char* reply );


      Autorizace je tedy rozdělena do dvou kroků. Nejlépe vše vysvětlíme na příkladu. Chtějme tedy načíst obsah internetové adresy http://www.geniuz.cz. V prvním kroku zavoláme funkci AuthOne s parametrem domény, ve které máme účet, v příkladu "GENIUZDOMAIN" a s parametrem host kam se chceme dostat, tedy "www.geniuz.cz". V reply vrácený NTLM kód vložíme jako řádek hlavičky požadavku ve tvaru: Proxy-Authorization: NTLM reply. Celá první hlavička tedy bude vypadat například následovně:

      GET http://www.geniuz.cz:80/ HTTP/1.0
      Accept: * / *
      Host: www.geniuz.cz:80
      Proxy-Connection: keep-alive
      Proxy-Authorization: NTLM TlRMTVNTUAABAAAAB7IAAAsACwAtAAAA...
      ______
prázdný řádek


      Ve vrácené hlavičce najdeme řádek podobný tomuto:

      Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAFAAUADAAAAA...


      Vrácenému NTLM autentikačnímu kódu říkejme authcode. V tom případě zavoláme druhou exportovanou funkci AuthTwo s parametrem authcode (příklad - "TlRMTVNTUAACAAAAFAAUADAAAAA..."), doménou "GENIUZDOMAIN", hostem v našem případě "www.geniuz.cz", uživatelem v příkladu "jankral" a heslem v příkladu "jan148#ca". V paremtru reply funkce AuthTwo vrátí druhý NTLM kód, který vložíme do hlavičky a poté již proxy vrátí skutečně požadovanou stránku.

      Knihovna je zkompilována pro OS Windows 32 s a nebo bez užití MFC. Rekompilace pod jiné platformy než Windows je možná. Získání zdrojového kódu je též možné. Pro informaci, jak si knihovnu objednat, e-mailujte na adresu

Cena knihovny je 9.000 Kč
Cena zdrojového kódu je 49.000 Kč
geniuz@geniuz.cz


      Zde si můžete stáhnout testovací program.

      Pro lepší orientaci a představu uvádím většinu zdrojového kódu příkladu využívajícího knihovnu ntlm.dll:

     


//*** exportované funkce z ntlm.dll
int AuthOne( char* domain, char* host, char* reply );
int AuthTwo( const char* authcode, char* domain, char* host, char* user, char* passwd, char* reply );

#define MAX_BUFFER_LENGTH 64000

//*** zjískání požadovaného řádku z hlavičky
CString GetHeaderStr(CString parsedName, CString header, CString parser = "\r\n", CString spacer = ": ")
{
	int index = -1;
	parsedName += spacer;
	int len = parsedName.GetLength();
	int parslen = parser.GetLength();

	do		index = header.Find( parsedName, index + 1 );
	while	(( index > 0 ? header.Mid(index-parslen,parslen) != parser : false ));

	if( index >= 0 )
	{
		index += len;
		int last = header.Find(parser, index);
		if( last < 0 ) return -1;
		return header.Mid(index, last - index );
	}
	else
		return "";
}

//*** maska standartní základní hlavičky
char header_mask[] =
		"GET http://%s:%d/ HTTP/1.0\r\n"
		"Accept: * /*\r\n"
		"Host: %s:%d\r\n"
		"Proxy-Connection: keep-alive\r\n"
		"Proxy-Authorization: NTLM %s\r\n"
		"\r\n";

//*** poslání a načtení dat ze socketu
bool SendAndReceive( CSocket& socket, char* proxy, int port, CString& header, char* buffer )
{
	if( header.GetLength() != socket.Send( header, header.GetLength() ) )
	{
		AfxMessageBox( "Send error" );
		return false;
	}
	if( 0 > socket.Receive( buffer, MAX_BUFFER_LENGTH ) )
	{
		AfxMessageBox( "Receive error" );
		return false;
	}
	return true;
}

//*** ukázka NTLM autentikace
void Ntlm()
{
	CString header;
	//*** smyšlená autentikační data
	char domain[] = "GENIUZDOMAIN";
	char user[] = "jankral";
	char passwd[] = "jan148#ca";
	char host[] = "www.geniuz.cz";
	char hostport = 80;
	char proxy[] = "PROXY";
	char proxyport = 80;
	char buffer[ MAX_BUFFER_LENGTH ] = "\x00";
	char ntlm[256];

	//*** vytvoření socketu
	CSocket socket;
	if( !socket.Create() )
	{ AfxMessageBox( "Create error" ); return; }
	if( !socket.Connect( proxy, proxyport ) )
	{ AfxMessageBox( "Connect error" );	return; }

	//*** první autorizace z ntlm.dll
	AuthOne( domain, host, ntlm );
	//*** zformátování hlacičky
	header.Format( header_mask, host, hostport, host, hostport, ntlm );
	//*** odeslání hlavičky a získání odpovědi
	if( !SendAndReceive(socket, proxy, proxyport, header, buffer) ) return;

	//*** zjískání autentikačního kódu z vrácené hlavičky
	CString response = GetHeaderStr( "Proxy-Authenticate", buffer );

	//*** druhá autorizace z ntlm.dll
	AuthTwo( response.Mid(5), domain, host, user, passwd, ntlm );
	//*** zformátování hlacičky
	header.Format( header_mask, host, hostport, host, hostport, ntlm );
	//*** odeslání hlavičky a získání odpovědi
	if( !SendAndReceive(socket, proxy, proxyport, header, buffer) ) return;

	socket.Close();

	//*** buffer obsahuje skutečnou stánku po předchozí NTLM autorizaci
	AfxMessageBox( buffer );
}