Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Early PR: Support ntlm based proxy in tunnel agent. #36

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
395 changes: 395 additions & 0 deletions lib/ntlm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,395 @@
// This software (ntlm.js) was copied from a file of the same name at https://github.com/SamDecrock/node-http-ntlm/blob/master/ntlm.js.
//
// As of this writing, it is a part of the node-http-ntlm module produced by SamDecrock.
//
// It is used as a part of the NTLM proxy support

var crypto = require('crypto');

var flags = {
NTLM_NegotiateUnicode : 0x00000001,
NTLM_NegotiateOEM : 0x00000002,
NTLM_RequestTarget : 0x00000004,
NTLM_Unknown9 : 0x00000008,
NTLM_NegotiateSign : 0x00000010,
NTLM_NegotiateSeal : 0x00000020,
NTLM_NegotiateDatagram : 0x00000040,
NTLM_NegotiateLanManagerKey : 0x00000080,
NTLM_Unknown8 : 0x00000100,
NTLM_NegotiateNTLM : 0x00000200,
NTLM_NegotiateNTOnly : 0x00000400,
NTLM_Anonymous : 0x00000800,
NTLM_NegotiateOemDomainSupplied : 0x00001000,
NTLM_NegotiateOemWorkstationSupplied : 0x00002000,
NTLM_Unknown6 : 0x00004000,
NTLM_NegotiateAlwaysSign : 0x00008000,
NTLM_TargetTypeDomain : 0x00010000,
NTLM_TargetTypeServer : 0x00020000,
NTLM_TargetTypeShare : 0x00040000,
NTLM_NegotiateExtendedSecurity : 0x00080000,
NTLM_NegotiateIdentify : 0x00100000,
NTLM_Unknown5 : 0x00200000,
NTLM_RequestNonNTSessionKey : 0x00400000,
NTLM_NegotiateTargetInfo : 0x00800000,
NTLM_Unknown4 : 0x01000000,
NTLM_NegotiateVersion : 0x02000000,
NTLM_Unknown3 : 0x04000000,
NTLM_Unknown2 : 0x08000000,
NTLM_Unknown1 : 0x10000000,
NTLM_Negotiate128 : 0x20000000,
NTLM_NegotiateKeyExchange : 0x40000000,
NTLM_Negotiate56 : 0x80000000
};
var typeflags = {
NTLM_TYPE1_FLAGS : flags.NTLM_NegotiateUnicode
+ flags.NTLM_NegotiateOEM
+ flags.NTLM_RequestTarget
+ flags.NTLM_NegotiateNTLM
+ flags.NTLM_NegotiateOemDomainSupplied
+ flags.NTLM_NegotiateOemWorkstationSupplied
+ flags.NTLM_NegotiateAlwaysSign
+ flags.NTLM_NegotiateExtendedSecurity
+ flags.NTLM_NegotiateVersion
+ flags.NTLM_Negotiate128
+ flags.NTLM_Negotiate56,

NTLM_TYPE2_FLAGS : flags.NTLM_NegotiateUnicode
+ flags.NTLM_RequestTarget
+ flags.NTLM_NegotiateNTLM
+ flags.NTLM_NegotiateAlwaysSign
+ flags.NTLM_NegotiateExtendedSecurity
+ flags.NTLM_NegotiateTargetInfo
+ flags.NTLM_NegotiateVersion
+ flags.NTLM_Negotiate128
+ flags.NTLM_Negotiate56
};

function createType1Message(options){
var domain = escape(options.domain.toUpperCase());
var workstation = escape(options.workstation.toUpperCase());
var protocol = 'NTLMSSP\0';

var BODY_LENGTH = 40;

var type1flags = typeflags.NTLM_TYPE1_FLAGS;
if(!domain || domain === '')
type1flags = type1flags - flags.NTLM_NegotiateOemDomainSupplied;

var pos = 0;
var buf = new Buffer(BODY_LENGTH + domain.length + workstation.length);


buf.write(protocol, pos, protocol.length); pos += protocol.length; // protocol
buf.writeUInt32LE(1, pos); pos += 4; // type 1
buf.writeUInt32LE(type1flags, pos); pos += 4; // TYPE1 flag

buf.writeUInt16LE(domain.length, pos); pos += 2; // domain length
buf.writeUInt16LE(domain.length, pos); pos += 2; // domain max length
buf.writeUInt32LE(BODY_LENGTH + workstation.length, pos); pos += 4; // domain buffer offset

buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation length
buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation max length
buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // workstation buffer offset

buf.writeUInt8(5, pos); pos += 1; //ProductMajorVersion
buf.writeUInt8(1, pos); pos += 1; //ProductMinorVersion
buf.writeUInt16LE(2600, pos); pos += 2; //ProductBuild

buf.writeUInt8(0 , pos); pos += 1; //VersionReserved1
buf.writeUInt8(0 , pos); pos += 1; //VersionReserved2
buf.writeUInt8(0 , pos); pos += 1; //VersionReserved3
buf.writeUInt8(15, pos); pos += 1; //NTLMRevisionCurrent

buf.write(workstation, pos, workstation.length, 'ascii'); pos += workstation.length; // workstation string
buf.write(domain , pos, domain.length , 'ascii'); pos += domain.length;

return 'NTLM ' + buf.toString('base64');
}

function parseType2Message(rawmsg, callback){
var match = rawmsg.match(/NTLM (.+)?/);
if(!match || !match[1])
return callback(new Error("Couldn't find NTLM in the message type2 comming from the server"));

var buf = new Buffer(match[1], 'base64');

var msg = {};

msg.signature = buf.slice(0, 8);
msg.type = buf.readInt16LE(8);

if(msg.type != 2)
return callback(new Error("Server didn't return a type 2 message"));

msg.targetNameLen = buf.readInt16LE(12);
msg.targetNameMaxLen = buf.readInt16LE(14);
msg.targetNameOffset = buf.readInt32LE(16);
msg.targetName = buf.slice(msg.targetNameOffset, msg.targetNameOffset + msg.targetNameMaxLen);

msg.negotiateFlags = buf.readInt32LE(20);
msg.serverChallenge = buf.slice(24, 32);
msg.reserved = buf.slice(32, 40);

if(msg.negotiateFlags & flags.NTLM_NegotiateTargetInfo){
msg.targetInfoLen = buf.readInt16LE(40);
msg.targetInfoMaxLen = buf.readInt16LE(42);
msg.targetInfoOffset = buf.readInt32LE(44);
msg.targetInfo = buf.slice(msg.targetInfoOffset, msg.targetInfoOffset + msg.targetInfoLen);
}
return msg;
}

function createType3Message(msg2, options){
var nonce = msg2.serverChallenge;
var username = options.username;
var password = options.password;
var negotiateFlags = msg2.negotiateFlags;

var isUnicode = negotiateFlags & flags.NTLM_NegotiateUnicode;
var isNegotiateExtendedSecurity = negotiateFlags & flags.NTLM_NegotiateExtendedSecurity;

var BODY_LENGTH = 72;

var domainName = escape(options.domain.toUpperCase());
var workstation = escape(options.workstation.toUpperCase());

var workstationBytes, domainNameBytes, usernameBytes, encryptedRandomSessionKeyBytes;

var encryptedRandomSessionKey = "";
if(isUnicode){
workstationBytes = new Buffer(workstation, 'utf16le');
domainNameBytes = new Buffer(domainName, 'utf16le');
usernameBytes = new Buffer(username, 'utf16le');
encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'utf16le');
}else{
workstationBytes = new Buffer(workstation, 'ascii');
domainNameBytes = new Buffer(domainName, 'ascii');
usernameBytes = new Buffer(username, 'ascii');
encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'ascii');
}

var lmChallengeResponse = calc_resp(create_LM_hashed_password_v1(password), nonce);
var ntChallengeResponse = calc_resp(create_NT_hashed_password_v1(password), nonce);

if(isNegotiateExtendedSecurity){
var pwhash = create_NT_hashed_password_v1(password);
var clientChallenge = "";
for(var i=0; i < 8; i++){
clientChallenge += String.fromCharCode( Math.floor(Math.random()*256) );
}
var clientChallengeBytes = new Buffer(clientChallenge, 'ascii');
var challenges = ntlm2sr_calc_resp(pwhash, nonce, clientChallengeBytes);
lmChallengeResponse = challenges.lmChallengeResponse;
ntChallengeResponse = challenges.ntChallengeResponse;
}

var signature = 'NTLMSSP\0';

var pos = 0;
var buf = new Buffer(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length + encryptedRandomSessionKeyBytes.length);

buf.write(signature, pos, signature.length); pos += signature.length;
buf.writeUInt32LE(3, pos); pos += 4; // type 1

buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseLen
buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseMaxLen
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length, pos); pos += 4; // LmChallengeResponseOffset

buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseLen
buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseMaxLen
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length, pos); pos += 4; // NtChallengeResponseOffset

buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameLen
buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameMaxLen
buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // DomainNameOffset

buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameLen
buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameMaxLen
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length, pos); pos += 4; // UserNameOffset

buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationLen
buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationMaxLen
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length, pos); pos += 4; // WorkstationOffset

buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyLen
buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyMaxLen
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length, pos); pos += 4; // EncryptedRandomSessionKeyOffset

buf.writeUInt32LE(typeflags.NTLM_TYPE2_FLAGS, pos); pos += 4; // NegotiateFlags

buf.writeUInt8(5, pos); pos++; // ProductMajorVersion
buf.writeUInt8(1, pos); pos++; // ProductMinorVersion
buf.writeUInt16LE(2600, pos); pos += 2; // ProductBuild
buf.writeUInt8(0, pos); pos++; // VersionReserved1
buf.writeUInt8(0, pos); pos++; // VersionReserved2
buf.writeUInt8(0, pos); pos++; // VersionReserved3
buf.writeUInt8(15, pos); pos++; // NTLMRevisionCurrent

domainNameBytes.copy(buf, pos); pos += domainNameBytes.length;
usernameBytes.copy(buf, pos); pos += usernameBytes.length;
workstationBytes.copy(buf, pos); pos += workstationBytes.length;
lmChallengeResponse.copy(buf, pos); pos += lmChallengeResponse.length;
ntChallengeResponse.copy(buf, pos); pos += ntChallengeResponse.length;
encryptedRandomSessionKeyBytes.copy(buf, pos); pos += encryptedRandomSessionKeyBytes.length;

return 'NTLM ' + buf.toString('base64');
}

function create_LM_hashed_password_v1(password){
// fix the password length to 14 bytes
password = password.toUpperCase();
var passwordBytes = new Buffer(password, 'ascii');

var passwordBytesPadded = new Buffer(14);
passwordBytesPadded.fill("\0");
var sourceEnd = 14;
if(passwordBytes.length < 14) sourceEnd = passwordBytes.length;
passwordBytes.copy(passwordBytesPadded, 0, 0, sourceEnd);

// split into 2 parts of 7 bytes:
var firstPart = passwordBytesPadded.slice(0,7);
var secondPart = passwordBytesPadded.slice(7);

function encrypt(buf){
var key = insertZerosEvery7Bits(buf);
var des = crypto.createCipheriv('DES-ECB', key, '');
return des.update("KGS!@#$%"); // page 57 in [MS-NLMP]);
}

var firstPartEncrypted = encrypt(firstPart);
var secondPartEncrypted = encrypt(secondPart);

return Buffer.concat([firstPartEncrypted, secondPartEncrypted]);
}

function insertZerosEvery7Bits(buf){
var binaryArray = bytes2binaryArray(buf);
var newBinaryArray = [];
for(var i=0; i<binaryArray.length; i++){
newBinaryArray.push(binaryArray[i]);

if((i+1)%7 === 0){
newBinaryArray.push(0);
}
}
return binaryArray2bytes(newBinaryArray);
}

function bytes2binaryArray(buf){
var hex2binary = {
0: [0,0,0,0],
1: [0,0,0,1],
2: [0,0,1,0],
3: [0,0,1,1],
4: [0,1,0,0],
5: [0,1,0,1],
6: [0,1,1,0],
7: [0,1,1,1],
8: [1,0,0,0],
9: [1,0,0,1],
A: [1,0,1,0],
B: [1,0,1,1],
C: [1,1,0,0],
D: [1,1,0,1],
E: [1,1,1,0],
F: [1,1,1,1]
};

var hexString = buf.toString('hex').toUpperCase();
var array = [];
for(var i=0; i<hexString.length; i++){
var hexchar = hexString.charAt(i);
array = array.concat(hex2binary[hexchar]);
}
return array;
}

function binaryArray2bytes(array){
var binary2hex = {
'0000': 0,
'0001': 1,
'0010': 2,
'0011': 3,
'0100': 4,
'0101': 5,
'0110': 6,
'0111': 7,
'1000': 8,
'1001': 9,
'1010': 'A',
'1011': 'B',
'1100': 'C',
'1101': 'D',
'1110': 'E',
'1111': 'F'
};

var bufArray = [];

for(var i=0; i<array.length; i +=8 ){
if((i+7) > array.length)
break;

var binString1 = '' + array[i] + '' + array[i+1] + '' + array[i+2] + '' + array[i+3];
var binString2 = '' + array[i+4] + '' + array[i+5] + '' + array[i+6] + '' + array[i+7];
var hexchar1 = binary2hex[binString1];
var hexchar2 = binary2hex[binString2];

var buf = new Buffer(hexchar1 + '' + hexchar2, 'hex');
bufArray.push(buf);
}

return Buffer.concat(bufArray);
}

function create_NT_hashed_password_v1(password){
var buf = new Buffer(password, 'utf16le');
var md4 = crypto.createHash('md4');
md4.update(buf);
return new Buffer(md4.digest());
}

function calc_resp(password_hash, server_challenge){
// padding with zeros to make the hash 21 bytes long
var passHashPadded = new Buffer(21);
passHashPadded.fill("\0");
password_hash.copy(passHashPadded, 0, 0, password_hash.length);

var resArray = [];

var des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(0,7)), '');
resArray.push( des.update(server_challenge.slice(0,8)) );

des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(7,14)), '');
resArray.push( des.update(server_challenge.slice(0,8)) );

des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(14,21)), '');
resArray.push( des.update(server_challenge.slice(0,8)) );

return Buffer.concat(resArray);
}

function ntlm2sr_calc_resp(responseKeyNT, serverChallenge, clientChallenge){
// padding with zeros to make the hash 16 bytes longer
var lmChallengeResponse = new Buffer(clientChallenge.length + 16);
lmChallengeResponse.fill("\0");
clientChallenge.copy(lmChallengeResponse, 0, 0, clientChallenge.length);

var buf = Buffer.concat([serverChallenge, clientChallenge]);
var md5 = crypto.createHash('md5');
md5.update(buf);
var sess = md5.digest();
var ntChallengeResponse = calc_resp(responseKeyNT, sess.slice(0,8));

return {
lmChallengeResponse: lmChallengeResponse,
ntChallengeResponse: ntChallengeResponse
};
}

exports.createType1Message = createType1Message;
exports.parseType2Message = parseType2Message;
exports.createType3Message = createType3Message;



Loading