{ +-------------------------------------------------------------+ } { | | } { | GM-Software | } { | =========== | } { | | } { | Project: All Projects | } { | | } { | Description: NTLM authentication messages. | } { | | } { | | } { | Copyright (C) - 2018 - Gerrit Moeller. | } { | | } { | Source code distributed under MIT license. | } { | | } { | See: https://www.gm-software.de | } { | | } { +-------------------------------------------------------------+ } {$INCLUDE GMCompilerSettings.inc} unit GMNtlm; interface uses GMStrDef; const // // ToDo: // ===== // - Bequeme Routine zum Anf�gen von Daten eines TNTLMDynDataDesc // - CalcTimeStamp nochmal pr�fen // // // see: https://curl.haxx.se/rfc/ntlm.html // NTLM_Unicode_Charset = $00000001; // Indicates that Unicode strings are supported for use in security buffer data. NTLM_OEM_Charset = $00000002; // Indicates that OEM strings are supported for use in security buffer data. NTLM_Request_Target = $00000004; // Requests that the server's authentication realm be included in the Type 2 message. //$00000008 unknown This flag's usage has not been identified. NTLM_Sign = $00000010; // Specifies that authenticated communication between the client and server should carry a digital signature (message integrity). NTLM_Seal = $00000020; // Specifies that authenticated communication between the client and server should be encrypted (message confidentiality). NTLM_Datagram_Style = $00000040; // Indicates that datagram authentication is being used. NTLM_Lan_Manager_Key = $00000080; // Indicates that the Lan Manager Session Key should be used for signing and sealing authenticated communications. NTLM_Netware = $00000100; // This flag's usage has not been identified. NTLM_NTLM = $00000200; // Indicates that NTLM authentication is being used. //$00000400 unknown This flag's usage has not been identified. NTLM_Anonymous = $00000800; // Sent by the client in the Type 3 message to indicate that an anonymous context has been established. This also affects the response fields (as detailed in the "Anonymous Response" section). NTLM_Domain_Supplied = $00001000; // Sent by the client in the Type 1 message to indicate that the name of the domain in which the client workstation has membership is included in the message. This is used by the server to determine whether the client is eligible for local authentication. NTLM_Host_Supplied = $00002000; // Sent by the client in the Type 1 message to indicate that the client workstation's name is included in the message. This is used by the server to determine whether the client is eligible for local authentication. NTLM_Local_Call = $00004000; // Sent by the server to indicate that the server and client are on the same machine. Implies that the client may use the established local credentials for authentication instead of calculating a response to the challenge. NTLM_Always_Sign = $00008000; // Indicates that authenticated communication between the client and server should be signed with a "dummy" signature. NTLM_Target_Type_Domain = $00010000; // Sent by the server in the Type 2 message to indicate that the target authentication realm is a domain. NTLM_Target_Type_Server = $00020000; // Sent by the server in the Type 2 message to indicate that the target authentication realm is a server. NTLM_Target_Type_Share = $00040000; // Sent by the server in the Type 2 message to indicate that the target authentication realm is a share. Presumably, this is for share-level authentication. Usage is unclear. NTLM_NTLM2_Key = $00080000; // Indicates that the NTLM2 signing and sealing scheme should be used for protecting authenticated communications. Note that this refers to a particular session security scheme, and is not related to the use of NTLMv2 authentication. This flag can, however, have an effect on the response calculations (as detailed in the "NTLM2 Session Response" section). NTLM_Request_Init_Response = $00100000; // This flag's usage has not been identified. NTLM_Request_Accept_Response = $00200000; // This flag's usage has not been identified. NTLM_Request_Non_NT_Session_Key = $00400000;// This flag's usage has not been identified. NTLM_Target_Info = $00800000; // Sent by the server in the Type 2 message to indicate that it is including a Target Information block in the message. The Target Information block is used in the calculation of the NTLMv2 response. NTLM_Encrypt_128Bit = $20000000; // Indicates that 128-bit encryption is supported. NTLM_Key_Exchange = $40000000; // Indicates that the client will provide an encrypted master key in the "Session Key" field of the Type 3 message. NTLM_Encrypt_56Bit = $80000000; // Indicates that 56-bit encryption is supported. type // // NTLM types // PNTLMDynDataDesc = ^TNTLMDynDataDesc; TNTLMDynDataDesc = packed record LenInBytes: Word; AllocSizeInBytes: Word; Offset: LongInt; end; PNTLMClientStartMsg = ^TNTLMClientStartMsg; TNTLMClientStartMsg = packed record Protocol: array [0..7] of AnsiChar; MSgKind: LongInt; Flags: LongWord; Domain: TNTLMDynDataDesc; Host: TNTLMDynDataDesc; end; PNTLMServerChallengeMsg = ^TNTLMServerChallengeMsg; TNTLMServerChallengeMsg = packed record Protocol: array [0..7] of AnsiChar; MSgKind: LongInt; Domain: TNTLMDynDataDesc; Flags: LongWord; Nonce: array [0..7] of AnsiChar; context: array [0..1] of LongWord; TargetInfo: TNTLMDynDataDesc; end; PNTLNClientCredentialMsg = ^TNTLNClientCredentialMsg; TNTLNClientCredentialMsg = packed record Protocol: array [0..7] of AnsiChar; MSgKind: LongInt; LMPwdHash: TNTLMDynDataDesc; NTPwdHash: TNTLMDynDataDesc; Domain: TNTLMDynDataDesc; User: TNTLMDynDataDesc; Host: TNTLMDynDataDesc; SessionKey: TNTLMDynDataDesc; Flags: LongWord; end; // // Convenience types // PNTLMServerResponse = ^TNTLMServerResponse; TNTLMServerResponse = record Protocol: AnsiString; MSgKind: LongInt; Flags: LongWord; Nonce: AnsiString; Domain: TGMString; TargetInfoBlockData: AnsiString; end; TNTLMClientData = record Protocol: AnsiString; MSgKind: LongInt; Flags: LongWord; Domain: TGMString; Host: TGMString; end; TNTLMTargetInfoData = record ServerName: TGMString; DomainName: TGMString; DNSHost: TGMString; DNSDomain: TGMString; end; const cStrNTLMProtocolSignature: AnsiString = 'NTLMSSP'#0; function BuildNTLMClientStartMsg: TGMString; function DecodeNTLMClientStartMsg(const ABase64DataStr: TGMString): TNTLMClientData; function DecodeNTLMServerChallengeMsg(const ABase64DataStr: TGMString): TNTLMServerResponse; function BuildNTLMClientCredentialsMsg(const AUserName, APassword: TGMString; AServerResponse: PNTLMServerResponse): TGMString; procedure NTLMClearServerResponse(var AServerResponse: TNTLMServerResponse); function NTLMParseTargetInfoData(const ATargetInfoData: AnsiString): TNTLMTargetInfoData; implementation uses SysUtils, GMCommon, GMINetBase, GMCharCoding {$IFDEF TLS_SUPPORT},jwaWinCrypt, GMWinCrypt, GMOpenSSLApi{$ENDIF} ; const cResponseHashSize = 21; procedure ConvertDynBufferDescToLittleEndian(var ADynBufferDesc: TNTLMDynDataDesc); begin ADynBufferDesc.LenInBytes := UInt16ToLittleEndian(ADynBufferDesc.LenInBytes); ADynBufferDesc.AllocSizeInBytes := UInt16ToLittleEndian(ADynBufferDesc.AllocSizeInBytes); ADynBufferDesc.Offset := Int32ToLittleEndian(ADynBufferDesc.Offset); end; procedure ConvertDynBufferDescFromLittleEndian(var ADynBufferDesc: TNTLMDynDataDesc); begin ADynBufferDesc.LenInBytes := UInt16FromLittleEndian(ADynBufferDesc.LenInBytes); ADynBufferDesc.AllocSizeInBytes := UInt16FromLittleEndian(ADynBufferDesc.AllocSizeInBytes); ADynBufferDesc.Offset := Int32FromLittleEndian(ADynBufferDesc.Offset); end; procedure NTLMClearServerResponse(var AServerResponse: TNTLMServerResponse); begin AServerResponse.Protocol := ''; AServerResponse.MSgKind := 0; AServerResponse.Flags := 0; AServerResponse.Nonce := ''; AServerResponse.Domain := ''; AServerResponse.TargetInfoBlockData := ''; end; //procedure ClearTargetInfoData(var ATargetInfo: TNTLMTargetInfoData); //begin // ATargetInfo.ServerName := ''; // ATargetInfo.DomainName := ''; // ATargetInfo.DNSHost := ''; // ATargetInfo.DNSDomain := ''; //end; function GetNTLMDynDataAsString(var ADataDesc: TNTLMDynDataDesc; const AData: AnsiString; const AFlags: LongWord): TGMString; var targetNameA: AnsiString; targetNameW: UnicodeString; len: Integer; begin Result := ''; //if ADataDesc = nil then Exit; if (ADataDesc.LenInBytes > 0) and (ADataDesc.Offset < Length(AData)) then begin len := Min(ADataDesc.LenInBytes, Length(AData) - ADataDesc.Offset); if len > 0 then begin if AFlags and NTLM_OEM_Charset <> 0 then begin SetLength(targetNameA, len); Move(GMAddPtr(PAnsiChar(AData), ADataDesc.Offset)^, PAnsiChar(targetNameA)^, len); Result := targetNameA; end else if AFlags and NTLM_Unicode_Charset <> 0 then begin SetLength(targetNameW, len div SizeOf(WideChar)); Move(GMAddPtr(PAnsiChar(AData), ADataDesc.Offset)^, PWideChar(targetNameW)^, len); Result := targetNameW; end; end; end; end; function BuildNTLMClientStartMsg: TGMString; var bufStr: AnsiString; hostName, domainName: AnsiString; userW, domainW: TGMString; begin hostName := GMUpperCase(GMThisComputerName); GMGetUserAndDomainNames(userW, domainW); domainName := GMUpperCase(domainW); SetLength(bufStr, SizeOf(TNTLMClientStartMsg) + Length(hostName) + Length(domainName)); FillByte(PAnsiChar(bufStr)^, Length(bufStr), 0); with PNTLMClientStartMsg(PAnsiChar(bufStr))^ do begin Move(cStrNTLMProtocolSignature[1], Protocol, Length(cStrNTLMProtocolSignature)); MSgKind := Int32ToLittleEndian(1); // NTLM_Request_Target Flags := UInt32ToLittleEndian(NTLM_Unicode_Charset or NTLM_OEM_Charset or NTLM_NTLM or NTLM_Domain_Supplied or NTLM_Host_Supplied or NTLM_Always_Sign); // hostName name must be provided as AnsiString! Host.LenInBytes := Length(hostName); Host.AllocSizeInBytes := Host.LenInBytes; Host.Offset := SizeOf(TNTLMClientStartMsg); Move(PAnsiChar(hostName)^, (PAnsiChar(bufStr) + Host.Offset)^, Host.LenInBytes); // domainName name must be provided as AnsiString! Domain.LenInBytes := Length(domainName); Domain.AllocSizeInBytes := Domain.LenInBytes; Domain.Offset := Host.Offset + Host.LenInBytes; Move(PAnsiChar(domainName)^, (PAnsiChar(bufStr) + Domain.Offset)^, Domain.LenInBytes); // Convert buffer members to little endian memory layout ConvertDynBufferDescToLittleEndian(Host); ConvertDynBufferDescToLittleEndian(Domain); end; Result := GMEncodeBase64Str(bufStr); end; function DecodeNTLMClientStartMsg(const ABase64DataStr: TGMString): TNTLMClientData; var decodedData: AnsiString; clientMsg: TNTLMClientStartMsg; begin //FillByte(clientMsg, SizeOf(clientMsg), 0); clientMsg := Default(TNTLMClientStartMsg); decodedData := GMDecodeBase64Str(ABase64DataStr); Move(PAnsiChar(decodedData)^, clientMsg, Max(0, Min(SizeOf(clientMsg), Length(decodedData)))); //clientMsg := PNTLMClientStartMsg(PAnsichar(decodedData)); clientMsg.MSgKind := Int32FromLittleEndian(clientMsg.MSgKind); clientMsg.Flags := UInt32FromLittleEndian(clientMsg.Flags); ConvertDynBufferDescFromLittleEndian(clientMsg.Domain); ConvertDynBufferDescFromLittleEndian(clientMsg.Host); Result.Protocol := clientMsg.Protocol; Result.MSgKind := clientMsg.MSgKind; Result.Flags := clientMsg.Flags; Result.Host := GetNTLMDynDataAsString(clientMsg.Host, decodedData, NTLM_OEM_Charset); Result.Domain := GetNTLMDynDataAsString(clientMsg.Domain, decodedData, NTLM_OEM_Charset); end; type TTargetInfoPart = record Kind: Word; Value: UnicodeString; end; function ReadNextTargetInfoPart(var ADataPtr: PByte; var ARestLen: LongInt): TTargetInfoPart; var blockLen: Word; begin Result.Kind := 0; Result.Value := ''; if ARestLen < (SizeOf(Result.Kind) + SizeOf(blockLen)) then begin ARestLen := 0; Exit; end; Result.Kind := UInt16ToLittleEndian(PWord(ADataPtr)^); Inc(ADataPtr, SizeOf(Result.Kind)); Dec(ARestLen, SizeOf(Result.Kind)); blockLen := UInt16ToLittleEndian(PWord(ADataPtr)^); Inc(ADataPtr, SizeOf(blockLen)); Dec(ARestLen, SizeOf(blockLen)); blockLen := Min(blockLen, ARestLen); SetLength(Result.Value, blockLen div SizeOf(WideChar)); Move(ADataPtr^, PWideChar(Result.Value)^, Length(Result.Value) * SizeOf(WideChar)); Inc(ADataPtr, blockLen); Dec(ARestLen, blockLen); end; function NTLMParseTargetInfoData(const ATargetInfoData: AnsiString): TNTLMTargetInfoData; var blockPtr: PByte; blockLen: LongInt; infoPart: TTargetInfoPart; begin //ClearTargetInfoData(Result); Result := Default(TNTLMTargetInfoData); blockPtr := PByte(PAnsiChar(ATargetInfoData)); blockLen := Length(ATargetInfoData); while blockLen >= 2 * SizeOf(Word) do begin infoPart := ReadNextTargetInfoPart(blockPtr, blockLen); case infoPart.Kind of 1: Result.ServerName := infoPart.Value; 2: Result.DomainName := infoPart.Value; 3: Result.DNSHost := infoPart.Value; 4: Result.DNSDomain := infoPart.Value; end; end; end; function DecodeNTLMServerChallengeMsg(const ABase64DataStr: TGMString): TNTLMServerResponse; var decodedData: AnsiString; blockPtr: PByte; blockLen: LongInt; serverMsg: TNTLMServerChallengeMsg; // targetInfo: TNTLMTargetInfoData; begin //NTLMClearServerResponse(Result); Result := Default(TNTLMServerResponse); //FillByte(serverMsg, SizeOf(serverMsg), 0); serverMsg := Default(TNTLMServerChallengeMsg); decodedData := GMDecodeBase64Str(ABase64DataStr); Move(PAnsiChar(decodedData)^, serverMsg, Max(0, Min(SizeOf(serverMsg), Length(decodedData)))); //serverMsg := PNTLMServerChallengeMsg(PAnsiChar(decodedData)); serverMsg.MSgKind := Int32FromLittleEndian(serverMsg.MSgKind); ConvertDynBufferDescFromLittleEndian(serverMsg.Domain); ConvertDynBufferDescFromLittleEndian(serverMsg.TargetInfo); serverMsg.Flags := UInt32FromLittleEndian(serverMsg.Flags); Result.Protocol := serverMsg.Protocol; Result.MSgKind := serverMsg.MSgKind; Result.Flags := serverMsg.Flags; Result.Nonce := serverMsg.Nonce; Result.Domain := GetNTLMDynDataAsString(serverMsg.Domain, decodedData, serverMsg.Flags); blockPtr := GMAddPtr(PAnsiChar(decodedData), serverMsg.TargetInfo.Offset); blockLen := Max(0, Min(serverMsg.TargetInfo.LenInBytes, Length(decodedData) - serverMsg.TargetInfo.Offset)); SetLength(Result.TargetInfoBlockData, blockLen); if blockLen > 0 then Move(blockPtr^, PAnsiChar(Result.TargetInfoBlockData)^, blockLen); //targetInfo := NTLMParseTargetInfoData(Result.TargetInfoBlockData); //blockLen := 0; end; procedure GetUserAndDomainNameFromInput(const AUserAndDomainCombined: TGMString; var AUserName, ADomainName: TGMString); var pCh: PGMChar; begin pCh := GMStrLScan(PGMChar(AUserAndDomainCombined), '\', Length(AUserAndDomainCombined)); if pCh <> nil then begin ADomainName := Copy(AUserAndDomainCombined, 1, pCh - PGMChar(AUserAndDomainCombined)); AUserName := Copy(AUserAndDomainCombined, pCh - PGMChar(AUserAndDomainCombined) + 2, Length(AUserAndDomainCombined) - (pCh - PGMChar(AUserAndDomainCombined)) - 1); end else begin pCh := GMStrLScan(PGMChar(AUserAndDomainCombined), '@', Length(AUserAndDomainCombined)); if pCh <> nil then begin AUserName := Copy(AUserAndDomainCombined, 1, pCh - PGMChar(AUserAndDomainCombined)); ADomainName := Copy(AUserAndDomainCombined, pCh - PGMChar(AUserAndDomainCombined) + 2, Length(AUserAndDomainCombined) - (pCh - PGMChar(AUserAndDomainCombined)) - 1); end else begin ADomainName := ''; AUserName := AUserAndDomainCombined; end end; end; procedure AssignDESKey(AKey_56: PDES_cblock; AKs: PDES_key_schedule); var key: des_cblock; begin key[0] := AKey_56[0]; key[1] := ((AKey_56[0] shl 7) and $FF) or (AKey_56[1] shr 1); key[2] := ((AKey_56[1] shl 6) and $FF) or (AKey_56[2] shr 2); key[3] := ((AKey_56[2] shl 5) and $FF) or (AKey_56[3] shr 3); key[4] := ((AKey_56[3] shl 4) and $FF) or (AKey_56[4] shr 4); key[5] := ((AKey_56[4] shl 3) and $FF) or (AKey_56[5] shr 5); key[6] := ((AKey_56[5] shl 2) and $FF) or (AKey_56[6] shr 6); key[7] := (AKey_56[6] shl 1) and $FF; DES_set_odd_parity(@key); // <- OpenSSL routine DES_set_key(@key, AKs); // <- OpenSSL routine end; function CalcDESHash(key: Pointer; data: PDES_cblock): AnsiString; var ks: des_key_schedule; begin //FillByte(ks, SizeOf(ks), 0); ks := Default(des_key_schedule); SetLength(Result, 24); AssignDESKey(PDES_cblock(key), @ks); DES_ecb_encrypt(data, PDES_cblock(PAnsiChar(Result)), @ks, DES_ENCRYPT); AssignDESKey(PDES_cblock(GMAddPtr(key, 7)), @ks); DES_ecb_encrypt(data, PDES_cblock(PAnsiChar(Result) + 8), @ks, DES_ENCRYPT); AssignDESKey(PDES_cblock(GMAddPtr(key, 14)), @ks); DES_ecb_encrypt(data, PDES_cblock(PAnsiChar(Result) + 16), @ks, DES_ENCRYPT); end; function BuildLMData(const APassword, ANonce: AnsiString): AnsiString; const cFixedPwdLen = 14; cMagic: des_cblock = ($4B, $47, $53, $21, $40, $23, $24, $25); var pw, pwHash: AnsiString; ks: des_key_schedule; padLen: Integer; begin //FillByte(ks, SizeOf(ks), 0); ks := Default(des_key_schedule); pw := GMUpperCaseA(APassword); padLen := cFixedPwdLen - Length(pw); SetLength(pw, cFixedPwdLen); if padLen > 0 then FillByte(pw[Length(pw) - padLen + 1], padLen, 0); SetLength(pwHash, cResponseHashSize); FillByte(ks, SizeOf(ks), 0); AssignDESKey(PDES_cblock(PAnsiChar(pw)), @ks); DES_ecb_encrypt(@cMagic, PDES_cblock(@pwHash[1]), @ks, DES_ENCRYPT); AssignDESKey(PDES_cblock(PAnsiChar(pw) + 7), @ks); DES_ecb_encrypt(@cMagic, PDES_cblock(@pwHash[9]), @ks, DES_ENCRYPT); FillByte(pwHash[17], 5, 0); Result := CalcDESHash(PAnsiChar(pwHash), PDES_cblock(PAnsiChar(ANonce))); end; function BuildNTData(const APassword: UnicodeString; const ANonce: AnsiString): AnsiString; var padLen: Integer; begin Result := GMCalcHashValue(PWideChar(APassword), Length(APassword) * SizeOf(WideChar), CALG_MD4); padLen := cResponseHashSize - Length(Result); SetLength(Result, cResponseHashSize); if padLen > 0 then FillByte(Result[Length(Result) - padLen + 1], padLen, 0); Result := CalcDESHash(PAnsiChar(Result), PDES_cblock(PAnsiChar(ANonce))); end; function WideStrContensAsAnsiStr(const AValue: UnicodeString): AnsiString; begin SetLength(Result, Length(AValue) * SizeOf(WideChar)); Move(PWideChar(AValue)^, PAnsiChar(Result)^, Length(Result)); end; function CalcNTLMv2Hash(const AUserName, APassword, ADomain: UnicodeString): AnsiString; var md4PwdHash: AnsiString; // identity: UnicodeString; begin md4PwdHash := GMCalcHashValue(PWideChar(APassword), Length(APassword) * SizeOf(WideChar), CALG_MD4); //identity := GMUpperCaseW(AUserName) + ADomain; Result := GMHmacMd5(WideStrContensAsAnsiStr(GMUpperCaseW(AUserName) + ADomain), md4PwdHash); end; function BuildLMv2Data(const AUserName, APassword, ADomain: UnicodeString; const AServerNonce, AClientNonce: AnsiString): AnsiString; var ntlmV2Hash, hmacMD5: AnsiString; begin ntlmV2Hash := CalcNTLMv2Hash(AUserName, APassword, ADomain); hmacMD5 := GMHmacMd5(AServerNonce + AClientNonce, ntlmV2Hash); Result := hmacMD5 + AClientNonce; end; function BuildNtlmV2Blob(const ATargetInfoBlock, AClientNonce: AnsiString): AnsiString; var dataPtr: PByte; function CalcTimeStamp(const ADateTime: TDatetime): Int64; const cUnixStartDate: TDateTime = 25569.0; begin Result := Round((((ADateTime - cUnixStartDate) * 86400) + 11644473600) * 10000000); end; begin SetLength(Result, 4 * SizeOf(LongInt) + SizeOf(Int64) + Length(AClientNonce) + Length(ATargetInfoBlock)); FillByte(PAnsiChar(Result)^, Length(Result), 0); dataPtr := PByte(PAnsiChar(Result)); PLongInt(dataPtr)^ := Int32ToLittleEndian($00000101); Inc(dataPtr, SizeOf(LongInt)); PLongInt(dataPtr)^ := Int32ToLittleEndian(0); Inc(dataPtr, SizeOf(LongInt)); PInt64(dataPtr)^ := Int64ToLittleEndian(CalcTimeStamp(Now)); Inc(dataPtr, SizeOf(Int64)); Move(PAnsiChar(AClientNonce)^, dataPtr^, Length(AClientNonce)); Inc(dataPtr, Length(AClientNonce)); PLongInt(dataPtr)^ := Int32ToLittleEndian(0); Inc(dataPtr, SizeOf(LongInt)); Move(PAnsiChar(ATargetInfoBlock)^, dataPtr^, Length(ATargetInfoBlock)); Inc(dataPtr, Length(ATargetInfoBlock)); PLongInt(dataPtr)^ := Int32ToLittleEndian(0); //Inc(dataPtr, SizeOf(LongInt)); end; function BuildNTv2Data(const AUserName, APassword, ADomain: UnicodeString; const AServerNonce, AClientNonce, ATargetInfoBlock: AnsiString): AnsiString; var ntlmV2Hash, blobData, hmacMD5: AnsiString; begin ntlmV2Hash := CalcNTLMv2Hash(AUserName, APassword, ADomain); blobData := BuildNtlmV2Blob(ATargetInfoBlock, AClientNonce); hmacMD5 := GMHmacMd5(AServerNonce + blobData, ntlmV2Hash); Result := hmacMD5 + blobData; end; function CreateRandomClientNonce: AnsiString; var i: Integer; begin SetLength(Result, 8); for i:=1 to Length(Result) do Result[i] := Chr(Random(256)); end; function BuildNTLMClientCredentialsMsg(const AUserName, APassword: TGMString; AServerResponse: PNTLMServerResponse): TGMString; var bufStr, lmData, ntData, clientNonce: AnsiString; hostName, domainName, userName: UnicodeString; // targetInfo: TNTLMTargetInfoData; begin Result := ''; if AServerResponse = nil then Exit; //targetInfo := NTLMParseTargetInfoData(AServerResponse.TargetInfoBlockData); hostName := GMUpperCase(GMThisComputerName); GetUserAndDomainNameFromInput(AUserName, userName, domainName); domainName := GMUpperCaseW(domainName); lmData := ''; ntData := ''; clientNonce := CreateRandomClientNonce; if AServerResponse.Flags and NTLM_NTLM <> 0 then begin if Length(AServerResponse.TargetInfoBlockData) >= 2 * SizeOf(Word) then ntData := BuildNTv2Data(userName, APassword, domainName, AServerResponse.Nonce, clientNonce, AServerResponse.TargetInfoBlockData) else ntData := BuildNTData(APassword, AServerResponse.Nonce); end else if Length(AServerResponse.TargetInfoBlockData) >= 2 * SizeOf(Word) then lmData := BuildLMv2Data(userName, APassword, domainName, AServerResponse.Nonce, clientNonce) else lmData := BuildLMData(APassword, AServerResponse.Nonce); SetLength(bufStr, SizeOf(TNTLNClientCredentialMsg) + (Length(userName) + Length(hostName) + Length(domainName)) * SizeOf(WideChar) + Length(lmData) + Length(ntData)); FillByte(PAnsiChar(bufStr)^, Length(bufStr), 0); with PNTLNClientCredentialMsg(PAnsiChar(bufStr))^ do begin Move(cStrNTLMProtocolSignature[1], Protocol, Length(cStrNTLMProtocolSignature)); MSgKind := 3; Domain.LenInBytes := Length(domainName) * SizeOf(WideChar); Domain.AllocSizeInBytes := Domain.LenInBytes; Domain.Offset := SizeOf(TNTLNClientCredentialMsg); Move(PWideChar(domainName)^, (PAnsiChar(bufStr) + Domain.Offset)^, Domain.LenInBytes); User.LenInBytes := Length(userName) * SizeOf(WideChar); User.AllocSizeInBytes := User.LenInBytes; User.Offset := Domain.Offset + Domain.LenInBytes; Move(PWideChar(userName)^, (PAnsiChar(bufStr) + User.Offset)^, User.LenInBytes); Host.LenInBytes := Length(hostName) * SizeOf(WideChar); Host.AllocSizeInBytes := Host.LenInBytes; Host.Offset := User.Offset + User.LenInBytes; Move(PWideChar(hostName)^, (PAnsiChar(bufStr) + Host.Offset)^, Host.LenInBytes); LMPwdHash.LenInBytes := Length(lmData); LMPwdHash.AllocSizeInBytes := LMPwdHash.LenInBytes; LMPwdHash.Offset := Host.Offset + Host.LenInBytes; Move(PAnsiChar(lmData)^, (PAnsiChar(bufStr) + LMPwdHash.Offset)^, LMPwdHash.LenInBytes); NTPwdHash.LenInBytes := Length(ntData); NTPwdHash.AllocSizeInBytes := NTPwdHash.LenInBytes; NTPwdHash.Offset := LMPwdHash.Offset + LMPwdHash.LenInBytes; Move(PAnsiChar(ntData)^, (PAnsiChar(bufStr) + NTPwdHash.Offset)^, NTPwdHash.LenInBytes); SessionKey.Offset := Length(bufStr); ConvertDynBufferDescToLittleEndian(LMPwdHash); ConvertDynBufferDescToLittleEndian(NTPwdHash); ConvertDynBufferDescToLittleEndian(Host); ConvertDynBufferDescToLittleEndian(User); ConvertDynBufferDescToLittleEndian(Domain); ConvertDynBufferDescToLittleEndian(SessionKey); Flags := UInt32ToLittleEndian(NTLM_Always_Sign or NTLM_NTLM or NTLM_Unicode_Charset); end; Result := GMEncodeBase64Str(bufStr); end; initialization Randomize; end.