2011-08-04 20:32:01 +00:00
|
|
|
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
|
|
|
2020-10-31 21:07:32 +00:00
|
|
|
const EventEmitter = require('events').EventEmitter
|
|
|
|
const util = require('util')
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2020-10-31 21:07:32 +00:00
|
|
|
const assert = require('assert-plus')
|
2022-03-27 14:13:16 +00:00
|
|
|
const asn1 = require('@ldapjs/asn1')
|
2020-10-31 21:07:32 +00:00
|
|
|
const logger = require('../logger')
|
|
|
|
|
2023-02-21 18:28:58 +00:00
|
|
|
const messages = require('@ldapjs/messages')
|
|
|
|
const AbandonRequest = messages.AbandonRequest
|
|
|
|
const AddRequest = messages.AddRequest
|
|
|
|
const AddResponse = messages.AddResponse
|
|
|
|
const BindRequest = messages.BindRequest
|
|
|
|
const BindResponse = messages.BindResponse
|
|
|
|
const CompareRequest = messages.CompareRequest
|
|
|
|
const CompareResponse = messages.CompareResponse
|
|
|
|
const DeleteRequest = messages.DeleteRequest
|
|
|
|
const DeleteResponse = messages.DeleteResponse
|
|
|
|
const ExtendedRequest = messages.ExtensionRequest
|
|
|
|
const ExtendedResponse = messages.ExtensionResponse
|
|
|
|
const ModifyRequest = messages.ModifyRequest
|
|
|
|
const ModifyResponse = messages.ModifyResponse
|
|
|
|
const ModifyDNRequest = messages.ModifyDnRequest
|
|
|
|
const ModifyDNResponse = messages.ModifyDnResponse
|
|
|
|
const SearchRequest = messages.SearchRequest
|
|
|
|
const SearchEntry = messages.SearchResultEntry
|
|
|
|
const SearchReference = messages.SearchResultReference
|
2020-10-31 21:07:32 +00:00
|
|
|
const SearchResponse = require('./search_response')
|
2023-02-21 18:28:58 +00:00
|
|
|
const UnbindRequest = messages.UnbindRequest
|
|
|
|
const LDAPResult = messages.LdapResult
|
2019-08-27 21:11:49 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
const Protocol = require('@ldapjs/protocol')
|
2019-08-27 21:11:49 +00:00
|
|
|
|
|
|
|
/// --- Globals
|
|
|
|
|
2020-10-31 21:07:32 +00:00
|
|
|
const BerReader = asn1.BerReader
|
2019-08-27 21:11:49 +00:00
|
|
|
|
|
|
|
/// --- API
|
|
|
|
|
|
|
|
function Parser (options = {}) {
|
|
|
|
assert.object(options)
|
|
|
|
|
|
|
|
EventEmitter.call(this)
|
|
|
|
|
|
|
|
this.buffer = null
|
|
|
|
this.log = options.log || logger
|
2011-08-04 20:32:01 +00:00
|
|
|
}
|
2019-08-27 21:11:49 +00:00
|
|
|
util.inherits(Parser, EventEmitter)
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2023-02-21 18:28:58 +00:00
|
|
|
/**
|
|
|
|
* The LDAP server/client implementations will receive data from a stream and feed
|
|
|
|
* it into this method. This method will collect that data into an internal
|
|
|
|
* growing buffer. As that buffer fills with enough data to constitute a valid
|
|
|
|
* LDAP message, the data will be parsed, emitted as a message object, and
|
|
|
|
* reset the buffer to account for any next message in the stream.
|
|
|
|
*/
|
2012-02-18 08:15:52 +00:00
|
|
|
Parser.prototype.write = function (data) {
|
2019-08-27 21:11:49 +00:00
|
|
|
if (!data || !Buffer.isBuffer(data)) { throw new TypeError('data (buffer) required') }
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2020-10-31 21:07:32 +00:00
|
|
|
let nextMessage = null
|
|
|
|
const self = this
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2019-08-27 21:11:49 +00:00
|
|
|
function end () {
|
|
|
|
if (nextMessage) { return self.write(nextMessage) }
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2019-08-27 21:11:49 +00:00
|
|
|
return true
|
2011-11-11 18:08:48 +00:00
|
|
|
}
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2023-02-21 18:28:58 +00:00
|
|
|
self.buffer = self.buffer ? Buffer.concat([self.buffer, data]) : data
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2023-02-21 18:28:58 +00:00
|
|
|
let ber = new BerReader(self.buffer)
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2020-10-31 21:07:32 +00:00
|
|
|
let foundSeq = false
|
2014-09-07 19:42:31 +00:00
|
|
|
try {
|
2019-08-27 21:11:49 +00:00
|
|
|
foundSeq = ber.readSequence()
|
2014-09-07 19:42:31 +00:00
|
|
|
} catch (e) {
|
2019-08-27 21:11:49 +00:00
|
|
|
this.emit('error', e)
|
2014-09-07 19:42:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!foundSeq || ber.remain < ber.length) {
|
|
|
|
// ENOTENOUGH
|
2019-08-27 21:11:49 +00:00
|
|
|
return false
|
2014-09-07 19:42:31 +00:00
|
|
|
} else if (ber.remain > ber.length) {
|
|
|
|
// ETOOMUCH
|
2023-02-21 18:28:58 +00:00
|
|
|
|
|
|
|
// This is an odd branch. Basically, it is setting `nextMessage` to
|
|
|
|
// a buffer that represents data part of a message subsequent to the one
|
|
|
|
// being processed. It then re-creates `ber` as a representation of
|
|
|
|
// the message being processed and advances its offset to the value
|
|
|
|
// position of the TLV.
|
|
|
|
|
|
|
|
// Set `nextMessage` to the bytes subsequent to the current message's
|
|
|
|
// value bytes. That is, slice from the byte immediately following the
|
|
|
|
// current message's value bytes until the end of the buffer.
|
2019-08-27 21:11:49 +00:00
|
|
|
nextMessage = self.buffer.slice(ber.offset + ber.length)
|
2023-02-21 18:28:58 +00:00
|
|
|
|
|
|
|
const currOffset = ber.offset
|
|
|
|
ber = new BerReader(ber.buffer.subarray(0, currOffset + ber.length))
|
|
|
|
ber.readSequence()
|
|
|
|
|
2019-08-27 21:11:49 +00:00
|
|
|
assert.equal(ber.remain, ber.length)
|
2011-11-11 18:08:48 +00:00
|
|
|
}
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
// If we're here, ber holds the message, and nextMessage is temporarily
|
|
|
|
// pointing at the next sequence of data (if it exists)
|
2019-08-27 21:11:49 +00:00
|
|
|
self.buffer = null
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2020-10-31 21:07:32 +00:00
|
|
|
let message
|
2011-11-11 18:08:48 +00:00
|
|
|
try {
|
2023-02-21 18:28:58 +00:00
|
|
|
if (Object.prototype.toString.call(ber) === '[object BerReader]') {
|
|
|
|
// Parse the BER into a JavaScript object representation. The message
|
|
|
|
// objects require the full sequence in order to construct the object.
|
|
|
|
// At this point, we have already read the sequence tag and length, so
|
|
|
|
// we need to rewind the buffer a bit. The `.sequenceToReader` method
|
|
|
|
// does this for us.
|
|
|
|
message = messages.LdapMessage.parse(ber.sequenceToReader())
|
|
|
|
} else {
|
|
|
|
// Bail here if peer isn't speaking protocol at all
|
|
|
|
message = this.getMessage(ber)
|
|
|
|
}
|
2014-09-07 19:42:31 +00:00
|
|
|
|
|
|
|
if (!message) {
|
2019-08-27 21:11:49 +00:00
|
|
|
return end()
|
2014-09-07 19:42:31 +00:00
|
|
|
}
|
2023-02-21 18:28:58 +00:00
|
|
|
|
|
|
|
// TODO: find a better way to handle logging now that messages and the
|
|
|
|
// server are decoupled. ~ jsumners 2023-02-17
|
|
|
|
message.log = this.log
|
2011-08-04 20:32:01 +00:00
|
|
|
} catch (e) {
|
2019-08-27 21:11:49 +00:00
|
|
|
this.emit('error', e, message)
|
|
|
|
return false
|
2011-08-04 20:32:01 +00:00
|
|
|
}
|
|
|
|
|
2019-08-27 21:11:49 +00:00
|
|
|
this.emit('message', message)
|
|
|
|
return end()
|
|
|
|
}
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2012-02-18 08:15:52 +00:00
|
|
|
Parser.prototype.getMessage = function (ber) {
|
2019-08-27 21:11:49 +00:00
|
|
|
assert.ok(ber)
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2020-10-31 21:07:32 +00:00
|
|
|
const self = this
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2023-02-21 18:28:58 +00:00
|
|
|
const messageId = ber.readInt()
|
2020-10-31 21:07:32 +00:00
|
|
|
const type = ber.readSequence()
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2020-10-31 21:07:32 +00:00
|
|
|
let Message
|
2011-08-04 20:32:01 +00:00
|
|
|
switch (type) {
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_REQ_ABANDON:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = AbandonRequest
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_REQ_ADD:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = AddRequest
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_RES_ADD:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = AddResponse
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_REQ_BIND:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = BindRequest
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_RES_BIND:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = BindResponse
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_REQ_COMPARE:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = CompareRequest
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_RES_COMPARE:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = CompareResponse
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_REQ_DELETE:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = DeleteRequest
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_RES_DELETE:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = DeleteResponse
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_REQ_EXTENSION:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = ExtendedRequest
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_RES_EXTENSION:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = ExtendedResponse
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_REQ_MODIFY:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = ModifyRequest
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_RES_MODIFY:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = ModifyResponse
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_REQ_MODRDN:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = ModifyDNRequest
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_RES_MODRDN:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = ModifyDNResponse
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_REQ_SEARCH:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = SearchRequest
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_RES_SEARCH_ENTRY:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = SearchEntry
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_RES_SEARCH_REF:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = SearchReference
|
|
|
|
break
|
2011-09-27 18:49:33 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_RES_SEARCH:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = SearchResponse
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2022-06-06 02:01:40 +00:00
|
|
|
case Protocol.operations.LDAP_REQ_UNBIND:
|
2019-08-27 21:11:49 +00:00
|
|
|
Message = UnbindRequest
|
|
|
|
break
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2019-08-27 21:11:49 +00:00
|
|
|
default:
|
|
|
|
this.emit('error',
|
|
|
|
new Error('Op 0x' + (type ? type.toString(16) : '??') +
|
2012-02-18 08:15:52 +00:00
|
|
|
' not supported'),
|
2019-08-27 21:11:49 +00:00
|
|
|
new LDAPResult({
|
2023-02-21 18:28:58 +00:00
|
|
|
messageId: messageId,
|
2022-06-06 02:01:40 +00:00
|
|
|
protocolOp: type || Protocol.operations.LDAP_RES_EXTENSION
|
2019-08-27 21:11:49 +00:00
|
|
|
}))
|
2011-11-11 18:08:48 +00:00
|
|
|
|
2019-08-27 21:11:49 +00:00
|
|
|
return false
|
2011-08-04 20:32:01 +00:00
|
|
|
}
|
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
return new Message({
|
2023-02-21 18:28:58 +00:00
|
|
|
messageId: messageId,
|
2012-02-18 08:54:22 +00:00
|
|
|
log: self.log
|
2019-08-27 21:11:49 +00:00
|
|
|
})
|
|
|
|
}
|
2015-10-31 17:28:25 +00:00
|
|
|
|
2019-08-27 21:11:49 +00:00
|
|
|
/// --- Exports
|
2015-10-31 17:28:25 +00:00
|
|
|
|
2019-08-27 21:11:49 +00:00
|
|
|
module.exports = Parser
|