diff --git a/src/radius/handler/EAPPacketHandler.ts b/src/radius/handler/EAPPacketHandler.ts index 43afcc7..81f7b76 100644 --- a/src/radius/handler/EAPPacketHandler.ts +++ b/src/radius/handler/EAPPacketHandler.ts @@ -3,7 +3,7 @@ import * as NodeCache from 'node-cache'; import debug from 'debug'; import { makeid } from '../../helpers'; -import { IPacket, IPacketHandler, IPacketHandlerResult } from '../../types/PacketHandler'; +import { IPacket, IPacketHandler, IPacketHandlerResult, PacketResponseCode } from '../../types/PacketHandler'; import { IEAPMethod } from '../../types/EAPMethod'; import { buildEAPResponse, decodeEAPHeader } from './eap/EAPHelper'; @@ -34,99 +34,104 @@ export class EAPPacketHandler implements IPacketHandler { // EAP MESSAGE const msg = packet.attributes['EAP-Message'] as Buffer; - const { code, type, identifier, data } = decodeEAPHeader(msg); - - const currentState = this.eapConnectionStates.get(stateID) as { validMethods: IEAPMethod[] }; - - switch (code) { - case 1: // for request - case 2: // for response - switch (type) { - case 1: // identifiy - log('>>>>>>>>>>>> REQUEST FROM CLIENT: IDENTIFY', stateID, data.toString()); - if (data) { - this.identities.set(stateID, data); // use token til binary 0.); - } else { - log('no msg'); - } - - // start identify - if (currentState.validMethods.length > 0) { - return currentState.validMethods[0].identify(identifier, stateID, data); - } - - return buildEAPResponse(identifier, 3); // NAK - case 2: // notification - log('>>>>>>>>>>>> REQUEST FROM CLIENT: notification', {}); - console.info('notification'); - break; - case 4: // md5-challenge - log('>>>>>>>>>>>> REQUEST FROM CLIENT: md5-challenge', {}); - - console.info('md5-challenge'); - break; - case 254: // expanded type - console.error('not implemented type', type); - break; - case 3: // nak - // console.log('got NAK', data); - if (data) { - // if there is data, each data octect reprsents a eap method the clients supports, - // kick out all unsupported ones - const supportedEAPMethods: number[] = []; - for (const supportedMethod of data) { - supportedEAPMethods.push(supportedMethod); + try { + const { code, type, identifier, data } = decodeEAPHeader(msg); + + const currentState = this.eapConnectionStates.get(stateID) as { validMethods: IEAPMethod[] }; + + switch (code) { + case 1: // for request + case 2: // for response + switch (type) { + case 1: // identifiy + log('>>>>>>>>>>>> REQUEST FROM CLIENT: IDENTIFY', stateID, data.toString()); + if (data) { + this.identities.set(stateID, data); // use token til binary 0.); + } else { + log('no msg'); } - currentState.validMethods = currentState.validMethods.filter((method) => { - return supportedEAPMethods.includes(method.getEAPType()); // kick it out? - }); - // save - this.eapConnectionStates.set(stateID, currentState); - - // new identidy request // start identify if (currentState.validMethods.length > 0) { return currentState.validMethods[0].identify(identifier, stateID, data); } - } - // continue with responding a NAK and add rest of supported methods - // eslint-disable-next-line no-fallthrough - default: { - const eapMethod = this.eapMethods.find((method) => { - return type === method.getEAPType(); - }); - - if (eapMethod) { - return eapMethod.handleMessage( - identifier, - stateID, - msg, - packet, - this.identities.get(stateID) - ); - } - // we do not support this auth type, ask for something we support - const serverSupportedMethods = currentState.validMethods.map((method) => - method.getEAPType() - ); + return buildEAPResponse(identifier, 3); // NAK + case 2: // notification + log('>>>>>>>>>>>> REQUEST FROM CLIENT: notification', {}); + console.info('notification'); + break; + case 4: // md5-challenge + log('>>>>>>>>>>>> REQUEST FROM CLIENT: md5-challenge', {}); + + console.info('md5-challenge'); + break; + case 254: // expanded type + console.error('not implemented type', type); + break; + case 3: // nak + // console.log('got NAK', data); + if (data) { + // if there is data, each data octect reprsents a eap method the clients supports, + // kick out all unsupported ones + const supportedEAPMethods: number[] = []; + for (const supportedMethod of data) { + supportedEAPMethods.push(supportedMethod); + } + + currentState.validMethods = currentState.validMethods.filter((method) => { + return supportedEAPMethods.includes(method.getEAPType()); // kick it out? + }); + // save + this.eapConnectionStates.set(stateID, currentState); + + // new identidy request + // start identify + if (currentState.validMethods.length > 0) { + return currentState.validMethods[0].identify(identifier, stateID, data); + } + } + // continue with responding a NAK and add rest of supported methods + // eslint-disable-next-line no-fallthrough + default: { + const eapMethod = this.eapMethods.find((method) => { + return type === method.getEAPType(); + }); + + if (eapMethod) { + return eapMethod.handleMessage( + identifier, + stateID, + msg, + packet, + this.identities.get(stateID) + ); + } + + // we do not support this auth type, ask for something we support + const serverSupportedMethods = currentState.validMethods.map((method) => + method.getEAPType() + ); - console.error('unsupported type', type, `requesting: ${serverSupportedMethods}`); + console.error('unsupported type', type, `requesting: ${serverSupportedMethods}`); - return buildEAPResponse(identifier, 3, Buffer.from(serverSupportedMethods)); + return buildEAPResponse(identifier, 3, Buffer.from(serverSupportedMethods)); + } } - } - break; - case 3: - log('Client Auth Success'); - break; - case 4: - log('Client Auth FAILURE'); - break; - default: + break; + case 3: + log('Client Auth Success'); + break; + case 4: + log('Client Auth FAILURE'); + break; + default: + } + // silently ignore; + return {}; + } catch (err) { + console.error('decoding of (generic) EAP package failed', msg, err); + return {}; } - // silently ignore; - return {}; } } diff --git a/src/radius/handler/eap/eapMethods/EAP-GTC.ts b/src/radius/handler/eap/eapMethods/EAP-GTC.ts index 151af0b..8f64084 100644 --- a/src/radius/handler/eap/eapMethods/EAP-GTC.ts +++ b/src/radius/handler/eap/eapMethods/EAP-GTC.ts @@ -37,22 +37,29 @@ export class EAPGTC implements IEAPMethod { ): Promise { const username = identity; // this.loginData.get(stateID) as Buffer | undefined; - const { data } = decodeEAPHeader(msg); + try { + const { data } = decodeEAPHeader(msg); - const token = this.extractValue(data); + const token = this.extractValue(data); - if (!username) { - throw new Error('no username'); - } + if (!username) { + throw new Error('no username'); + } - log('username', username, username.toString()); - log('token', token, token.toString()); + log('username', username, username.toString()); + log('token', token, token.toString()); - const success = await this.authentication.authenticate(username.toString(), token.toString()); + const success = await this.authentication.authenticate(username.toString(), token.toString()); - return { - code: success ? PacketResponseCode.AccessAccept : PacketResponseCode.AccessReject, - attributes: (success && [['User-Name', username]]) || undefined, - }; + return { + code: success ? PacketResponseCode.AccessAccept : PacketResponseCode.AccessReject, + attributes: (success && [['User-Name', username]]) || undefined, + }; + } catch (err) { + console.error('decoding of EAP-GTC package failed', msg, err); + return { + code: PacketResponseCode.AccessReject, + }; + } } } diff --git a/src/radius/handler/eap/eapMethods/EAP-TTLS.ts b/src/radius/handler/eap/eapMethods/EAP-TTLS.ts index 3070107..d193cb3 100644 --- a/src/radius/handler/eap/eapMethods/EAP-TTLS.ts +++ b/src/radius/handler/eap/eapMethods/EAP-TTLS.ts @@ -300,113 +300,120 @@ export class EAPTTLS implements IEAPMethod { 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); + try { + 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 {}; } - log(`empty data queue for ${stateID}`); - return {}; - } - - let connection = this.openTLSSockets.get(stateID) as ITLSServer; + let connection = this.openTLSSockets.get(stateID) as ITLSServer; - if (!connection) { - connection = startTLSServer(); - this.openTLSSockets.set(stateID, connection); + if (!connection) { + connection = startTLSServer(); + this.openTLSSockets.set(stateID, connection); - connection.events.on('end', () => { - // cleanup socket - log('ENDING SOCKET'); - this.openTLSSockets.del(stateID); - }); - } + connection.events.on('end', () => { + // cleanup socket + log('ENDING SOCKET'); + this.openTLSSockets.del(stateID); + }); + } - const sendResponsePromise = newDeferredPromise(); + const sendResponsePromise = newDeferredPromise(); - const incomingMessageHandler = async (incomingData: Buffer) => { - const ret: any = {}; - ret.attributes = {}; - ret.raw_attributes = []; + const incomingMessageHandler = async (incomingData: Buffer) => { + const ret: any = {}; + ret.attributes = {}; + ret.raw_attributes = []; - const AVPs = this.decodeAVPs(incomingData); + const AVPs = this.decodeAVPs(incomingData); - // build attributes for packet handler - const attributes: IPacketAttributes = {}; - AVPs.forEach((avp) => { - attributes[attr_id_to_name(avp.type)] = avp.data; - }); + // build attributes for packet handler + const attributes: IPacketAttributes = {}; + AVPs.forEach((avp) => { + attributes[attr_id_to_name(avp.type)] = avp.data; + }); - attributes.State = `${stateID}-inner`; + attributes.State = `${stateID}-inner`; - // handle incoming package via inner tunnel - const result = await this.innerTunnel.handlePacket( - { - attributes, - }, - this.getEAPType() - ); + // handle incoming package via inner tunnel + const result = await this.innerTunnel.handlePacket( + { + attributes, + }, + this.getEAPType() + ); - log('inner tunnel result', result); + 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, + { + ...packet, + attributes: { + ...packet.attributes, + ...this.transformAttributesArrayToMap(result.attributes), + }, + } + ) + ); + 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(attr_name_to_id('EAP-Message'), eapMessage[1] as Buffer) + ); + }; - if ( - result.code === PacketResponseCode.AccessReject || - result.code === PacketResponseCode.AccessAccept - ) { + const responseHandler = (encryptedResponseData: Buffer) => { + // send back... sendResponsePromise.resolve( - this.authResponse( - identifier, - result.code === PacketResponseCode.AccessAccept, - connection.tls, - { - ...packet, - attributes: { - ...packet.attributes, - ...this.transformAttributesArrayToMap(result.attributes), - }, - } - ) + this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData) ); - 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(attr_name_to_id('EAP-Message'), eapMessage[1] as Buffer) - ); - }; - - const responseHandler = (encryptedResponseData: Buffer) => { - // send back... - sendResponsePromise.resolve( - this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData) - ); - }; + }; - // register event listeners - connection.events.on('incoming', incomingMessageHandler); - connection.events.on('response', responseHandler); + // register event listeners + connection.events.on('incoming', incomingMessageHandler); + connection.events.on('response', responseHandler); - // emit data to tls server - connection.events.emit('decrypt', data); - const responseData = await sendResponsePromise.promise; + // emit data to tls server + connection.events.emit('decrypt', data); + const responseData = await sendResponsePromise.promise; - // cleanup - connection.events.off('incoming', incomingMessageHandler); - connection.events.off('response', responseHandler); + // cleanup + connection.events.off('incoming', incomingMessageHandler); + connection.events.off('response', responseHandler); - // send response - return responseData; // this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData); + // send response + return responseData; // this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData); + } catch (err) { + console.error('decoding of EAP-TTLS package failed', msg, err); + return { + code: PacketResponseCode.AccessReject, + }; + } } private transformAttributesArrayToMap(attributes: [string, Buffer | string][] | undefined) {