Skip to content

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Very simple NTLM authenticator
http = require 'http'
ntlm = require './ntlm'
serverUrl = 'localhost:30000/login'
server = http.createServer (req, res) ->
ntlm.handleRequest req, serverUrl, (err, response, httpCode) ->
if err?
console.log 'ERROR:', err
res.writeHead 400
res.end err
else
console.log 'response', response, 'httpCode', httpCode
headers =
'WWW-Authenticate': response
res.writeHead httpCode, headers
res.end response
server.listen 20001
binary = require 'binary'
crypto = require 'crypto'
restler = require 'restler'
class Flags
@NEGOTIATE_UNICODE = 0x01
@NEGOTIATE_OEM = 0x02
@REQUEST_TARGET = 0x04
@NEGOTIATE_NTLM = 0x200
@NEGOTIATE_DOMAIN_SUPPLIED = 0x1000
@NEGOTIATE_WORKSTATION_SUPPLIED = 0x2000
@NEGOTIATE_LOCAL_CALL = 0x4000
@NEGOTIATE_ALWAYS_SIGN = 0x8000
@NEGOTIATE_TARGET_IS_DOMAIN = 0x10000
@NEGOTIATE_TARGET_IS_SERVER = 0x20000
@NEGOTIATE_TARGET_IS_SHARE = 0x40000
@NEGOTIATE_NTLM2_KEY = 0x80000
@NEGOTIATE_TARGET_SUPPLIED = 0x800000
@NEGOTIATE_128 = 0x20000000
@NEGOTIATE_56 = 0x80000000
class GenerateNtlm
constructor: (type) ->
@buffer = new Buffer 1024
@bufferOffset = 0
@writeHeader()
@writeType type
write: (data, length) ->
for i in [0...length]
@buffer.writeUInt8 data[i], @bufferOffset
@bufferOffset += 1
writeUInt16: (v) ->
@buffer.writeUInt16LE v, @bufferOffset
@bufferOffset += 2
writeUInt32: (v) ->
@buffer.writeUInt32LE v, @bufferOffset
@bufferOffset += 4
writeStaticString: (s) ->
@buffer.write s, @bufferOffset
@bufferOffset += s.length
@buffer.writeUInt8 0, @bufferOffset
@bufferOffset += 1
writeType: (type) ->
@writeUInt32 type
writeHeader: ->
@writeStaticString "NTLMSSP"
writeFlags: (flags) ->
@writeUInt32 flags
writeString: (str) ->
@writeUInt16 str.length
@writeUInt16 str.length
@writeUInt32 0 # offset
finalize: ->
buf = @buffer.slice 0, @bufferOffset
"NTLM " + buf.toString 'base64'
class GenerateNtlm1 extends GenerateNtlm
constructor: () ->
super 1
@writeFlags Flags.NEGOTIATE_UNICODE | Flags.NEGOTIATE_OEM
class GenerateNtlm2 extends GenerateNtlm
constructor: ->
super 2
generate: (targetName, done) ->
@writeString targetName
@writeFlags Flags.NEGOTIATE_UNICODE | Flags.NEGOTIATE_OEM
crypto.randomBytes 8, (ex, buf) =>
@nonce = buf
@write @nonce, 8
result = @finalize()
done null, result
class ParseNtlm
parseLength: ->
@parser.word16lu('length').vars.length
parseUint32: ->
@parser.word32lu('temp').vars.temp
parseLengthAndOffset: ->
result =
length: @parseLength()
allocated: @parseLength()
offset: @parser.word32lu('offset').vars.offset
result
parseString: ->
data = @parseLengthAndOffset()
return '' if data.length is 0
start = data.offset
end = data.offset + data.length - 1
stringBuffer = @buffer.slice start, end
parsedString = stringBuffer.toString 'utf-8'
parsedString
parseStaticString: (len) ->
buf = @parser.buffer('temp', len).vars.temp
console.log 'buf slice len:', buf.length
buf.toString().slice 0, -1
class ParseNtlmHeader extends ParseNtlm
parse: (authentication, done) ->
@buffer = new Buffer authentication, 'base64'
@parser = binary.parse @buffer
protocol = @parseStaticString 8
if protocol isnt 'NTLMSSP'
done "Wrong header: #{protocol}"
return
type = @parseUint32()
if type < 1 or type > 3
done "Unknown type: #{type}"
return
done null, type
class ParseNtlm3 extends ParseNtlm
constructor: (authentication) ->
@buffer = new Buffer authentication, 'base64'
@parser = binary.parse @buffer
@protocol = @parseStaticString 8
type = @parser.word8('type').vars.type
@parser.word8 ''
@parser.word8 ''
@parser.word8 ''
@lanManagerResponse = @parseString()
@ntResponse = @parseString()
@domain = @parseString()
@username = @parseString()
@workstation = @parseString()
debugOutput: ->
console.log "Username: '#{@username}'"
console.log "domain: '#{@domain}'"
console.log "workstation: '#{@workstation}'"
respond = (authorization, loginServerUrl, done) ->
if authorization?
isNtlm = authorization.substr(0, 4) is 'NTLM'
authorization = authorization.substr(5)
headerParse = new ParseNtlmHeader
headerParse.parse authorization, (err, type) ->
if err?
done err
return
switch type
when 3
console.log 'got packet 3'
parser = new ParseNtlm3 authorization
parser.debugOutput()
loginOptions =
username: parser.username
operation = restler.postJson loginServerUrl, loginOptions
operation.on 'complete', (data, res) ->
console.log 'data:', data
done null, data, 200
return
when 1
console.log 'got packet 1'
packet = new GenerateNtlm2
packet.generate '', (err, response) ->
authenticateResponse = response
console.log authenticateResponse
done null, authenticateResponse, 401
return
else
done "Unexpected type:#{type}"
return
else
authenticateResponse = 'NTLM'
done null, authenticateResponse, 401
exports.handleRequest = (req, loginServerUrl, done) ->
authorization = req.headers['authorization']
console.log 'authorization', authorization
respond authorization, loginServerUrl, done
generateSessionKey = (done) ->
crypto.randomBytes 32, (ex, buf) =>
done null, buf.toString('hex')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.