From 7ca60a20cc24eb8100ed1f20fe18e7ec664fd176 Mon Sep 17 00:00:00 2001 From: simon Date: Thu, 27 Feb 2020 11:33:05 +0100 Subject: [PATCH] fix: improve coping with long running auth requests --- src/auth/GoogleLDAPAuth.ts | 22 +++-- src/radius/handler/eap/eapMethods/EAP-TTLS.ts | 90 ++++--------------- src/server/UDPServer.ts | 2 +- 3 files changed, 33 insertions(+), 81 deletions(-) diff --git a/src/auth/GoogleLDAPAuth.ts b/src/auth/GoogleLDAPAuth.ts index 84bbb27..ce538be 100644 --- a/src/auth/GoogleLDAPAuth.ts +++ b/src/auth/GoogleLDAPAuth.ts @@ -1,4 +1,4 @@ -import { Client, ClientOptions, createClient } from 'ldapjs'; +import { ClientOptions, createClient } from 'ldapjs'; import debug from 'debug'; import * as tls from 'tls'; import { IAuthentication } from '../types/Authentication'; @@ -22,8 +22,6 @@ interface IGoogleLDAPAuthOptions { } export class GoogleLDAPAuth implements IAuthentication { - private ldapDNClient: Client; - private lastDNsFetch: Date; private allValidDNsCache: { [key: string]: string }; @@ -43,10 +41,6 @@ export class GoogleLDAPAuth implements IAuthentication { } }; - this.ldapDNClient = createClient(this.config).on('error', error => { - console.error('Error in ldap', error); - }); - this.fetchDNs(); } @@ -54,7 +48,12 @@ export class GoogleLDAPAuth implements IAuthentication { const dns: { [key: string]: string } = {}; await new Promise((resolve, reject) => { - this.ldapDNClient.search( + const ldapDNClient = createClient(this.config).on('error', error => { + console.error('Error in ldap', error); + reject(error); + }); + + ldapDNClient.search( this.base, { scope: 'sub' @@ -100,6 +99,13 @@ export class GoogleLDAPAuth implements IAuthentication { const cacheValidTime = new Date(); cacheValidTime.setHours(cacheValidTime.getHours() - 12); + /* + just a test for super slow google responses + await new Promise((resolve, reject) => { + setTimeout(resolve, 10000); // wait 10 seconds + }) + */ + let dnsFetched = false; if (!this.lastDNsFetch || this.lastDNsFetch < cacheValidTime || forceFetching) { diff --git a/src/radius/handler/eap/eapMethods/EAP-TTLS.ts b/src/radius/handler/eap/eapMethods/EAP-TTLS.ts index 20d38ac..a0ba9c1 100644 --- a/src/radius/handler/eap/eapMethods/EAP-TTLS.ts +++ b/src/radius/handler/eap/eapMethods/EAP-TTLS.ts @@ -3,6 +3,7 @@ /* eslint-disable no-bitwise */ import * as tls from 'tls'; import * as NodeCache from 'node-cache'; +// eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore import { attr_id_to_name, attr_name_to_id } from 'radius'; import debug from 'debug'; @@ -43,6 +44,8 @@ interface IAVPEntry { } export class EAPTTLS implements IEAPMethod { + private lastProcessedIdentifier = new NodeCache({ useClones: false, stdTTL: 60 }); + // { [key: string]: Buffer } = {}; private queueData = new NodeCache({ useClones: false, stdTTL: 60 }); // queue data maximum for 60 seconds @@ -185,7 +188,7 @@ export class EAPTTLS implements IEAPMethod { Message Length | Data... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ - // const identifier = msg.slice(1, 2).readUInt8(0); + const identifier = msg.slice(1, 2).readUInt8(0); const flags = msg.slice(5, 6).readUInt8(0); // .toString('hex'); /* 0 1 2 3 4 5 6 7 @@ -201,15 +204,15 @@ export class EAPTTLS implements IEAPMethod { */ const decodedFlags = { // L - lengthIncluded: flags & 0b010000000, + lengthIncluded: !!(flags & 0b10000000), // M - moreFragments: flags & 0b001000000, + moreFragments: !!(flags & 0b01000000), // S - start: flags & 0b000100000, + start: !!(flags & 0b00100000), // R - // reserved: flags & 0b000011000, + // reserved: flags & 0b00011000, // V - version: flags & 0b010000111 + version: flags & 0b00000111 }; let msglength; @@ -221,7 +224,7 @@ export class EAPTTLS implements IEAPMethod { log('>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS', { flags: `00000000${flags.toString(2)}`.substr(-8), decodedFlags, - // identifier, + identifier, msglength, data // dataStr: data.toString() @@ -291,15 +294,23 @@ export class EAPTTLS implements IEAPMethod { msg: Buffer, packet: IPacket ): Promise { + if (identifier === this.lastProcessedIdentifier.get(stateID)) { + log(`ignoring message ${identifier}, because it's processing already... ${stateID}`); + + return {}; + } + this.lastProcessedIdentifier.set(stateID, identifier); const { data } = this.decodeTTLSMessage(msg); // check if no data package is there and we have something in the queue, if so.. empty the queue first if (!data || data.length === 0) { const queuedData = this.queueData.get(stateID); if (queuedData instanceof Buffer && queuedData.length > 0) { + log(`returning queued data for ${stateID}`); return this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, queuedData, false); } + log(`empty data queue for ${stateID}`); return {}; } @@ -373,71 +384,6 @@ export class EAPTTLS implements IEAPMethod { 'encrypt', this.buildAVP(attr_name_to_id('EAP-Message'), eapMessage[1] as Buffer) ); - - /* - switch (type) { - case 1: // PAP - try { - const { username, password } = this.papChallenge.decode(incomingData); - const authResult = await this.authentication.authenticate(username, password); - - sendResponsePromise.resolve( - this.authResponse(identifier, authResult, connection.tls, orgRadiusPacket) - ); - } catch (err) { - // pwd not found.. - console.error('pwd not found', err); - connection.events.emit('end'); - // NAK - sendResponsePromise.resolve(this.buildEAPTTLSResponse(identifier, 3, 0, stateID)); - } - break; - case 79: { - const result = await this.tunnelEAP.handlePacket( - { - State: `${stateID}-inner`, - 'EAP-Message': AVPdata - }, - orgRadiusPacket - ); - - log('inner tunnel result', result); - - if ( - result.code === PacketResponseCode.AccessReject || - result.code === PacketResponseCode.AccessAccept - ) { - sendResponsePromise.resolve( - this.authResponse( - identifier, - result.code === PacketResponseCode.AccessAccept, - connection.tls, - orgRadiusPacket - ) - ); - return; - } - - const eapMessage = result.attributes?.find(attr => attr[0] === 'EAP-Message'); - if (!eapMessage) { - throw new Error('no eap message found'); - } - - connection.events.emit('encrypt', this.buildAVP(79, eapMessage[1])); - break; - } - default: { - log('data', incomingData); - log('data str', incomingData.toString()); - - log('UNSUPPORTED AUTH TYPE, requesting identify again (we need PAP!)', type); - - connection.events.emit( - 'encrypt', - this.buildAVP(79, this.buildEAPTTLS(identifier, 3, 0, stateID, Buffer.from([1]))) - ); - } - } */ }; const responseHandler = (encryptedResponseData: Buffer) => { diff --git a/src/server/UDPServer.ts b/src/server/UDPServer.ts index 2e0de07..7767a64 100644 --- a/src/server/UDPServer.ts +++ b/src/server/UDPServer.ts @@ -41,7 +41,7 @@ export class UDPServer extends events.EventEmitter implements IServer { // if expectAcknowledgment is false (e.g. Access-Accept or Access-Reject), we do not retry const identifierForRetry = `${address}:${port}`; if (expectAcknowledgment && retried < UDPServer.MAX_RETRIES) { - this.timeout[identifierForRetry] = setTimeout(sendResponse, 600 * (retried + 1)); + this.timeout[identifierForRetry] = setTimeout(() => sendResponse(), 1600 * (retried + 1)); } retried += 1; };