From 954cedca3e5660b222839a76c9cef73a1bc73f15 Mon Sep 17 00:00:00 2001 From: lwvemike Date: Fri, 19 Apr 2024 14:12:22 +0300 Subject: [PATCH] fix: add error handling to socket.data callback in case of validation of configuration failure --- packages/tacacs-plus/src/client/index.ts | 174 +++++++++++++---------- 1 file changed, 97 insertions(+), 77 deletions(-) diff --git a/packages/tacacs-plus/src/client/index.ts b/packages/tacacs-plus/src/client/index.ts index c34588f..8568566 100644 --- a/packages/tacacs-plus/src/client/index.ts +++ b/packages/tacacs-plus/src/client/index.ts @@ -85,40 +85,50 @@ export class Client { socket.write(authorizationRequest.toBuffer()) socket.on('data', (data: Buffer) => { - const decodedPacket = Packet.decodePacket(data, this.#secret) - - this.#logger.debug(`Received:\n${decodedPacket.toHumanReadable()}\n`) - - const authorizationReply = AuthorizationReply.decode(decodedPacket) - - switch (authorizationReply.status) { - case AuthorizationReply.STATUSES.TAC_PLUS_AUTHOR_STATUS_FAIL: { - socket.destroy() - const error = new TacacsError('Authorization Failed', getNameFromCollectionValue(authorizationReply.status, AuthorizationReply.STATUSES)) - this.#logger.error(error.message) - return reject(error) - } - case AuthorizationReply.STATUSES.TAC_PLUS_AUTHOR_STATUS_PASS_REPL: { - this.#logger.log('PASS_REPL') - return resolve(authorizationReply.args) - } - case AuthorizationReply.STATUSES.TAC_PLUS_AUTHOR_STATUS_PASS_ADD: { - this.#logger.log('PASS_ADD') - return resolve(authorizationReply.args) - } - case AuthorizationReply.STATUSES.TAC_PLUS_AUTHOR_STATUS_ERROR: { - socket.destroy() - const error = new TacacsError('Authorization Errored', getNameFromCollectionValue(authorizationReply.status, AuthorizationReply.STATUSES)) - this.#logger.error(error.message) - return reject(error) + try { + const decodedPacket = Packet.decodePacket(data, this.#secret) + + this.#logger.debug(`Received:\n${decodedPacket.toHumanReadable()}\n`) + + const authorizationReply = AuthorizationReply.decode(decodedPacket) + + switch (authorizationReply.status) { + case AuthorizationReply.STATUSES.TAC_PLUS_AUTHOR_STATUS_FAIL: { + socket.destroy() + const error = new TacacsError('Authorization Failed', getNameFromCollectionValue(authorizationReply.status, AuthorizationReply.STATUSES)) + this.#logger.error(error.message) + return reject(error) + } + case AuthorizationReply.STATUSES.TAC_PLUS_AUTHOR_STATUS_PASS_REPL: { + this.#logger.log('PASS_REPL') + return resolve(authorizationReply.args) + } + case AuthorizationReply.STATUSES.TAC_PLUS_AUTHOR_STATUS_PASS_ADD: { + this.#logger.log('PASS_ADD') + return resolve(authorizationReply.args) + } + case AuthorizationReply.STATUSES.TAC_PLUS_AUTHOR_STATUS_ERROR: { + socket.destroy() + const error = new TacacsError('Authorization Errored', getNameFromCollectionValue(authorizationReply.status, AuthorizationReply.STATUSES)) + this.#logger.error(error.message) + return reject(error) + } + case AuthorizationReply.STATUSES.TAC_PLUS_AUTHOR_STATUS_FOLLOW: { + return notImplemented('FOLLOW') + } + default: { + const error = new TacacsError('Unknown', 'Unknown') + reject(error) + } } - case AuthorizationReply.STATUSES.TAC_PLUS_AUTHOR_STATUS_FOLLOW: { - return notImplemented('FOLLOW') - } - default: { - const error = new TacacsError('Unknown', 'Unknown') - reject(error) + } + catch (err: unknown) { + socket.destroy() + if (err instanceof Error) { + this.#logger.error(`Original error: ${err.message}`) } + + return reject(new Error('Invalid Tacacs+ configuration, client configuration or Packet validation')) } }) }) @@ -196,53 +206,63 @@ export class Client { socket.write(authStartPacket.toBuffer()) socket.on('data', (data: Buffer) => { - const decodedPacket = Packet.decodePacket(data, this.#secret) - - this.#logger.debug(`Received:\n${decodedPacket.toHumanReadable()}\n`) - - const authenticationReply = new AuthenticationReply(decodedPacket.body, decodedPacket.header.length) - - switch (authenticationReply.status) { - case AuthenticationReply.STATUSES.TAC_PLUS_AUTHEN_STATUS_GETPASS: { - const bodyBuffer = new AuthenticationContinue({ userMsg: password }).toBuffer() - - const innerHeader = new Header({ - majorVersion: Header.MAJOR_VERSIONS.TAC_PLUS_MAJOR_VER, - minorVersion, - type: Header.TYPES.TAC_PLUS_AUTHEN, - flags: (this.#secret === undefined ? Header.FLAGS.TAC_PLUS_UNENCRYPTED_FLAG : 0), - seqNo: getNextSeqNo(), - sessionId, - length: bodyBuffer.length, - }) - - const authContinuePacket = new Packet(innerHeader, bodyBuffer, this.#secret) - - this.#logger.debug(`\nSent:\n${authContinuePacket.toHumanReadable()}\n`) - - return socket.write(authContinuePacket.toBuffer()) + try { + const decodedPacket = Packet.decodePacket(data, this.#secret) + + this.#logger.debug(`Received:\n${decodedPacket.toHumanReadable()}\n`) + + const authenticationReply = new AuthenticationReply(decodedPacket.body, decodedPacket.header.length) + + switch (authenticationReply.status) { + case AuthenticationReply.STATUSES.TAC_PLUS_AUTHEN_STATUS_GETPASS: { + const bodyBuffer = new AuthenticationContinue({ userMsg: password }).toBuffer() + + const innerHeader = new Header({ + majorVersion: Header.MAJOR_VERSIONS.TAC_PLUS_MAJOR_VER, + minorVersion, + type: Header.TYPES.TAC_PLUS_AUTHEN, + flags: (this.#secret === undefined ? Header.FLAGS.TAC_PLUS_UNENCRYPTED_FLAG : 0), + seqNo: getNextSeqNo(), + sessionId, + length: bodyBuffer.length, + }) + + const authContinuePacket = new Packet(innerHeader, bodyBuffer, this.#secret) + + this.#logger.debug(`\nSent:\n${authContinuePacket.toHumanReadable()}\n`) + + return socket.write(authContinuePacket.toBuffer()) + } + case AuthenticationReply.STATUSES.TAC_PLUS_AUTHEN_STATUS_PASS: { + socket.destroy() + this.#logger.log('Authentication PASS') + return resolve(true) + } + case AuthenticationReply.STATUSES.TAC_PLUS_AUTHEN_STATUS_FAIL: { + socket.destroy() + const error = new TacacsError('Authentication Failed', getNameFromCollectionValue(authenticationReply.status, AuthenticationReply.STATUSES)) + this.#logger.error(error.message) + return reject(error) + } + case AuthenticationReply.STATUSES.TAC_PLUS_AUTHEN_STATUS_ERROR: { + socket.destroy() + const error = new TacacsError('Authentication Errored', getNameFromCollectionValue(authenticationReply.status, AuthenticationReply.STATUSES)) + this.#logger.error(error.message) + return reject(error) + } + default: { + const error = new TacacsError('Unknown', 'Unknown') + reject(error) + } } - case AuthenticationReply.STATUSES.TAC_PLUS_AUTHEN_STATUS_PASS: { - socket.destroy() - this.#logger.log('Authentication PASS') - return resolve(true) - } - case AuthenticationReply.STATUSES.TAC_PLUS_AUTHEN_STATUS_FAIL: { - socket.destroy() - const error = new TacacsError('Authentication Failed', getNameFromCollectionValue(authenticationReply.status, AuthenticationReply.STATUSES)) - this.#logger.error(error.message) - return reject(error) - } - case AuthenticationReply.STATUSES.TAC_PLUS_AUTHEN_STATUS_ERROR: { - socket.destroy() - const error = new TacacsError('Authentication Errored', getNameFromCollectionValue(authenticationReply.status, AuthenticationReply.STATUSES)) - this.#logger.error(error.message) - return reject(error) - } - default: { - const error = new TacacsError('Unknown', 'Unknown') - reject(error) + } + catch (err: unknown) { + socket.destroy() + if (err instanceof Error) { + this.#logger.error(`Original error: ${err.message}`) } + + return reject(new Error('Invalid Tacacs+ configuration, client configuration or Packet validation')) } }) })